// Copyright (C) 2013-2014 Bonsai Software, Inc. // // This program is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation, either version 3 of the License, or // (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License // along with this program. If not, see <http://www.gnu.org/licenses/>. package com.bonsai.wallet32; import java.util.ArrayList; import java.util.List; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import android.content.Intent; import android.os.Bundle; import android.text.Editable; import android.text.TextWatcher; import android.view.LayoutInflater; import android.view.View; import android.widget.CompoundButton; import android.widget.CompoundButton.OnCheckedChangeListener; import android.widget.EditText; import android.widget.RadioButton; import android.widget.TableLayout; import android.widget.TableRow; import android.widget.TextView; import com.google.bitcoin.core.Address; public class ReceiveBitcoinActivity extends BaseWalletActivity { private static Logger mLogger = LoggerFactory.getLogger(ReceiveBitcoinActivity.class); protected EditText mBTCAmountEditText; protected EditText mFiatAmountEditText; protected boolean mUserSetAmountFiat; @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_receive_bitcoin); // Start off presuming the user set the BTC amount. mUserSetAmountFiat = false; mBTCAmountEditText = (EditText) findViewById(R.id.amount_btc); mFiatAmountEditText = (EditText) findViewById(R.id.amount_fiat); mBTCAmountEditText.addTextChangedListener(mBTCAmountWatcher); mFiatAmountEditText.addTextChangedListener(mFiatAmountWatcher); mLogger.info("ReceiveBitcoinActivity created"); } @Override protected void onResume() { super.onResume(); // Set these each time we resume in case we've visited the // Settings and they've changed. { TextView tv = (TextView) findViewById(R.id.amount_btc_label); tv.setText(mBTCFmt.unitStr()); } mLogger.info("ReceiveBitcoinActivity resumed"); } @Override protected void onWalletStateChanged() { updateAccounts(); } @Override protected void onRateChanged() { updateAccounts(); updateAmountFields(); } // NOTE - This code implements a pair of "cross updating" fields. // If the user changes the BTC amount the fiat field is constantly // updated at the current mFiatPerBTC rate. If the user changes // the fiat field the BTC field is constantly updated at the // current rate. private final TextWatcher mBTCAmountWatcher = new TextWatcher() { @Override public void beforeTextChanged(CharSequence ss, int start, int count, int after) { // Note that the user changed the BTC last. mUserSetAmountFiat = false; } @Override public void onTextChanged(CharSequence ss, int start, int before, int count) {} @Override public void afterTextChanged(Editable ss) { updateAmountFields(); } }; private final TextWatcher mFiatAmountWatcher = new TextWatcher() { @Override public void beforeTextChanged(CharSequence ss, int start, int count, int after) { mUserSetAmountFiat = true; } @Override public void onTextChanged(CharSequence ss, int start, int before, int count) {} @Override public void afterTextChanged(Editable ss) { updateAmountFields(); } }; protected void updateAmountFields() { // Which field did the user last edit? if (mUserSetAmountFiat) { // The user set the Fiat amount. String ss = mFiatAmountEditText.getText().toString(); // Avoid recursion by removing the other fields listener. mBTCAmountEditText.removeTextChangedListener (mBTCAmountWatcher); String bbs; try { double ff = parseNumberWorkaround(ss.toString()); long bb; if (mFiatPerBTC == 0.0) { bbs = ""; } else { bb = mBTCFmt.btcAtRate(ff, mFiatPerBTC); bbs = mBTCFmt.format(bb); } } catch (final NumberFormatException ex) { bbs = ""; } mBTCAmountEditText.setText(bbs, TextView.BufferType.EDITABLE); // Restore the other fields listener. mBTCAmountEditText.addTextChangedListener(mBTCAmountWatcher); } else { // The user set the BTC amount. String ss = mBTCAmountEditText.getText().toString(); // Avoid recursion by removing the other fields listener. mFiatAmountEditText.removeTextChangedListener (mFiatAmountWatcher); String ffs; try { long bb = mBTCFmt.parse(ss.toString()); double ff = mBTCFmt.fiatAtRate(bb, mFiatPerBTC); ffs = String.format("%.2f", ff); } catch (final NumberFormatException ex) { ffs = ""; } mFiatAmountEditText.setText(ffs, TextView.BufferType.EDITABLE); // Restore the other fields listener. mFiatAmountEditText.addTextChangedListener(mFiatAmountWatcher); } } private List<Integer> mAccountIds; private int mCheckedToId = -1; private OnCheckedChangeListener mReceiveToListener = new OnCheckedChangeListener() { @Override public void onCheckedChanged(CompoundButton cb, boolean isChecked) { if (cb.isChecked()) { TableLayout table = (TableLayout) findViewById(R.id.to_choices); mCheckedToId = cb.getId(); for (Integer acctid : mAccountIds) { int rbid = acctid.intValue(); if (rbid != mCheckedToId) { RadioButton rb = (RadioButton) table.findViewById(rbid); rb.setChecked(false); } } } } }; private void addAccountHeader(TableLayout table) { TableRow row = (TableRow) LayoutInflater.from(this) .inflate(R.layout.receive_to_header, table, false); TextView tv = (TextView) row.findViewById(R.id.header_btc); tv.setText(mBTCFmt.unitStr()); table.addView(row); } private void addAccountRow(TableLayout table, int acctId, String acctName, long btc, double fiat) { TableRow row = (TableRow) LayoutInflater.from(this) .inflate(R.layout.receive_to_row, table, false); RadioButton tv0 = (RadioButton) row.findViewById(R.id.to_account); tv0.setId(acctId); // Change id to the acctId. tv0.setText(acctName); tv0.setOnCheckedChangeListener(mReceiveToListener); if (acctId == mCheckedToId) tv0.setChecked(true); TextView tv1 = (TextView) row.findViewById(R.id.row_btc); tv1.setText(String.format("%s", mBTCFmt.formatCol(btc, 0, true, true))); TextView tv2 = (TextView) row.findViewById(R.id.row_fiat); tv2.setText(String.format("%.02f", fiat)); table.addView(row); } private void updateAccounts() { if (mWalletService == null) return; TableLayout table = (TableLayout) findViewById(R.id.to_choices); // Clear any existing table content. table.removeAllViews(); addAccountHeader(table); mAccountIds = new ArrayList<Integer>(); // double sumbtc = 0.0; List<Balance> balances = mWalletService.getBalances(); if (balances != null) { for (Balance bal : balances) { // sumbtc += bal.balance; addAccountRow(table, bal.accountId, bal.accountName, bal.balance, mBTCFmt.fiatAtRate(bal.balance, mFiatPerBTC)); mAccountIds.add(bal.accountId); } } } public void receiveBitcoin(View view) { if (mWalletService == null) { showErrorDialog(mRes.getString(R.string.receive_error_nowallet)); return; } // Which account was selected? if (mCheckedToId == -1) { showErrorDialog(mRes.getString(R.string.receive_error_noaccount)); return; } mLogger.info(String.format("receiving to account %d", mCheckedToId)); Address addr = mWalletService.nextReceiveAddress(mCheckedToId); String addrstr = addr.toString(); mLogger.info(String.format("picked %s to receive", addrstr)); // Was the amount specified? EditText amountEditText = (EditText) findViewById(R.id.amount_btc); String amountString = amountEditText.getText().toString(); long amount = 0; if (amountString.length() != 0) { try { amount = mBTCFmt.parse(amountString); } catch (NumberFormatException ex) { showErrorDialog(mRes .getString(R.string.receive_error_badamount)); return; } } // Dispatch to the address viewer. Intent intent = new Intent(this, ViewAddressActivity.class); intent.putExtra("address", addrstr); intent.putExtra("amount", amount); startActivity(intent); } private static double parseNumberWorkaround(String numstr) throws NumberFormatException { // Some countries use comma as the decimal separator. // Android's numberDecimal EditText fields don't handle this // correctly (https://code.google.com/p/android/issues/detail?id=2626). // As a workaround we substitute ',' -> '.' manually ... return Double.parseDouble(numstr.toString().replace(',', '.')); } } // Local Variables: // mode: java // c-basic-offset: 4 // tab-width: 4 // End: