wowwallet/app/src/main/java/com/m2049r/xmrwallet/WalletFragment.java

378 lines
13 KiB
Java
Raw Normal View History

/*
* Copyright (c) 2017 m2049r
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.m2049r.xmrwallet;
import android.content.Context;
import android.os.Bundle;
2017-09-02 02:05:50 -06:00
import android.support.annotation.Nullable;
import android.support.v4.app.Fragment;
import android.support.v7.widget.RecyclerView;
import android.util.Log;
import android.view.LayoutInflater;
2017-09-02 02:05:50 -06:00
import android.view.Menu;
import android.view.MenuInflater;
import android.view.View;
import android.view.ViewGroup;
2017-10-31 15:35:23 -06:00
import android.widget.AdapterView;
import android.widget.ArrayAdapter;
import android.widget.Button;
2017-10-31 15:35:23 -06:00
import android.widget.FrameLayout;
import android.widget.ImageView;
import android.widget.ProgressBar;
2017-10-31 15:35:23 -06:00
import android.widget.Spinner;
import android.widget.TextView;
2017-10-31 15:35:23 -06:00
import com.m2049r.xmrwallet.layout.AsyncExchangeRate;
import com.m2049r.xmrwallet.layout.Toolbar;
import com.m2049r.xmrwallet.layout.TransactionInfoAdapter;
import com.m2049r.xmrwallet.model.TransactionInfo;
import com.m2049r.xmrwallet.model.Wallet;
import com.m2049r.xmrwallet.util.Helper;
import java.text.NumberFormat;
import java.util.List;
2017-10-31 15:35:23 -06:00
public class WalletFragment extends Fragment
implements TransactionInfoAdapter.OnInteractionListener,
AsyncExchangeRate.Listener {
private static final String TAG = "WalletFragment";
private TransactionInfoAdapter adapter;
private NumberFormat formatter = NumberFormat.getInstance();
private FrameLayout flExchange;
private TextView tvBalance;
private TextView tvUnconfirmedAmount;
private TextView tvProgress;
private ImageView ivSynced;
private ProgressBar pbProgress;
private Button bReceive;
private Button bSend;
private Spinner sCurrency;
2017-10-31 15:35:23 -06:00
2017-09-02 02:05:50 -06:00
@Override
public void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setHasOptionsMenu(true);
}
@Override
public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) {
if (activityCallback.hasWallet())
2017-09-08 15:59:50 -06:00
inflater.inflate(R.menu.wallet_menu, menu);
2017-09-02 02:05:50 -06:00
super.onCreateOptionsMenu(menu, inflater);
}
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
2017-10-31 15:35:23 -06:00
View view = inflater.inflate(R.layout.fragment_wallet, container, false);
flExchange = (FrameLayout) view.findViewById(R.id.flExchange);
((ProgressBar) view.findViewById(R.id.pbExchange)).getIndeterminateDrawable().
setColorFilter(getResources().getColor(R.color.trafficGray),
android.graphics.PorterDuff.Mode.MULTIPLY);
tvProgress = (TextView) view.findViewById(R.id.tvProgress);
pbProgress = (ProgressBar) view.findViewById(R.id.pbProgress);
tvBalance = (TextView) view.findViewById(R.id.tvBalance);
2017-10-31 15:35:23 -06:00
tvBalance.setText(Helper.getDisplayAmount(0));
tvUnconfirmedAmount = (TextView) view.findViewById(R.id.tvUnconfirmedAmount);
2017-09-08 15:59:50 -06:00
tvUnconfirmedAmount.setText(getResources().getString(R.string.xmr_unconfirmed_amount, Helper.getDisplayAmount(0)));
2017-10-31 15:35:23 -06:00
ivSynced = (ImageView) view.findViewById(R.id.ivSynced);
sCurrency = (Spinner) view.findViewById(R.id.sCurrency);
2017-11-03 03:14:19 -06:00
sCurrency.setAdapter(ArrayAdapter.createFromResource(getContext(), R.array.currency, R.layout.item_spinner_balance));
bSend = (Button) view.findViewById(R.id.bSend);
2017-10-31 15:35:23 -06:00
bReceive = (Button) view.findViewById(R.id.bReceive);
RecyclerView recyclerView = (RecyclerView) view.findViewById(R.id.list);
2017-09-08 15:59:50 -06:00
this.adapter = new TransactionInfoAdapter(getActivity(), this);
recyclerView.setAdapter(adapter);
2017-10-31 15:35:23 -06:00
bSend.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
activityCallback.onSendRequest();
}
});
2017-10-31 15:35:23 -06:00
bReceive.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
activityCallback.onWalletReceive();
}
});
sCurrency.setOnItemSelectedListener(new AdapterView.OnItemSelectedListener() {
@Override
public void onItemSelected(AdapterView<?> parentView, View selectedItemView, int position, long id) {
refreshBalance();
}
@Override
public void onNothingSelected(AdapterView<?> parentView) {
// nothing (yet?)
}
});
if (activityCallback.isSynced()) {
onSynced();
}
activityCallback.forceUpdate();
return view;
}
2017-10-31 15:35:23 -06:00
String balanceCurrency = "XMR";
double balanceRate = 1.0;
void refreshBalance() {
if (sCurrency.getSelectedItemPosition() == 0) { // XMR
double amountXmr = Double.parseDouble(Wallet.getDisplayAmount(unlockedBalance)); // assume this cannot fail!
tvBalance.setText(Helper.getFormattedAmount(amountXmr, true));
} else { // not XMR
String currency = (String) sCurrency.getSelectedItem();
if (!currency.equals(balanceCurrency) || (balanceRate <= 0)) {
showExchanging();
new AsyncExchangeRate(this).execute("XMR", currency);
} else {
exchange("XMR", balanceCurrency, balanceRate);
}
}
}
boolean isExchanging = false;
void showExchanging() {
isExchanging = true;
tvBalance.setVisibility(View.GONE);
flExchange.setVisibility(View.VISIBLE);
}
void hideExchanging() {
isExchanging = false;
tvBalance.setVisibility(View.VISIBLE);
flExchange.setVisibility(View.GONE);
}
// Callbacks from AsyncExchangeRate
// callback from AsyncExchangeRate when it can't get exchange rate
public void exchangeFailed() {
sCurrency.setSelection(0, true); // default to XMR
double amountXmr = Double.parseDouble(Wallet.getDisplayAmount(unlockedBalance)); // assume this cannot fail!
tvBalance.setText(Helper.getFormattedAmount(amountXmr, true));
hideExchanging();
}
void updateBalance() {
if (isExchanging) return; // wait for exchange to finish - it will fire this itself then.
// at this point selection is XMR in case of error
String displayB;
double amountA = Double.parseDouble(Wallet.getDisplayAmount(unlockedBalance)); // assume this cannot fail!
if (!"XMR".equals(balanceCurrency)) { // not XMR
double amountB = amountA * balanceRate;
displayB = Helper.getFormattedAmount(amountB, false);
} else { // XMR
displayB = Helper.getFormattedAmount(amountA, true);
}
tvBalance.setText(displayB);
}
// callback from AsyncExchangeRate when we have a rate
public void exchange(String currencyA, String currencyB, double rate) {
hideExchanging();
if (!"XMR".equals(currencyA)) {
Log.e(TAG, "Not XMR");
sCurrency.setSelection(0, true);
balanceCurrency = "XMR";
balanceRate = 1.0;
} else {
int spinnerPosition = ((ArrayAdapter) sCurrency.getAdapter()).getPosition(currencyB);
if (spinnerPosition < 0) { // requested currency not in list
Log.e(TAG, "Requested currency not in list " + currencyB);
sCurrency.setSelection(0, true);
} else {
sCurrency.setSelection(spinnerPosition, true);
}
balanceCurrency = currencyB;
balanceRate = rate;
}
updateBalance();
}
// Callbacks from TransactionInfoAdapter
@Override
public void onInteraction(final View view, final TransactionInfo infoItem) {
2017-08-19 11:40:48 -06:00
activityCallback.onTxDetailsRequest(infoItem);
}
// called from activity
2017-10-31 15:35:23 -06:00
public void onRefreshed(final Wallet wallet, final boolean full) {
Log.d(TAG, "onRefreshed()");
if (full) {
List<TransactionInfo> list = wallet.getHistory().getAll();
adapter.setInfos(list);
adapter.notifyDataSetChanged();
}
updateStatus(wallet);
}
2017-08-20 08:18:59 -06:00
public void onSynced() {
2017-08-29 14:22:07 -06:00
if (!activityCallback.isWatchOnly()) {
2017-08-19 08:20:10 -06:00
bSend.setVisibility(View.VISIBLE);
bSend.setEnabled(true);
}
}
2017-10-31 15:35:23 -06:00
public void setProgressText(final String text) {
tvProgress.setText(text);
}
public void onProgress(final String text) {
if (text != null) {
2017-10-31 15:35:23 -06:00
setProgressText(text);
pbProgress.setVisibility(View.VISIBLE);
} else {
2017-10-31 15:35:23 -06:00
pbProgress.setVisibility(View.INVISIBLE);
setProgressText(getString(R.string.status_working));
onProgress(-1);
}
}
public void onProgress(final int n) {
if (n >= 0) {
pbProgress.setIndeterminate(false);
pbProgress.setProgress(n);
} else {
pbProgress.setIndeterminate(true);
}
}
2017-10-31 15:35:23 -06:00
void setActivityTitle(Wallet wallet) {
if (wallet == null) return;
walletTitle = wallet.getName();
String watchOnly = (wallet.isWatchOnly() ? getString(R.string.label_watchonly) : "");
walletSubtitle = wallet.getAddress().substring(0, 16) + "" + watchOnly;
activityCallback.setTitle(walletTitle, walletSubtitle);
Log.d(TAG, "wallet title is " + walletTitle);
}
private long firstBlock = 0;
private String walletTitle = null;
2017-10-31 15:35:23 -06:00
private String walletSubtitle = null;
private long unlockedBalance = 0;
private void updateStatus(Wallet wallet) {
if (!isAdded()) return;
Log.d(TAG, "updateStatus()");
if (walletTitle == null) {
2017-10-31 15:35:23 -06:00
setActivityTitle(wallet);
onProgress(100); // of loading
}
long balance = wallet.getBalance();
2017-10-31 15:35:23 -06:00
unlockedBalance = wallet.getUnlockedBalance();
refreshBalance();
double amountXmr = Double.parseDouble(Helper.getDisplayAmount(balance - unlockedBalance)); // assume this cannot fail!
String unconfirmed = Helper.getFormattedAmount(amountXmr, true);
tvUnconfirmedAmount.setText(getResources().getString(R.string.xmr_unconfirmed_amount, unconfirmed));
//tvUnconfirmedAmount.setText(getResources().getString(R.string.xmr_unconfirmed_amount,
// Helper.getDisplayAmount(balance - unlockedBalance, Helper.DISPLAY_DIGITS_SHORT)));
String sync = "";
if (!activityCallback.hasBoundService())
throw new IllegalStateException("WalletService not bound.");
Wallet.ConnectionStatus daemonConnected = activityCallback.getConnectionStatus();
if (daemonConnected == Wallet.ConnectionStatus.ConnectionStatus_Connected) {
long daemonHeight = activityCallback.getDaemonHeight();
if (!wallet.isSynchronized()) {
long n = daemonHeight - wallet.getBlockChainHeight();
sync = getString(R.string.status_syncing) + " " + formatter.format(n) + " " + getString(R.string.status_remaining);
if (firstBlock == 0) {
firstBlock = wallet.getBlockChainHeight();
}
int x = 100 - Math.round(100f * n / (1f * daemonHeight - firstBlock));
//onProgress(getString(R.string.status_syncing) + " " + sync);
if (x == 0) x = -1;
onProgress(x);
2017-10-31 15:35:23 -06:00
ivSynced.setVisibility(View.GONE);
} else {
sync = getString(R.string.status_synced) + formatter.format(wallet.getBlockChainHeight());
2017-10-31 15:35:23 -06:00
ivSynced.setVisibility(View.VISIBLE);
}
}
2017-10-31 15:35:23 -06:00
setProgressText(sync);
// TODO show connected status somewhere
}
2017-08-15 15:59:41 -06:00
Listener activityCallback;
// Container Activity must implement this interface
2017-08-15 15:59:41 -06:00
public interface Listener {
boolean hasBoundService();
void forceUpdate();
Wallet.ConnectionStatus getConnectionStatus();
long getDaemonHeight(); //mBoundService.getDaemonHeight();
void onSendRequest();
2017-08-19 11:40:48 -06:00
void onTxDetailsRequest(TransactionInfo info);
boolean isSynced();
2017-08-19 08:20:10 -06:00
boolean isWatchOnly();
String getTxKey(String txId);
2017-09-02 02:05:50 -06:00
void onWalletReceive();
boolean hasWallet();
2017-10-31 15:35:23 -06:00
void setToolbarButton(int type);
void setTitle(String title, String subtitle);
void setSubtitle(String subtitle);
}
@Override
public void onAttach(Context context) {
super.onAttach(context);
2017-08-15 15:59:41 -06:00
if (context instanceof Listener) {
this.activityCallback = (Listener) context;
} else {
throw new ClassCastException(context.toString()
2017-08-15 15:59:41 -06:00
+ " must implement Listener");
}
}
2017-10-31 15:35:23 -06:00
@Override
public void onResume() {
super.onResume();
Log.d(TAG, "onResume()");
activityCallback.setTitle(walletTitle, walletSubtitle);
activityCallback.setToolbarButton(Toolbar.BUTTON_CLOSE);
}
}