mirror of
https://codeberg.org/anoncontributorxmr/mysu.git
synced 2024-11-25 08:52:28 +00:00
Add coin control option for freezing UTXOs
This commit is contained in:
parent
fbf0ea95ac
commit
14b989a760
14 changed files with 155 additions and 24 deletions
|
@ -2,15 +2,15 @@ apply plugin: 'com.android.application'
|
|||
apply plugin: "androidx.navigation.safeargs"
|
||||
|
||||
android {
|
||||
compileSdkVersion 33
|
||||
compileSdkVersion 34
|
||||
buildToolsVersion '30.0.3'
|
||||
ndkVersion '17.2.4988734'
|
||||
defaultConfig {
|
||||
applicationId "net.mynero.wallet"
|
||||
minSdkVersion 21
|
||||
targetSdkVersion 33
|
||||
versionCode 40302
|
||||
versionName "0.4.3.2 'Fluorine Fermi'"
|
||||
targetSdkVersion 34
|
||||
versionCode 40400
|
||||
versionName "0.4.4 'Fluorine Fermi'"
|
||||
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
|
||||
externalNativeBuild {
|
||||
cmake {
|
||||
|
|
|
@ -1135,7 +1135,7 @@ Java_net_mynero_wallet_model_Wallet_getCoinsJ(JNIEnv *env, jobject instance) {
|
|||
|
||||
jobject newCoinsInfo(JNIEnv *env, Monero::CoinsInfo *info) {
|
||||
jmethodID c = env->GetMethodID(class_CoinsInfo, "<init>",
|
||||
"(JZLjava/lang/String;JLjava/lang/String;Ljava/lang/String;ZJ)V");
|
||||
"(JZLjava/lang/String;JLjava/lang/String;Ljava/lang/String;ZJZ)V");
|
||||
jstring _key_image = env->NewStringUTF(info->keyImage().c_str());
|
||||
jstring _pub_key = env->NewStringUTF(info->pubKey().c_str());
|
||||
jstring _hash = env->NewStringUTF(info->hash().c_str());
|
||||
|
@ -1147,7 +1147,8 @@ jobject newCoinsInfo(JNIEnv *env, Monero::CoinsInfo *info) {
|
|||
_hash,
|
||||
_pub_key,
|
||||
info->unlocked(),
|
||||
static_cast<jlong> (info->internalOutputIndex()));
|
||||
static_cast<jlong> (info->internalOutputIndex()),
|
||||
info->frozen());
|
||||
env->DeleteLocalRef(_key_image);
|
||||
env->DeleteLocalRef(_hash);
|
||||
env->DeleteLocalRef(_pub_key);
|
||||
|
@ -1183,6 +1184,18 @@ Java_net_mynero_wallet_model_Coins_refreshJ(JNIEnv *env, jobject instance) {
|
|||
return coins_cpp2java(env, coins->getAll());
|
||||
}
|
||||
|
||||
JNIEXPORT void JNICALL
|
||||
Java_net_mynero_wallet_model_Coins_setFrozen(JNIEnv *env, jobject instance, jstring publicKey, jboolean frozen) {
|
||||
Monero::Coins *coins = getHandle<Monero::Coins>(env, instance);
|
||||
const char *_publicKey = env->GetStringUTFChars(publicKey, nullptr);
|
||||
if (frozen) {
|
||||
coins->setFrozen(_publicKey);
|
||||
} else {
|
||||
coins->thaw(_publicKey);
|
||||
}
|
||||
env->ReleaseStringUTFChars(publicKey, _publicKey);
|
||||
}
|
||||
|
||||
//virtual TransactionHistory * history() const = 0;
|
||||
JNIEXPORT jlong JNICALL
|
||||
Java_net_mynero_wallet_model_Wallet_getHistoryJ(JNIEnv *env, jobject instance) {
|
||||
|
|
|
@ -32,11 +32,12 @@ import net.mynero.wallet.util.Constants;
|
|||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Objects;
|
||||
|
||||
public class CoinsInfoAdapter extends RecyclerView.Adapter<CoinsInfoAdapter.ViewHolder> {
|
||||
|
||||
private List<CoinsInfo> localDataSet;
|
||||
private List<String> selectedUtxos;
|
||||
private List<CoinsInfo> selectedUtxos;
|
||||
private CoinsInfoAdapterListener listener = null;
|
||||
|
||||
/**
|
||||
|
@ -48,13 +49,13 @@ public class CoinsInfoAdapter extends RecyclerView.Adapter<CoinsInfoAdapter.View
|
|||
this.selectedUtxos = new ArrayList<>();
|
||||
}
|
||||
|
||||
public void submitList(List<CoinsInfo> dataSet, List<String> selectedUtxos) {
|
||||
public void submitList(List<CoinsInfo> dataSet, List<CoinsInfo> selectedUtxos) {
|
||||
this.localDataSet = dataSet;
|
||||
this.selectedUtxos = selectedUtxos;
|
||||
notifyDataSetChanged();
|
||||
}
|
||||
|
||||
public void updateSelectedUtxos(List<String> selectedUtxos) {
|
||||
public void updateSelectedUtxos(List<CoinsInfo> selectedUtxos) {
|
||||
this.selectedUtxos = selectedUtxos;
|
||||
notifyDataSetChanged();
|
||||
}
|
||||
|
@ -98,8 +99,14 @@ public class CoinsInfoAdapter extends RecyclerView.Adapter<CoinsInfoAdapter.View
|
|||
this.listener = listener;
|
||||
}
|
||||
|
||||
public void bind(CoinsInfo coinsInfo, List<String> selectedUtxos) {
|
||||
boolean selected = selectedUtxos.contains(coinsInfo.getKeyImage());
|
||||
public void bind(CoinsInfo coinsInfo, List<CoinsInfo> selectedUtxos) {
|
||||
boolean selected = false;
|
||||
for(CoinsInfo selectedUtxo : selectedUtxos) {
|
||||
if (Objects.equals(selectedUtxo.getKeyImage(), coinsInfo.getKeyImage())) {
|
||||
selected = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
TextView pubKeyTextView = itemView.findViewById(R.id.utxo_pub_key_textview);
|
||||
TextView amountTextView = itemView.findViewById(R.id.utxo_amount_textview);
|
||||
TextView globalIdxTextView = itemView.findViewById(R.id.utxo_global_index_textview);
|
||||
|
@ -119,10 +126,12 @@ public class CoinsInfoAdapter extends RecyclerView.Adapter<CoinsInfoAdapter.View
|
|||
return unlocked;
|
||||
});
|
||||
|
||||
if (!coinsInfo.isUnlocked()) {
|
||||
itemView.setBackgroundColor(ContextCompat.getColor(itemView.getContext(), R.color.oled_locked_utxo));
|
||||
} else if (selected) {
|
||||
if (selected) {
|
||||
itemView.setBackgroundColor(ContextCompat.getColor(itemView.getContext(), R.color.oled_colorSecondary));
|
||||
} else if(coinsInfo.isFrozen()) {
|
||||
itemView.setBackgroundColor(ContextCompat.getColor(itemView.getContext(), R.color.oled_frozen_utxo));
|
||||
} else if (!coinsInfo.isUnlocked()) {
|
||||
itemView.setBackgroundColor(ContextCompat.getColor(itemView.getContext(), R.color.oled_locked_utxo));
|
||||
} else {
|
||||
itemView.setBackgroundColor(ContextCompat.getColor(itemView.getContext(), android.R.color.transparent));
|
||||
}
|
||||
|
|
|
@ -5,6 +5,7 @@ import android.view.LayoutInflater;
|
|||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
import android.widget.Button;
|
||||
import android.widget.Toast;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.annotation.Nullable;
|
||||
|
@ -19,18 +20,27 @@ import net.mynero.wallet.fragment.dialog.SendBottomSheetDialog;
|
|||
import net.mynero.wallet.model.CoinsInfo;
|
||||
import net.mynero.wallet.service.AddressService;
|
||||
import net.mynero.wallet.service.UTXOService;
|
||||
import net.mynero.wallet.util.MoneroThreadPoolExecutor;
|
||||
import net.mynero.wallet.util.UriData;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
||||
public class UtxosFragment extends Fragment implements CoinsInfoAdapter.CoinsInfoAdapterListener, SendBottomSheetDialog.Listener {
|
||||
|
||||
private UtxosViewModel mViewModel;
|
||||
private final ArrayList<String> selectedUtxos = new ArrayList<>();
|
||||
private final ArrayList<CoinsInfo> selectedUtxos = new ArrayList<>();
|
||||
private final CoinsInfoAdapter adapter = new CoinsInfoAdapter(this);
|
||||
private Button sendUtxosButton;
|
||||
private Button churnUtxosButton;
|
||||
private Button freezeUtxosButton;
|
||||
enum FreezeActionType {
|
||||
FREEZE,
|
||||
UNFREEZE,
|
||||
TOGGLE_FREEZE
|
||||
}
|
||||
private FreezeActionType freezeActionType = FreezeActionType.FREEZE;
|
||||
|
||||
@Override
|
||||
public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container,
|
||||
|
@ -47,22 +57,45 @@ public class UtxosFragment extends Fragment implements CoinsInfoAdapter.CoinsInf
|
|||
}
|
||||
|
||||
private void bindListeners(View view) {
|
||||
freezeUtxosButton = view.findViewById(R.id.freeze_utxos_button);
|
||||
sendUtxosButton = view.findViewById(R.id.send_utxos_button);
|
||||
churnUtxosButton = view.findViewById(R.id.churn_utxos_button);
|
||||
sendUtxosButton.setVisibility(View.GONE);
|
||||
churnUtxosButton.setVisibility(View.GONE);
|
||||
freezeUtxosButton.setVisibility(View.GONE);
|
||||
freezeUtxosButton.setOnClickListener(view1 -> {
|
||||
Toast.makeText(getContext(), "Toggling freeze status, please wait.", Toast.LENGTH_SHORT).show();
|
||||
MoneroThreadPoolExecutor.MONERO_THREAD_POOL_EXECUTOR.execute(() -> {
|
||||
UTXOService.getInstance().toggleFrozen(selectedUtxos);
|
||||
getActivity().runOnUiThread(() -> {
|
||||
selectedUtxos.clear();
|
||||
adapter.updateSelectedUtxos(new ArrayList<>());
|
||||
sendUtxosButton.setVisibility(View.GONE);
|
||||
churnUtxosButton.setVisibility(View.GONE);
|
||||
freezeUtxosButton.setVisibility(View.GONE);
|
||||
});
|
||||
});
|
||||
});
|
||||
sendUtxosButton.setOnClickListener(view1 -> {
|
||||
ArrayList<String> selectedKeyImages = new ArrayList<>();
|
||||
for(CoinsInfo coinsInfo : selectedUtxos) {
|
||||
selectedKeyImages.add(coinsInfo.getKeyImage());
|
||||
}
|
||||
SendBottomSheetDialog sendDialog = new SendBottomSheetDialog();
|
||||
sendDialog.listener = this;
|
||||
sendDialog.selectedUtxos = selectedUtxos;
|
||||
sendDialog.selectedUtxos = selectedKeyImages;
|
||||
sendDialog.show(getActivity().getSupportFragmentManager(), null);
|
||||
});
|
||||
churnUtxosButton.setOnClickListener(view1 -> {
|
||||
ArrayList<String> selectedKeyImages = new ArrayList<>();
|
||||
for(CoinsInfo coinsInfo : selectedUtxos) {
|
||||
selectedKeyImages.add(coinsInfo.getKeyImage());
|
||||
}
|
||||
SendBottomSheetDialog sendDialog = new SendBottomSheetDialog();
|
||||
sendDialog.listener = this;
|
||||
sendDialog.isChurning = true;
|
||||
sendDialog.uriData = UriData.parse(AddressService.getInstance().currentSubaddress().getAddress());
|
||||
sendDialog.selectedUtxos = selectedUtxos;
|
||||
sendDialog.selectedUtxos = selectedKeyImages;
|
||||
sendDialog.show(getActivity().getSupportFragmentManager(), null);
|
||||
});
|
||||
}
|
||||
|
@ -93,22 +126,43 @@ public class UtxosFragment extends Fragment implements CoinsInfoAdapter.CoinsInf
|
|||
|
||||
@Override
|
||||
public void onUtxoSelected(CoinsInfo coinsInfo) {
|
||||
boolean selected = selectedUtxos.contains(coinsInfo.getKeyImage());
|
||||
boolean selected = selectedUtxos.contains(coinsInfo);
|
||||
if (selected) {
|
||||
selectedUtxos.remove(coinsInfo.getKeyImage());
|
||||
selectedUtxos.remove(coinsInfo);
|
||||
} else {
|
||||
selectedUtxos.add(coinsInfo.getKeyImage());
|
||||
selectedUtxos.add(coinsInfo);
|
||||
}
|
||||
|
||||
boolean frozenExists = false, unfrozenExists = false, bothExist = false;
|
||||
for(CoinsInfo selectedUtxo : selectedUtxos) {
|
||||
if(selectedUtxo.isFrozen())
|
||||
frozenExists = true;
|
||||
else {
|
||||
unfrozenExists = true;
|
||||
}
|
||||
}
|
||||
bothExist = frozenExists && unfrozenExists;
|
||||
|
||||
if (selectedUtxos.isEmpty()) {
|
||||
sendUtxosButton.setVisibility(View.GONE);
|
||||
churnUtxosButton.setVisibility(View.GONE);
|
||||
freezeUtxosButton.setVisibility(View.GONE);
|
||||
freezeActionType = FreezeActionType.FREEZE;
|
||||
} else {
|
||||
sendUtxosButton.setVisibility(View.VISIBLE);
|
||||
churnUtxosButton.setVisibility(View.VISIBLE);
|
||||
freezeUtxosButton.setVisibility(View.VISIBLE);
|
||||
}
|
||||
|
||||
adapter.updateSelectedUtxos(selectedUtxos);
|
||||
|
||||
if(bothExist) {
|
||||
freezeUtxosButton.setText(R.string.toggle_freeze);
|
||||
} else if(frozenExists) {
|
||||
freezeUtxosButton.setText(R.string.unfreeze);
|
||||
} else if(unfrozenExists) {
|
||||
freezeUtxosButton.setText(R.string.freeze);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
@ -46,5 +46,7 @@ public class Coins {
|
|||
coins = transactionInfos;
|
||||
}
|
||||
|
||||
public native void setFrozen(String publicKey, boolean frozen);
|
||||
|
||||
private native List<CoinsInfo> refreshJ();
|
||||
}
|
||||
|
|
|
@ -46,8 +46,9 @@ public class CoinsInfo implements Parcelable, Comparable<CoinsInfo> {
|
|||
String pubKey;
|
||||
boolean unlocked;
|
||||
long localOutputIndex;
|
||||
boolean frozen;
|
||||
|
||||
public CoinsInfo(long globalOutputIndex, boolean spent, String keyImage, long amount, String hash, String pubKey, boolean unlocked, long localOutputIndex) {
|
||||
public CoinsInfo(long globalOutputIndex, boolean spent, String keyImage, long amount, String hash, String pubKey, boolean unlocked, long localOutputIndex, boolean frozen) {
|
||||
this.globalOutputIndex = globalOutputIndex;
|
||||
this.spent = spent;
|
||||
this.keyImage = keyImage;
|
||||
|
@ -56,6 +57,7 @@ public class CoinsInfo implements Parcelable, Comparable<CoinsInfo> {
|
|||
this.pubKey = pubKey;
|
||||
this.unlocked = unlocked;
|
||||
this.localOutputIndex = localOutputIndex;
|
||||
this.frozen = frozen;
|
||||
}
|
||||
|
||||
protected CoinsInfo(Parcel in) {
|
||||
|
@ -94,6 +96,10 @@ public class CoinsInfo implements Parcelable, Comparable<CoinsInfo> {
|
|||
return localOutputIndex;
|
||||
}
|
||||
|
||||
public boolean isFrozen() {
|
||||
return frozen;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int describeContents() {
|
||||
return 0;
|
||||
|
|
|
@ -5,6 +5,7 @@ import android.util.Pair;
|
|||
import androidx.lifecycle.LiveData;
|
||||
import androidx.lifecycle.MutableLiveData;
|
||||
|
||||
import net.mynero.wallet.model.Coins;
|
||||
import net.mynero.wallet.model.CoinsInfo;
|
||||
import net.mynero.wallet.model.PendingTransaction;
|
||||
import net.mynero.wallet.model.Wallet;
|
||||
|
@ -36,6 +37,14 @@ public class UTXOService extends ServiceBase {
|
|||
return WalletManager.getInstance().getWallet().getCoins().getAll();
|
||||
}
|
||||
|
||||
public void toggleFrozen(List<CoinsInfo> selectedCoins) {
|
||||
Coins coins = WalletManager.getInstance().getWallet().getCoins();
|
||||
for(CoinsInfo coin : selectedCoins) {
|
||||
coins.setFrozen(coin.getPubKey(), !coin.isFrozen());
|
||||
}
|
||||
refreshUtxos();
|
||||
}
|
||||
|
||||
public ArrayList<String> selectUtxos(long amount, boolean sendAll) throws Exception {
|
||||
final long basicFeeEstimate = calculateBasicFee(amount);
|
||||
final long amountWithBasicFee = amount + basicFeeEstimate;
|
||||
|
@ -46,7 +55,7 @@ public class UTXOService extends ServiceBase {
|
|||
Collections.sort(utxos);
|
||||
//loop through each utxo
|
||||
for (CoinsInfo coinsInfo : utxos) {
|
||||
if (!coinsInfo.isSpent() && coinsInfo.isUnlocked()) { //filter out spent and locked outputs
|
||||
if (!coinsInfo.isSpent() && coinsInfo.isUnlocked() && !coinsInfo.isFrozen()) { //filter out spent, locked, and frozen outputs
|
||||
if (sendAll) {
|
||||
// if send all, add all utxos and set amount to send all
|
||||
selectedUtxos.add(coinsInfo.getKeyImage());
|
||||
|
|
7
app/src/main/res/drawable/button_bg_center.xml
Normal file
7
app/src/main/res/drawable/button_bg_center.xml
Normal file
|
@ -0,0 +1,7 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<selector xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<!-- Color when the row is selected -->
|
||||
<item android:drawable="@drawable/button_bg_enabled_center" android:state_enabled="true" />
|
||||
<!-- Standard background color -->
|
||||
<item android:drawable="@drawable/button_bg_disabled_center" />
|
||||
</selector>
|
8
app/src/main/res/drawable/button_bg_disabled_center.xml
Normal file
8
app/src/main/res/drawable/button_bg_disabled_center.xml
Normal file
|
@ -0,0 +1,8 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<shape xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:shape="rectangle">
|
||||
<padding
|
||||
android:left="8dp"
|
||||
android:right="8dp" />
|
||||
<solid android:color="@color/button_disabled_bg_color" />
|
||||
</shape>
|
8
app/src/main/res/drawable/button_bg_enabled_center.xml
Normal file
8
app/src/main/res/drawable/button_bg_enabled_center.xml
Normal file
|
@ -0,0 +1,8 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<shape xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:shape="rectangle">
|
||||
<padding
|
||||
android:left="8dp"
|
||||
android:right="8dp" />
|
||||
<solid android:color="@color/oled_colorSecondary" />
|
||||
</shape>
|
|
@ -47,15 +47,25 @@
|
|||
app:layout_constraintStart_toStartOf="parent">
|
||||
|
||||
<Button
|
||||
android:id="@+id/churn_utxos_button"
|
||||
android:id="@+id/freeze_utxos_button"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:background="@drawable/button_bg_left"
|
||||
android:layout_marginEnd="1dp"
|
||||
android:text="@string/freeze"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
app:layout_constraintEnd_toStartOf="@id/churn_utxos_button"
|
||||
app:layout_constraintStart_toStartOf="parent" />
|
||||
|
||||
<Button
|
||||
android:id="@+id/churn_utxos_button"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:background="@drawable/button_bg_center"
|
||||
android:text="@string/churn"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
app:layout_constraintEnd_toStartOf="@id/send_utxos_button"
|
||||
app:layout_constraintStart_toStartOf="parent" />
|
||||
app:layout_constraintStart_toEndOf="@id/freeze_utxos_button" />
|
||||
|
||||
<Button
|
||||
android:id="@+id/send_utxos_button"
|
||||
|
|
|
@ -33,6 +33,7 @@
|
|||
<color name="oled_txBackgroundColor">#060606</color>
|
||||
<color name="oled_dialogBackgroundColor">#0E0E0E</color>
|
||||
<color name="oled_addressListColor">#353535</color>
|
||||
<color name="oled_frozen_utxo">#437895</color>
|
||||
|
||||
<!-- CLASSIC -->
|
||||
|
||||
|
|
|
@ -34,6 +34,7 @@
|
|||
<color name="oled_txBackgroundColor">#FBFBFB</color>
|
||||
<color name="oled_dialogBackgroundColor">#E8E8E8</color>
|
||||
<color name="oled_addressListColor">#bbbbbb</color>
|
||||
<color name="oled_frozen_utxo">#437895</color>
|
||||
|
||||
<!-- CLASSIC -->
|
||||
|
||||
|
|
|
@ -101,6 +101,9 @@
|
|||
<string name="outpoint_text">Outpoint: %1$s</string>
|
||||
<string name="create_wallet_failed">Create wallet failed: %1$s</string>
|
||||
<string name="churn">Churn</string>
|
||||
<string name="freeze">Freeze</string>
|
||||
<string name="unfreeze">Unfreeze</string>
|
||||
<string name="toggle_freeze">(Un)freeze</string>
|
||||
<string name="wallet_keys_label">Wallet Keys</string>
|
||||
<string name="done">Done</string>
|
||||
<string name="delete">Delete</string>
|
||||
|
|
Loading…
Reference in a new issue