Add tx details screen

This commit is contained in:
pokkst 2022-09-17 14:35:31 -05:00
parent 909e4a4231
commit e60c38bd01
No known key found for this signature in database
GPG key ID: 90C2ED85E67A50FF
10 changed files with 334 additions and 14 deletions

View file

@ -1,4 +1,5 @@
apply plugin: 'com.android.application' apply plugin: 'com.android.application'
apply plugin: "androidx.navigation.safeargs"
android { android {
compileSdkVersion 31 compileSdkVersion 31

View file

@ -1,7 +1,5 @@
package com.m2049r.xmrwallet.fragment.home; package com.m2049r.xmrwallet.fragment.home;
import android.content.Intent;
import android.net.Uri;
import android.os.Bundle; import android.os.Bundle;
import android.view.LayoutInflater; import android.view.LayoutInflater;
import android.view.View; import android.view.View;
@ -18,6 +16,7 @@ import androidx.fragment.app.Fragment;
import androidx.fragment.app.FragmentActivity; import androidx.fragment.app.FragmentActivity;
import androidx.fragment.app.FragmentManager; import androidx.fragment.app.FragmentManager;
import androidx.lifecycle.ViewModelProvider; import androidx.lifecycle.ViewModelProvider;
import androidx.navigation.NavDirections;
import androidx.navigation.fragment.NavHostFragment; import androidx.navigation.fragment.NavHostFragment;
import androidx.recyclerview.widget.LinearLayoutManager; import androidx.recyclerview.widget.LinearLayoutManager;
import androidx.recyclerview.widget.RecyclerView; import androidx.recyclerview.widget.RecyclerView;
@ -33,7 +32,6 @@ import com.m2049r.xmrwallet.model.WalletManager;
import com.m2049r.xmrwallet.service.BalanceService; import com.m2049r.xmrwallet.service.BalanceService;
import com.m2049r.xmrwallet.service.BlockchainService; import com.m2049r.xmrwallet.service.BlockchainService;
import com.m2049r.xmrwallet.service.HistoryService; import com.m2049r.xmrwallet.service.HistoryService;
import com.m2049r.xmrwallet.util.UriData;
import java.util.Collections; import java.util.Collections;
@ -68,7 +66,7 @@ public class HomeFragment extends Fragment implements TransactionInfoAdapter.TxI
Button receiveButton = view.findViewById(R.id.receive_button); Button receiveButton = view.findViewById(R.id.receive_button);
settingsImageView.setOnClickListener(view12 -> { settingsImageView.setOnClickListener(view12 -> {
navigate(R.id.settings_fragment); navigate(HomeFragmentDirections.navToSettings());
}); });
sendButton.setOnClickListener(view1 -> { sendButton.setOnClickListener(view1 -> {
@ -152,10 +150,11 @@ public class HomeFragment extends Fragment implements TransactionInfoAdapter.TxI
@Override @Override
public void onClickTransaction(TransactionInfo txInfo) { public void onClickTransaction(TransactionInfo txInfo) {
System.out.println(txInfo.hash); NavDirections directions = HomeFragmentDirections.navToTransaction(txInfo);
navigate(directions);
} }
private void navigate(int destination) { private void navigate(NavDirections destination) {
FragmentActivity activity = getActivity(); FragmentActivity activity = getActivity();
if (activity != null) { if (activity != null) {
FragmentManager fm = activity.getSupportFragmentManager(); FragmentManager fm = activity.getSupportFragmentManager();

View file

@ -0,0 +1,115 @@
package com.m2049r.xmrwallet.fragment.transaction;
import static com.m2049r.xmrwallet.util.DateHelper.DATETIME_FORMATTER;
import android.os.Bundle;
import android.text.Editable;
import android.text.TextWatcher;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.Button;
import android.widget.EditText;
import android.widget.ImageButton;
import android.widget.ImageView;
import android.widget.TextView;
import android.widget.Toast;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.fragment.app.Fragment;
import androidx.fragment.app.FragmentActivity;
import androidx.fragment.app.FragmentManager;
import androidx.lifecycle.Observer;
import androidx.lifecycle.ViewModelProvider;
import androidx.navigation.fragment.NavHostFragment;
import com.m2049r.xmrwallet.MainActivity;
import com.m2049r.xmrwallet.R;
import com.m2049r.xmrwallet.model.TransactionInfo;
import com.m2049r.xmrwallet.model.Wallet;
import com.m2049r.xmrwallet.model.WalletManager;
import com.m2049r.xmrwallet.service.PrefService;
import com.m2049r.xmrwallet.util.Constants;
import com.m2049r.xmrwallet.util.Helper;
import java.io.File;
import java.util.Calendar;
import java.util.Date;
import java.util.Objects;
import java.util.TimeZone;
public class TransactionFragment extends Fragment {
private TransactionViewModel mViewModel;
@Override
public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container,
@Nullable Bundle savedInstanceState) {
return inflater.inflate(R.layout.fragment_transaction, container, false);
}
@Override
public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) {
super.onViewCreated(view, savedInstanceState);
Calendar cal = Calendar.getInstance();
TimeZone tz = cal.getTimeZone(); //get the local time zone.
DATETIME_FORMATTER.setTimeZone(tz);
mViewModel = new ViewModelProvider(this).get(TransactionViewModel.class);
Bundle args = getArguments();
if(args != null) {
TransactionInfo txInfo = getArguments().getParcelable(Constants.NAV_ARG_TXINFO);
mViewModel.init(txInfo);
}
bindObservers(view);
bindListeners(view);
}
private void bindListeners(View view) {
ImageButton copyTxHashImageButton = view.findViewById(R.id.copy_txhash_imagebutton);
copyTxHashImageButton.setOnClickListener(view1 -> {
TransactionInfo txInfo = mViewModel.transaction.getValue();
if(txInfo != null) {
Helper.clipBoardCopy(getContext(), "transaction_hash", txInfo.hash);
}
});
ImageButton copyTxAddressImageButton = view.findViewById(R.id.copy_txaddress_imagebutton);
copyTxAddressImageButton.setOnClickListener(view1 -> {
TransactionInfo txInfo = mViewModel.transaction.getValue();
if(txInfo != null) {
String destination = mViewModel.destination.getValue();
if(destination != null) {
Helper.clipBoardCopy(getContext(), "transaction_address", destination);
}
}
});
}
private void bindObservers(View view) {
TextView txHashTextView = view.findViewById(R.id.transaction_hash_textview);
TextView txConfTextView = view.findViewById(R.id.transaction_conf_textview);
TextView txAddressTextView = view.findViewById(R.id.transaction_address_textview);
ImageButton copyTxAddressImageButton = view.findViewById(R.id.copy_txaddress_imagebutton);
TextView txDateTextView = view.findViewById(R.id.transaction_date_textview);
mViewModel.transaction.observe(getViewLifecycleOwner(), transactionInfo -> {
txHashTextView.setText(transactionInfo.hash);
txConfTextView.setText(""+transactionInfo.confirmations);
txDateTextView.setText(getDateTime(transactionInfo.timestamp));
});
mViewModel.destination.observe(getViewLifecycleOwner(), s -> {
txAddressTextView.setText(Objects.requireNonNullElse(s, "-"));
if(s == null) {
copyTxAddressImageButton.setVisibility(View.INVISIBLE);
}
});
}
private String getDateTime(long time) {
return DATETIME_FORMATTER.format(new Date(time * 1000));
}
}

View file

@ -0,0 +1,35 @@
package com.m2049r.xmrwallet.fragment.transaction;
import androidx.lifecycle.LiveData;
import androidx.lifecycle.MutableLiveData;
import androidx.lifecycle.ViewModel;
import com.m2049r.xmrwallet.model.TransactionInfo;
import com.m2049r.xmrwallet.model.Transfer;
import com.m2049r.xmrwallet.model.Wallet;
import com.m2049r.xmrwallet.model.WalletManager;
import com.m2049r.xmrwallet.service.HistoryService;
public class TransactionViewModel extends ViewModel {
private final MutableLiveData<TransactionInfo> _transaction = new MutableLiveData<>(null);
public LiveData<TransactionInfo> transaction = _transaction;
private final MutableLiveData<String> _destination = new MutableLiveData<>(null);
public LiveData<String> destination = _destination;
public void init(TransactionInfo info) {
Wallet wallet = WalletManager.getInstance().getWallet();
if(info.txKey == null) {
info.txKey = wallet.getTxKey(info.hash);
}
if(info.address == null && info.direction == TransactionInfo.Direction.Direction_In) {
_destination.setValue(wallet.getSubaddress(info.accountIndex, info.addressIndex));
} else if(info.address != null && info.direction == TransactionInfo.Direction.Direction_In) {
_destination.setValue(info.address);
} else if(info.transfers != null && info.direction == TransactionInfo.Direction.Direction_Out) {
if(info.transfers.size() == 1) {
_destination.setValue(info.transfers.get(0).address);
}
}
this._transaction.setValue(info);
}
}

View file

@ -12,4 +12,5 @@ public class Constants {
public static final String URI_PREFIX = "monero:"; public static final String URI_PREFIX = "monero:";
public static final String URI_ARG_AMOUNT = "tx_amount"; public static final String URI_ARG_AMOUNT = "tx_amount";
public static final String NAV_ARG_TXINFO = "nav_arg_txinfo";
} }

View file

@ -21,7 +21,7 @@ import java.text.SimpleDateFormat;
import java.util.Date; import java.util.Date;
public class DateHelper { public class DateHelper {
public static final SimpleDateFormat DATETIME_FORMATTER = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSSZ"); public static final SimpleDateFormat DATETIME_FORMATTER = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
public static Date parse(String dateString) throws ParseException { public static Date parse(String dateString) throws ParseException {
return DATETIME_FORMATTER.parse(dateString.replaceAll("Z$", "+0000")); return DATETIME_FORMATTER.parse(dateString.replaceAll("Z$", "+0000"));

View file

@ -0,0 +1,155 @@
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:padding="24dp"
tools:context=".fragment.settings.SettingsFragment">
<TextView
android:id="@+id/transaction_title_textview"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginBottom="32dp"
android:text="@string/transaction"
android:textSize="32sp"
android:textStyle="bold"
app:layout_constraintBottom_toTopOf="@id/transaction_hash_label_textview"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
<TextView
android:id="@+id/transaction_hash_label_textview"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="@string/transaction_hash"
android:textSize="18sp"
android:layout_marginTop="16dp"
android:textStyle="bold"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintTop_toBottomOf="@id/transaction_title_textview"/>
<TextView
android:id="@+id/transaction_hash_textview"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:text="@string/transaction_hash"
android:textSize="14sp"
android:layout_marginTop="8dp"
android:textStyle="bold"
android:singleLine="true"
android:ellipsize="middle"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintEnd_toStartOf="@id/copy_txhash_imagebutton"
app:layout_constraintTop_toTopOf="@id/copy_txhash_imagebutton"
app:layout_constraintBottom_toBottomOf="@id/copy_txhash_imagebutton"/>
<ImageButton
android:id="@+id/copy_txhash_imagebutton"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:background="@android:color/transparent"
android:minWidth="24dp"
android:minHeight="24dp"
android:padding="8dp"
android:src="@drawable/ic_content_copy_24dp"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toEndOf="@id/transaction_hash_textview"
app:layout_constraintTop_toBottomOf="@id/transaction_hash_label_textview" />
<TextView
android:id="@+id/transaction_conf_label_textview"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="@string/confirmations"
android:textSize="18sp"
android:layout_marginTop="16dp"
android:textStyle="bold"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintTop_toBottomOf="@id/copy_txhash_imagebutton"/>
<TextView
android:id="@+id/transaction_conf_textview"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:text="0"
android:textSize="14sp"
android:layout_marginTop="8dp"
android:textStyle="bold"
android:singleLine="true"
android:ellipsize="middle"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintTop_toBottomOf="@id/transaction_conf_label_textview"/>
<TextView
android:id="@+id/transaction_address_label_textview"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="@string/transaction_destination"
android:textSize="18sp"
android:layout_marginTop="16dp"
android:textStyle="bold"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintTop_toBottomOf="@id/transaction_conf_textview"/>
<TextView
android:id="@+id/transaction_address_textview"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:text="@string/transaction_hash"
android:textSize="14sp"
android:layout_marginTop="8dp"
android:textStyle="bold"
android:singleLine="true"
android:ellipsize="middle"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintEnd_toStartOf="@id/copy_txaddress_imagebutton"
app:layout_constraintTop_toTopOf="@id/copy_txaddress_imagebutton"
app:layout_constraintBottom_toBottomOf="@id/copy_txaddress_imagebutton"/>
<ImageButton
android:id="@+id/copy_txaddress_imagebutton"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:background="@android:color/transparent"
android:minWidth="24dp"
android:minHeight="24dp"
android:padding="8dp"
android:src="@drawable/ic_content_copy_24dp"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toEndOf="@id/transaction_address_textview"
app:layout_constraintTop_toBottomOf="@id/transaction_address_label_textview" />
<TextView
android:id="@+id/transaction_date_label_textview"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="@string/date"
android:textSize="18sp"
android:layout_marginTop="16dp"
android:textStyle="bold"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintTop_toBottomOf="@id/copy_txaddress_imagebutton"/>
<TextView
android:id="@+id/transaction_date_textview"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:text="0"
android:textSize="14sp"
android:layout_marginTop="8dp"
android:textStyle="bold"
android:singleLine="true"
android:ellipsize="middle"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintTop_toBottomOf="@id/transaction_date_label_textview"/>
</androidx.constraintlayout.widget.ConstraintLayout>

View file

@ -12,26 +12,33 @@
<action <action
android:id="@+id/nav_to_settings" android:id="@+id/nav_to_settings"
app:destination="@id/settings_fragment"> app:destination="@id/settings_fragment">
<argument
android:name="address"
app:argType="string" />
</action> </action>
<action <action
android:id="@+id/nav_to_onboarding" android:id="@+id/nav_to_onboarding"
app:destination="@id/onboarding_fragment"> app:destination="@id/onboarding_fragment">
</action>
<action
android:id="@+id/nav_to_transaction"
app:destination="@id/transaction_fragment">
<argument <argument
android:name="address" android:name="nav_arg_txinfo"
app:argType="string" /> app:argType="com.m2049r.xmrwallet.model.TransactionInfo"
app:nullable="true"/>
</action> </action>
</fragment> </fragment>
<fragment <fragment
android:id="@+id/settings_fragment" android:id="@+id/settings_fragment"
android:name="com.m2049r.xmrwallet.fragment.settings.SettingsFragment" android:name="com.m2049r.xmrwallet.fragment.settings.SettingsFragment"
android:label="fragment_send_amount" android:label="fragment_send_amount"
tools:layout="@layout/fragment_settings"></fragment> tools:layout="@layout/fragment_settings" />
<fragment <fragment
android:id="@+id/onboarding_fragment" android:id="@+id/onboarding_fragment"
android:name="com.m2049r.xmrwallet.fragment.onboarding.OnboardingFragment" android:name="com.m2049r.xmrwallet.fragment.onboarding.OnboardingFragment"
android:label="fragment_onboarding" android:label="fragment_onboarding"
tools:layout="@layout/fragment_settings"></fragment> tools:layout="@layout/fragment_settings" />
<fragment
android:id="@+id/transaction_fragment"
android:name="com.m2049r.xmrwallet.fragment.transaction.TransactionFragment"
android:label="fragment_onboarding"
tools:layout="@layout/fragment_settings" />
</navigation> </navigation>

View file

@ -80,6 +80,7 @@
<string name="nodes">Nodes</string> <string name="nodes">Nodes</string>
<string name="node_name_hint">My Monero Node</string> <string name="node_name_hint">My Monero Node</string>
<string name="node_address_hint">127.0.0.1:18081</string> <string name="node_address_hint">127.0.0.1:18081</string>
<string name="transaction">Transaction</string>
<string name="wallet_proxy_address_hint">127.0.0.1</string> <string name="wallet_proxy_address_hint">127.0.0.1</string>
<string name="wallet_proxy_port_hint">9050</string> <string name="wallet_proxy_port_hint">9050</string>
@ -89,4 +90,8 @@
<string name="connected">Connected</string> <string name="connected">Connected</string>
<string name="disconnected">Disconnected</string> <string name="disconnected">Disconnected</string>
<string name="version_mismatch">Version mismatch</string> <string name="version_mismatch">Version mismatch</string>
<string name="transaction_hash">Transaction Hash</string>
<string name="transaction_destination">Destination</string>
<string name="confirmations">Confirmations</string>
<string name="date">Date</string>
</resources> </resources>

View file

@ -6,6 +6,8 @@ buildscript {
} }
dependencies { dependencies {
classpath 'com.android.tools.build:gradle:7.2.0' classpath 'com.android.tools.build:gradle:7.2.0'
classpath "androidx.navigation:navigation-safe-args-gradle-plugin:2.5.2"
} }
} }