Handle tx history better, and work on the send screen a bit

NOTE: This commit still logs seed phrases for development purposes
This commit is contained in:
pokkst 2022-09-07 19:18:47 -05:00
parent ae232d1a92
commit 569e1e5f5b
No known key found for this signature in database
GPG key ID: 90C2ED85E67A50FF
12 changed files with 150 additions and 27 deletions

View file

@ -86,7 +86,7 @@ public class TransactionInfoAdapter extends RecyclerView.Adapter<TransactionInfo
TextView confirmationsTextView = ((TextView)itemView.findViewById(R.id.tvConfirmations)); TextView confirmationsTextView = ((TextView)itemView.findViewById(R.id.tvConfirmations));
CircularProgressIndicator confirmationsProgressBar = ((CircularProgressIndicator)itemView.findViewById(R.id.pbConfirmations)); CircularProgressIndicator confirmationsProgressBar = ((CircularProgressIndicator)itemView.findViewById(R.id.pbConfirmations));
confirmationsProgressBar.setMax(TransactionInfo.CONFIRMATION);
this.amountTextView = ((TextView)itemView.findViewById(R.id.tx_amount)); this.amountTextView = ((TextView)itemView.findViewById(R.id.tx_amount));
((TextView)itemView.findViewById(R.id.tx_failed)).setVisibility(View.GONE); ((TextView)itemView.findViewById(R.id.tx_failed)).setVisibility(View.GONE);
if(txInfo.isFailed) { if(txInfo.isFailed) {
@ -176,6 +176,7 @@ public class TransactionInfoAdapter extends RecyclerView.Adapter<TransactionInfo
public void submitList(List<TransactionInfo> dataSet) { public void submitList(List<TransactionInfo> dataSet) {
this.localDataSet = dataSet; this.localDataSet = dataSet;
notifyDataSetChanged();
} }
// Create new views (invoked by the layout manager) // Create new views (invoked by the layout manager)

View file

@ -41,6 +41,7 @@ public class ReceiveBottomSheetDialog extends BottomSheetDialogFragment {
AddressService.getInstance().address.observe(getViewLifecycleOwner(), addr -> { AddressService.getInstance().address.observe(getViewLifecycleOwner(), addr -> {
if (!addr.isEmpty()) { if (!addr.isEmpty()) {
System.out.println(addr);
addressTextView.setText(addr); addressTextView.setText(addr);
addressImageView.setImageBitmap(generate(addr, 256, 256)); addressImageView.setImageBitmap(generate(addr, 256, 256));
} }

View file

@ -1,9 +1,11 @@
package com.m2049r.xmrwallet.fragment.dialog; package com.m2049r.xmrwallet.fragment.dialog;
import android.content.ClipboardManager;
import android.os.Bundle; import android.os.Bundle;
import com.google.android.material.bottomsheet.BottomSheetDialogFragment; import com.google.android.material.bottomsheet.BottomSheetDialogFragment;
import com.m2049r.xmrwallet.R; import com.m2049r.xmrwallet.R;
import com.m2049r.xmrwallet.model.Wallet; import com.m2049r.xmrwallet.model.Wallet;
import com.m2049r.xmrwallet.service.BalanceService;
import com.m2049r.xmrwallet.service.TxService; import com.m2049r.xmrwallet.service.TxService;
import android.view.LayoutInflater; import android.view.LayoutInflater;
@ -11,12 +13,19 @@ import android.view.View;
import android.view.ViewGroup; import android.view.ViewGroup;
import android.widget.Button; import android.widget.Button;
import android.widget.EditText; import android.widget.EditText;
import android.widget.ImageButton;
import android.widget.TextView;
import android.widget.Toast; import android.widget.Toast;
import androidx.annotation.NonNull; import androidx.annotation.NonNull;
import androidx.annotation.Nullable; import androidx.annotation.Nullable;
import androidx.lifecycle.LiveData;
import androidx.lifecycle.MutableLiveData;
import androidx.lifecycle.Observer;
public class SendBottomSheetDialog extends BottomSheetDialogFragment { public class SendBottomSheetDialog extends BottomSheetDialogFragment {
private MutableLiveData<Boolean> _sendingMax = new MutableLiveData<>(false);
public LiveData<Boolean> sendingMax = _sendingMax;
@Override @Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
@ -26,26 +35,56 @@ public class SendBottomSheetDialog extends BottomSheetDialogFragment {
@Override @Override
public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) { public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) {
super.onViewCreated(view, savedInstanceState); super.onViewCreated(view, savedInstanceState);
ImageButton pasteAddressImageButton = view.findViewById(R.id.paste_address_imagebutton);
Button sendMaxButton = view.findViewById(R.id.send_max_button);
EditText addressEditText = view.findViewById(R.id.address_edittext); EditText addressEditText = view.findViewById(R.id.address_edittext);
EditText amountEditText = view.findViewById(R.id.amount_edittext); EditText amountEditText = view.findViewById(R.id.amount_edittext);
Button sendButton = view.findViewById(R.id.send_button); Button sendButton = view.findViewById(R.id.send_button);
TextView sendAllTextView = view.findViewById(R.id.sending_all_textview);
TxService.getInstance().clearSendEvent.observe(getViewLifecycleOwner(), o -> { TxService.getInstance().clearSendEvent.observe(getViewLifecycleOwner(), o -> {
dismiss(); dismiss();
}); });
pasteAddressImageButton.setOnClickListener(view1 -> {
});
sendMaxButton.setOnClickListener(view1 -> {
boolean currentValue = sendingMax.getValue() != null ? sendingMax.getValue() : false;
_sendingMax.postValue(!currentValue);
});
sendButton.setOnClickListener(view1 -> { sendButton.setOnClickListener(view1 -> {
String address = addressEditText.getText().toString().trim(); String address = addressEditText.getText().toString().trim();
String amount = amountEditText.getText().toString().trim(); String amount = amountEditText.getText().toString().trim();
boolean validAddress = Wallet.isAddressValid(address); boolean validAddress = Wallet.isAddressValid(address);
if (validAddress && !amount.isEmpty()) { if (validAddress && !amount.isEmpty()) {
long amountRaw = Wallet.getAmountFromString(amount);
long balance = BalanceService.getInstance().getUnlockedBalanceRaw();
if(amountRaw >= balance || amountRaw <= 0) {
Toast.makeText(getActivity(), getString(R.string.send_amount_invalid), Toast.LENGTH_SHORT).show();
}
sendButton.setEnabled(false); sendButton.setEnabled(false);
TxService.getInstance().sendTx(address, amount); boolean sendAll = sendingMax.getValue() != null ? sendingMax.getValue() : false;
TxService.getInstance().sendTx(address, amount, sendAll);
} else if (!validAddress) { } else if (!validAddress) {
Toast.makeText(getActivity(), getString(R.string.send_address_invalid), Toast.LENGTH_SHORT).show(); Toast.makeText(getActivity(), getString(R.string.send_address_invalid), Toast.LENGTH_SHORT).show();
} else if (amount.isEmpty()) { } else if (amount.isEmpty()) {
Toast.makeText(getActivity(), getString(R.string.send_amount_empty), Toast.LENGTH_SHORT).show(); Toast.makeText(getActivity(), getString(R.string.send_amount_empty), Toast.LENGTH_SHORT).show();
} }
}); });
sendingMax.observe(getViewLifecycleOwner(), sendingMax -> {
if(sendingMax) {
amountEditText.setVisibility(View.INVISIBLE);
sendAllTextView.setVisibility(View.VISIBLE);
sendMaxButton.setText(getText(R.string.undo));
} else {
amountEditText.setVisibility(View.VISIBLE);
sendAllTextView.setVisibility(View.GONE);
sendMaxButton.setText(getText(R.string.send_max));
}
});
} }
} }

View file

@ -34,6 +34,8 @@ import com.m2049r.xmrwallet.service.BalanceService;
import com.m2049r.xmrwallet.service.HistoryService; import com.m2049r.xmrwallet.service.HistoryService;
import com.m2049r.xmrwallet.service.TxService; import com.m2049r.xmrwallet.service.TxService;
import java.util.Collections;
public class HomeFragment extends Fragment implements TransactionInfoAdapter.TxInfoAdapterListener { public class HomeFragment extends Fragment implements TransactionInfoAdapter.TxInfoAdapterListener {
private HomeViewModel mViewModel; private HomeViewModel mViewModel;
@ -74,10 +76,20 @@ public class HomeFragment extends Fragment implements TransactionInfoAdapter.TxI
private void bindObservers(View view) { private void bindObservers(View view) {
RecyclerView txHistoryRecyclerView = view.findViewById(R.id.transaction_history_recyclerview); RecyclerView txHistoryRecyclerView = view.findViewById(R.id.transaction_history_recyclerview);
TextView balanceTextView = view.findViewById(R.id.balance_textview); TextView unlockedBalanceTextView = view.findViewById(R.id.balance_unlocked_textview);
TextView lockedBalanceTextView = view.findViewById(R.id.balance_locked_textview);
BalanceService.getInstance().balance.observe(getViewLifecycleOwner(), balance -> { BalanceService.getInstance().balance.observe(getViewLifecycleOwner(), balance -> {
balanceTextView.setText(getString(R.string.wallet_balance_text, Wallet.getDisplayAmount(balance))); unlockedBalanceTextView.setText(getString(R.string.wallet_balance_text, Wallet.getDisplayAmount(balance)));
});
BalanceService.getInstance().lockedBalance.observe(getViewLifecycleOwner(), lockedBalance -> {
if(lockedBalance == 0) {
lockedBalanceTextView.setVisibility(View.INVISIBLE);
} else {
lockedBalanceTextView.setText(getString(R.string.wallet_locked_balance_text, Wallet.getDisplayAmount(lockedBalance)));
lockedBalanceTextView.setVisibility(View.VISIBLE);
}
}); });
TransactionInfoAdapter adapter = new TransactionInfoAdapter(this); TransactionInfoAdapter adapter = new TransactionInfoAdapter(this);
@ -87,6 +99,7 @@ public class HomeFragment extends Fragment implements TransactionInfoAdapter.TxI
if(history.isEmpty()) { if(history.isEmpty()) {
txHistoryRecyclerView.setVisibility(View.GONE); txHistoryRecyclerView.setVisibility(View.GONE);
} else { } else {
Collections.sort(history);
adapter.submitList(history); adapter.submitList(history);
txHistoryRecyclerView.setVisibility(View.VISIBLE); txHistoryRecyclerView.setVisibility(View.VISIBLE);
} }

View file

@ -15,6 +15,8 @@ public class BalanceService extends ServiceBase {
private final MutableLiveData<Long> _balance = new MutableLiveData<>(0L); private final MutableLiveData<Long> _balance = new MutableLiveData<>(0L);
public LiveData<Long> balance = _balance; public LiveData<Long> balance = _balance;
private final MutableLiveData<Long> _lockedBalance = new MutableLiveData<>(0L);
public LiveData<Long> lockedBalance = _lockedBalance;
public BalanceService(MainActivity mainActivity, MoneroHandlerThread thread) { public BalanceService(MainActivity mainActivity, MoneroHandlerThread thread) {
super(mainActivity, thread); super(mainActivity, thread);
@ -22,6 +24,19 @@ public class BalanceService extends ServiceBase {
} }
public void refreshBalance() { public void refreshBalance() {
_balance.postValue(WalletManager.getInstance().getWallet().getBalance()); _balance.postValue(getUnlockedBalanceRaw());
_lockedBalance.postValue(getLockedBalanceRaw());
}
public long getUnlockedBalanceRaw() {
return WalletManager.getInstance().getWallet().getUnlockedBalance();
}
public long getTotalBalanceRaw() {
return WalletManager.getInstance().getWallet().getBalance();
}
public long getLockedBalanceRaw() {
return getTotalBalanceRaw() - getUnlockedBalanceRaw();
} }
} }

View file

@ -7,6 +7,7 @@ import com.m2049r.xmrwallet.MainActivity;
import com.m2049r.xmrwallet.model.TransactionInfo; import com.m2049r.xmrwallet.model.TransactionInfo;
import com.m2049r.xmrwallet.model.WalletManager; import com.m2049r.xmrwallet.model.WalletManager;
import java.util.Collections;
import java.util.List; import java.util.List;
public class HistoryService extends ServiceBase { public class HistoryService extends ServiceBase {
@ -25,7 +26,7 @@ public class HistoryService extends ServiceBase {
} }
public void refreshHistory() { public void refreshHistory() {
_history.postValue(WalletManager.getInstance().getWallet().getHistory().getAll()); _history.postValue(getHistory());
} }
public List<TransactionInfo> getHistory() { public List<TransactionInfo> getHistory() {

View file

@ -97,9 +97,9 @@ public class MoneroHandlerThread extends Thread implements WalletListener {
listener.onRefresh(); listener.onRefresh();
} }
public boolean sendTx(String address, String amountStr) { public boolean sendTx(String address, String amountStr, boolean sendAll) {
long amount = Wallet.getAmountFromString(amountStr); long amount = sendAll ? SWEEP_ALL : Wallet.getAmountFromString(amountStr);
PendingTransaction pendingTx = wallet.createTransaction(new TxData(address, SWEEP_ALL, 0, PendingTransaction.Priority.Priority_Default)); PendingTransaction pendingTx = wallet.createTransaction(new TxData(address, amount, 0, PendingTransaction.Priority.Priority_Default));
return pendingTx.commit("", true); return pendingTx.commit("", true);
} }

View file

@ -18,8 +18,8 @@ public class TxService extends ServiceBase {
instance = this; instance = this;
} }
public void sendTx(String address, String amount) { public void sendTx(String address, String amount, boolean sendAll) {
boolean success = this.getThread().sendTx(address, amount); boolean success = this.getThread().sendTx(address, amount, sendAll);
if (success) { if (success) {
_clearSendEvent.call(); _clearSendEvent.call();
} }

View file

@ -4,6 +4,6 @@
android:viewportWidth="24.0" android:viewportWidth="24.0"
android:viewportHeight="24.0"> android:viewportHeight="24.0">
<path <path
android:fillColor="@color/btn_color_selector" android:fillColor="@color/oled_textColorPrimary"
android:pathData="M19,2h-4.18C14.4,0.84 13.3,0 12,0c-1.3,0 -2.4,0.84 -2.82,2L5,2c-1.1,0 -2,0.9 -2,2v16c0,1.1 0.9,2 2,2h14c1.1,0 2,-0.9 2,-2L21,4c0,-1.1 -0.9,-2 -2,-2zM12,2c0.55,0 1,0.45 1,1s-0.45,1 -1,1 -1,-0.45 -1,-1 0.45,-1 1,-1zM19,20L5,20L5,4h2v3h10L17,4h2v16z" /> android:pathData="M19,2h-4.18C14.4,0.84 13.3,0 12,0c-1.3,0 -2.4,0.84 -2.82,2L5,2c-1.1,0 -2,0.9 -2,2v16c0,1.1 0.9,2 2,2h14c1.1,0 2,-0.9 2,-2L21,4c0,-1.1 -0.9,-2 -2,-2zM12,2c0.55,0 1,0.45 1,1s-0.45,1 -1,1 -1,-0.45 -1,-1 0.45,-1 1,-1zM19,20L5,20L5,4h2v3h10L17,4h2v16z" />
</vector> </vector>

View file

@ -6,35 +6,37 @@
android:layout_height="match_parent" android:layout_height="match_parent"
tools:context=".fragment.home.HomeFragment"> tools:context=".fragment.home.HomeFragment">
<ImageView <TextView
android:id="@+id/settings_imageview" android:id="@+id/balance_unlocked_textview"
android:layout_width="wrap_content" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:src="@drawable/ic_settings" android:layout_marginStart="24dp"
android:layout_marginEnd="24dp" android:layout_marginEnd="24dp"
android:layout_marginTop="24dp" android:layout_marginTop="24dp"
app:layout_constraintTop_toTopOf="parent" android:textAlignment="center"
app:layout_constraintEnd_toEndOf="parent"/> tools:text="UNLOCKED BALANCE"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"/>
<TextView <TextView
android:id="@+id/balance_textview" android:id="@+id/balance_locked_textview"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_marginStart="24dp" android:layout_marginStart="24dp"
android:layout_marginEnd="24dp" android:layout_marginEnd="24dp"
android:textAlignment="center" android:textAlignment="center"
tools:text="BALANCE" tools:text="LOCKED BALANCE"
app:layout_constraintEnd_toEndOf="parent" app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent" app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="@id/settings_imageview" app:layout_constraintTop_toBottomOf="@id/balance_unlocked_textview" />
app:layout_constraintBottom_toBottomOf="@id/settings_imageview"/>
<androidx.recyclerview.widget.RecyclerView <androidx.recyclerview.widget.RecyclerView
android:id="@+id/transaction_history_recyclerview" android:id="@+id/transaction_history_recyclerview"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_marginTop="16dp" android:layout_marginTop="16dp"
app:layout_constraintTop_toBottomOf="@id/balance_textview" app:layout_constraintTop_toBottomOf="@id/balance_locked_textview"
app:layout_constraintBottom_toTopOf="@id/receive_button" app:layout_constraintBottom_toTopOf="@id/receive_button"
app:layout_constraintStart_toStartOf="parent" app:layout_constraintStart_toStartOf="parent"
app:layout_constraintEnd_toEndOf="parent" app:layout_constraintEnd_toEndOf="parent"
@ -63,4 +65,14 @@
app:layout_constraintStart_toEndOf="@id/receive_button" app:layout_constraintStart_toEndOf="@id/receive_button"
app:layout_constraintEnd_toEndOf="parent" app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintBottom_toBottomOf="parent"/> app:layout_constraintBottom_toBottomOf="parent"/>
<ImageView
android:id="@+id/settings_imageview"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:src="@drawable/ic_settings"
android:layout_marginEnd="24dp"
app:layout_constraintTop_toTopOf="@id/balance_unlocked_textview"
app:layout_constraintBottom_toBottomOf="@id/balance_unlocked_textview"
app:layout_constraintEnd_toEndOf="parent"/>
</androidx.constraintlayout.widget.ConstraintLayout> </androidx.constraintlayout.widget.ConstraintLayout>

View file

@ -19,25 +19,62 @@
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_marginStart="24dp" android:layout_marginStart="24dp"
android:layout_marginEnd="24dp" android:layout_marginEnd="24dp"
android:layout_marginBottom="32dp"
android:hint="Address" android:hint="Address"
app:layout_constraintTop_toTopOf="parent" app:layout_constraintTop_toTopOf="parent"
app:layout_constraintBottom_toTopOf="@id/amount_edittext"/> app:layout_constraintBottom_toTopOf="@id/amount_edittext"/>
<ImageButton
android:id="@+id/paste_address_imagebutton"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:background="@android:color/transparent"
android:padding="8dp"
android:src="@drawable/ic_content_paste_24dp"
app:layout_constraintTop_toTopOf="@id/address_edittext"
app:layout_constraintBottom_toBottomOf="@id/address_edittext"
app:layout_constraintEnd_toEndOf="@id/address_edittext"/>
<EditText <EditText
android:id="@+id/amount_edittext" android:id="@+id/amount_edittext"
android:layout_width="match_parent" android:layout_width="0dp"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_marginStart="24dp" android:layout_marginStart="24dp"
android:layout_marginEnd="24dp" android:layout_marginEnd="12dp"
android:hint="Amount" android:hint="Amount"
android:inputType="numberDecimal" android:inputType="numberDecimal"
app:layout_constraintBottom_toTopOf="@id/send_button"/> app:layout_constraintStart_toStartOf="parent"
app:layout_constraintEnd_toStartOf="@id/send_max_button"
app:layout_constraintBottom_toTopOf="@id/send_button"
tools:visibility="visible"/>
<TextView
android:id="@+id/sending_all_textview"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginStart="24dp"
android:layout_marginEnd="12dp"
android:text="SENDING ALL"
android:textStyle="bold"
android:visibility="gone"
app:layout_constraintTop_toTopOf="@id/amount_edittext"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintEnd_toStartOf="@id/send_max_button"
app:layout_constraintBottom_toBottomOf="@id/amount_edittext"/>
<Button
android:id="@+id/send_max_button"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginEnd="24dp"
android:text="@string/send_max"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintTop_toTopOf="@id/amount_edittext"
app:layout_constraintBottom_toBottomOf="@id/amount_edittext"
app:layout_constraintStart_toEndOf="@id/amount_edittext"/>
<Button <Button
android:id="@+id/send_button" android:id="@+id/send_button"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_marginStart="24dp" android:layout_marginStart="24dp"
android:layout_marginEnd="24dp" android:layout_marginEnd="24dp"
android:layout_marginTop="32dp"
android:text="Send" android:text="Send"
app:layout_constraintTop_toBottomOf="@id/amount_edittext" app:layout_constraintTop_toBottomOf="@id/amount_edittext"
app:layout_constraintStart_toStartOf="parent"/> app:layout_constraintStart_toStartOf="parent"/>

View file

@ -536,5 +536,9 @@
<string name="hello_first_fragment">Hello first fragment</string> <string name="hello_first_fragment">Hello first fragment</string>
<string name="hello_second_fragment">Hello second fragment. Arg: %1$s</string> <string name="hello_second_fragment">Hello second fragment. Arg: %1$s</string>
<string name="wallet_balance_text">%1$s XMR</string> <string name="wallet_balance_text">%1$s XMR</string>
<string name="wallet_locked_balance_text">+ %1$s confirming</string>
<string name="send_amount_empty">Please enter an amount</string> <string name="send_amount_empty">Please enter an amount</string>
<string name="send_amount_invalid">Please enter a valid amount</string>
<string name="send_max">Send Max</string>
<string name="undo">Undo</string>
</resources> </resources>