wownero
/
wownerujo
Archived
4
0
Fork 0
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
This repo is archived. You can view files and clone it, but cannot push or open issues/pull-requests.
wownerujo/app/src/main/java/com/wownero/wownerujo/WalletFragment.java

446 lines
16 KiB

/*
* 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.wownero.wownerujo;
import android.content.Context;
import android.os.Bundle;
import android.os.Handler;
import android.os.Looper;
import android.support.annotation.Nullable;
import android.support.v4.app.Fragment;
import android.support.v7.widget.RecyclerView;
import android.view.LayoutInflater;
import android.view.Menu;
import android.view.MenuInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.AdapterView;
import android.widget.ArrayAdapter;
import android.widget.Button;
import android.widget.FrameLayout;
import android.widget.ImageView;
import android.widget.ProgressBar;
import android.widget.Spinner;
import android.widget.TextView;
import com.wownero.wownerujo.layout.TransactionInfoAdapter;
import com.wownero.wownerujo.model.TransactionInfo;
import com.wownero.wownerujo.model.Wallet;
import com.wownero.wownerujo.service.exchange.api.ExchangeApi;
import com.wownero.wownerujo.service.exchange.api.ExchangeCallback;
import com.wownero.wownerujo.service.exchange.api.ExchangeRate;
import com.wownero.wownerujo.util.Helper;
import com.wownero.wownerujo.widget.Toolbar;
import java.text.NumberFormat;
import java.util.List;
import timber.log.Timber;
public class WalletFragment extends Fragment
implements TransactionInfoAdapter.OnInteractionListener {
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;
@Override
public void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setHasOptionsMenu(true);
}
@Override
public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) {
if (activityCallback.hasWallet())
inflater.inflate(R.menu.wallet_menu, menu);
super.onCreateOptionsMenu(menu, inflater);
}
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
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);
tvBalance.setText(Helper.getDisplayAmount(0));
tvUnconfirmedAmount = (TextView) view.findViewById(R.id.tvUnconfirmedAmount);
tvUnconfirmedAmount.setText(getResources().getString(R.string.wow_unconfirmed_amount, Helper.getDisplayAmount(0)));
ivSynced = (ImageView) view.findViewById(R.id.ivSynced);
sCurrency = (Spinner) view.findViewById(R.id.sCurrency);
ArrayAdapter currencyAdapter = ArrayAdapter.createFromResource(getContext(), R.array.currency, R.layout.item_spinner_balance);
currencyAdapter.setDropDownViewResource(R.layout.item_spinner_dropdown_item);
sCurrency.setAdapter(currencyAdapter);
sCurrency.setEnabled(false);
bSend = (Button) view.findViewById(R.id.bSend);
bReceive = (Button) view.findViewById(R.id.bReceive);
RecyclerView recyclerView = (RecyclerView) view.findViewById(R.id.list);
this.adapter = new TransactionInfoAdapter(getActivity(), this);
recyclerView.setAdapter(adapter);
bSend.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
activityCallback.onSendRequest();
}
});
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;
}
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)); // crash if this fails!
if (!Helper.CRYPTO.equals(balanceCurrency)) { // not XMR
double amountB = amountA * balanceRate;
displayB = Helper.getFormattedAmount(amountB, false);
} else { // XMR
displayB = Helper.getFormattedAmount(amountA, true);
}
tvBalance.setText(displayB);
}
String balanceCurrency = Helper.CRYPTO;
double balanceRate = 1.0;
private final ExchangeApi exchangeApi = Helper.getExchangeApi();
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();
Timber.d(currency);
if (!currency.equals(balanceCurrency) || (balanceRate <= 0)) {
showExchanging();
exchangeApi.queryExchangeRate(Helper.CRYPTO, currency,
new ExchangeCallback() {
@Override
public void onSuccess(final ExchangeRate exchangeRate) {
if (isAdded())
new Handler(Looper.getMainLooper()).post(new Runnable() {
@Override
public void run() {
exchange(exchangeRate);
}
});
}
@Override
public void onError(final Exception e) {
Timber.e(e.getLocalizedMessage());
if (isAdded())
new Handler(Looper.getMainLooper()).post(new Runnable() {
@Override
public void run() {
exchangeFailed();
}
});
}
});
} else {
updateBalance();
}
}
}
boolean isExchanging = false;
void showExchanging() {
isExchanging = true;
tvBalance.setVisibility(View.GONE);
flExchange.setVisibility(View.VISIBLE);
sCurrency.setEnabled(false);
}
void hideExchanging() {
isExchanging = false;
tvBalance.setVisibility(View.VISIBLE);
flExchange.setVisibility(View.GONE);
sCurrency.setEnabled(true);
}
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();
}
public void exchange(final ExchangeRate exchangeRate) {
hideExchanging();
if (!Helper.CRYPTO.equals(exchangeRate.getBaseCurrency())) {
Timber.e("Not XMR");
sCurrency.setSelection(0, true);
balanceCurrency = Helper.CRYPTO;
balanceRate = 1.0;
} else {
int spinnerPosition = ((ArrayAdapter) sCurrency.getAdapter()).getPosition(exchangeRate.getQuoteCurrency());
if (spinnerPosition < 0) { // requested currency not in list
Timber.e("Requested currency not in list %s", exchangeRate.getQuoteCurrency());
sCurrency.setSelection(0, true);
} else {
sCurrency.setSelection(spinnerPosition, true);
}
balanceCurrency = exchangeRate.getQuoteCurrency();
balanceRate = exchangeRate.getRate();
}
updateBalance();
}
// Callbacks from TransactionInfoAdapter
@Override
public void onInteraction(final View view, final TransactionInfo infoItem) {
activityCallback.onTxDetailsRequest(infoItem);
}
// called from activity
public void onRefreshed(final Wallet wallet, final boolean full) {
Timber.d("onRefreshed(%b)", full);
if (full) {
List<TransactionInfo> list = wallet.getHistory().getAll();
adapter.setInfos(list);
adapter.notifyDataSetChanged();
}
updateStatus(wallet);
}
public void onSynced() {
if (!activityCallback.isWatchOnly()) {
bSend.setVisibility(View.VISIBLE);
bSend.setEnabled(true);
}
if (isVisible()) enableAccountsList(true); //otherwise it is enabled in onResume()
}
boolean walletLoaded = false;
public void onLoaded() {
walletLoaded = true;
showReceive();
}
private void showReceive() {
if (walletLoaded) {
bReceive.setVisibility(View.VISIBLE);
bReceive.setEnabled(true);
}
}
private String syncText = null;
public void setProgress(final String text) {
syncText = text;
tvProgress.setText(text);
}
private int syncProgress = -1;
public void setProgress(final int n) {
syncProgress = n;
if (n > 100) {
pbProgress.setIndeterminate(true);
pbProgress.setVisibility(View.VISIBLE);
} else if (n >= 0) {
pbProgress.setIndeterminate(false);
pbProgress.setProgress(n);
pbProgress.setVisibility(View.VISIBLE);
} else { // <0
pbProgress.setVisibility(View.INVISIBLE);
}
}
void setActivityTitle(Wallet wallet) {
if (wallet == null) return;
walletTitle = wallet.getName();
String watchOnly = (wallet.isWatchOnly() ? getString(R.string.label_watchonly) : "");
walletSubtitle = wallet.getAccountLabel();
activityCallback.setTitle(walletTitle, walletSubtitle);
Timber.d("wallet title is %s", walletTitle);
}
private long firstBlock = 0;
private String walletTitle = null;
private String walletSubtitle = null;
private long unlockedBalance = 0;
private int accountIdx = -1;
private void updateStatus(Wallet wallet) {
if (!isAdded()) return;
Timber.d("updateStatus()");
if ((walletTitle == null) || (accountIdx != wallet.getAccountIndex())) {
accountIdx = wallet.getAccountIndex();
setActivityTitle(wallet);
}
long balance = wallet.getBalance();
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.wow_unconfirmed_amount, unconfirmed));
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));
if (x == 0) x = 101; // indeterminate
setProgress(x);
ivSynced.setVisibility(View.GONE);
} else {
sync = getString(R.string.status_synced) + " " + formatter.format(wallet.getBlockChainHeight());
ivSynced.setVisibility(View.VISIBLE);
}
} else {
sync = getString(R.string.status_wallet_connecting);
setProgress(101);
}
setProgress(sync);
// TODO show connected status somewhere
}
Listener activityCallback;
// Container Activity must implement this interface
public interface Listener {
boolean hasBoundService();
void forceUpdate();
Wallet.ConnectionStatus getConnectionStatus();
long getDaemonHeight(); //mBoundService.getDaemonHeight();
void onSendRequest();
void onTxDetailsRequest(TransactionInfo info);
boolean isSynced();
boolean isWatchOnly();
String getTxKey(String txId);
void onWalletReceive();
boolean hasWallet();
void setToolbarButton(int type);
void setTitle(String title, String subtitle);
void setSubtitle(String subtitle);
}
@Override
public void onAttach(Context context) {
super.onAttach(context);
if (context instanceof Listener) {
this.activityCallback = (Listener) context;
} else {
throw new ClassCastException(context.toString()
+ " must implement Listener");
}
}
@Override
public void onResume() {
super.onResume();
Timber.d("onResume()");
activityCallback.setTitle(walletTitle, walletSubtitle);
//activityCallback.setToolbarButton(Toolbar.BUTTON_CLOSE); // TODO: Close button somewhere else
activityCallback.setToolbarButton(Toolbar.BUTTON_NONE);
setProgress(syncProgress);
setProgress(syncText);
showReceive();
if (activityCallback.isSynced()) enableAccountsList(true);
}
@Override
public void onPause() {
enableAccountsList(false);
super.onPause();
}
public interface DrawerLocker {
void setDrawerEnabled(boolean enabled);
}
private void enableAccountsList(boolean enable) {
if (activityCallback instanceof DrawerLocker) {
((DrawerLocker) activityCallback).setDrawerEnabled(enable);
}
}
}