// 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.text.SimpleDateFormat; import java.util.ArrayList; import java.util.Collections; import java.util.Comparator; import java.util.Date; import java.util.List; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import android.content.Intent; import android.graphics.Color; import android.os.Bundle; import android.view.LayoutInflater; import android.view.View; import android.widget.AdapterView; import android.widget.AdapterView.OnItemSelectedListener; import android.widget.ArrayAdapter; import android.widget.Spinner; import android.widget.TableLayout; import android.widget.TableRow; import android.widget.TextView; import com.google.bitcoin.core.Sha256Hash; import com.google.bitcoin.core.Transaction; import com.google.bitcoin.core.TransactionConfidence; import com.google.bitcoin.core.TransactionConfidence.ConfidenceType; import com.google.bitcoin.wallet.WalletTransaction; public class ViewTransactionsActivity extends BaseWalletActivity { private static Logger mLogger = LoggerFactory.getLogger(ViewTransactionsActivity.class); private int mAccountNum = -1; // -1 means all accounts @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_view_transactions); // Was an account specified? Intent intent = getIntent(); Bundle bundle = intent.getExtras(); if (bundle != null && bundle.containsKey("accountId")) mAccountNum = intent.getExtras().getInt("accountId"); Spinner spinner = (Spinner) findViewById(R.id.account_spinner); spinner.setOnItemSelectedListener(new OnItemSelectedListener() { @Override public void onItemSelected(AdapterView<?> arg0, View arg1, int arg2, long arg3) { int index = arg0.getSelectedItemPosition(); mAccountNum = index - 1; updateTransactions(); } @Override public void onNothingSelected(AdapterView<?> arg0) { // TODO Auto-generated method stub } }); mLogger.info("ViewTransactionsActivity created"); } @Override protected void onWalletStateChanged() { updateAccountSpinner(); updateTransactions(); } @Override protected void onRateChanged() { updateTransactions(); } private void updateAccountSpinner() { if (mWalletService != null) { mLogger.info("updating account spinner"); List<String> list = new ArrayList<String>(); list.add("All Accounts"); List<HDAccount> accts = mWalletService.getAccounts(); for (HDAccount acct : accts) list.add(acct.getName()); Spinner spinner = (Spinner) findViewById(R.id.account_spinner); ArrayAdapter<String> dataAdapter = new ArrayAdapter<String>(this, android.R.layout.simple_spinner_item, list); dataAdapter.setDropDownViewResource (android.R.layout.simple_spinner_dropdown_item); spinner.setAdapter(dataAdapter); spinner.setSelection(mAccountNum + 1); } } private void addTransactionHeader(TableLayout table) { TableRow row = (TableRow) LayoutInflater.from(this) .inflate(R.layout.transaction_table_header, table, false); TextView tv = (TextView) row.findViewById(R.id.header_btc); tv.setText(mBTCFmt.unitStr()); table.addView(row); } private void addTransactionRow(String hash, TableLayout table, String datestr, String timestr, String confstr, String btcstr, String btcbalstr, String fiatstr, String fiatbalstr, boolean tintrow) { TableRow row = (TableRow) LayoutInflater.from(this) .inflate(R.layout.transaction_table_row, table, false); row.setTag(hash); { TextView tv = (TextView) row.findViewById(R.id.row_date); tv.setText(datestr); } { TextView tv = (TextView) row.findViewById(R.id.row_time); tv.setText(timestr); } { TextView tv = (TextView) row.findViewById(R.id.row_confidence); tv.setText(confstr); } { TextView tv = (TextView) row.findViewById(R.id.row_btc_balance); tv.setText(btcbalstr); } { TextView tv = (TextView) row.findViewById(R.id.row_btc); tv.setText(btcstr); } { TextView tv = (TextView) row.findViewById(R.id.row_fiat_balance); tv.setText(fiatbalstr); } { TextView tv = (TextView) row.findViewById(R.id.row_fiat); tv.setText(fiatstr); } if (tintrow) row.setBackgroundColor(Color.parseColor("#ccffcc")); table.addView(row); } private void updateTransactions() { if (mWalletService == null) return; TableLayout table = (TableLayout) findViewById(R.id.transaction_table); // Clear any existing table content. table.removeAllViews(); addTransactionHeader(table); SimpleDateFormat dateFormater = new SimpleDateFormat("yyyy-MM-dd"); SimpleDateFormat timeFormater = new SimpleDateFormat("kk:mm:ss"); // Read all the transactions and sort by date. Iterable<WalletTransaction> txit = mWalletService.getTransactions(); ArrayList<WalletTransaction> txs = new ArrayList<WalletTransaction>(); for (WalletTransaction wtx : txit) txs.add(wtx); // Sort in reverse time order (most recent first). Collections.sort(txs, new Comparator<WalletTransaction>() { public int compare(WalletTransaction wt0, WalletTransaction wt1) { Date dt0 = wt0.getTransaction().getUpdateTime(); Date dt1 = wt1.getTransaction().getUpdateTime(); int cmp = -dt0.compareTo(dt1); if (cmp == 0) { // These two transactions happened in the same // block (same time) so we should compare // something else to keep the sorting order // stable. Sha256Hash h0 = wt0.getTransaction().getHash(); Sha256Hash h1 = wt1.getTransaction().getHash(); return -h0.compareTo(h1); } return cmp; } }); long btcbal = mWalletService.balanceForAccount(mAccountNum); int rowcounter = 0; for (WalletTransaction wtx : txs) { Transaction tx = wtx.getTransaction(); TransactionConfidence conf = tx.getConfidence(); ConfidenceType ct = conf.getConfidenceType(); long btc = mWalletService.amountForAccount(wtx, mAccountNum); if (btc != 0) { double fiat = mBTCFmt.fiatAtRate(btc, mFiatPerBTC); double fiatbal = mBTCFmt.fiatAtRate(btcbal, mFiatPerBTC); String hash = tx.getHashAsString(); String datestr = dateFormater.format(tx.getUpdateTime()); String timestr = timeFormater.format(tx.getUpdateTime()); String btcstr = mBTCFmt.formatCol(btc, 0, true, true); String btcbalstr = mBTCFmt.formatCol(btcbal, 0, true, true); String fiatstr = String.format("%.02f", fiat); String fiatbalstr = String.format("%.02f", fiatbal); String confstr; switch (ct) { case UNKNOWN: confstr = "U"; break; case BUILDING: int depth = conf.getDepthInBlocks(); confstr = depth > 100 ? "100+" : String.format("%d", depth); break; case PENDING: confstr = "P"; break; case DEAD: confstr = "D"; break; default: confstr = "?"; break; } // This is just too noisy ... // mLogger.info("tx " + hash); boolean tintrow = rowcounter % 2 == 0; ++rowcounter; addTransactionRow(hash, table, datestr, timestr, confstr, btcstr, btcbalstr, fiatstr, fiatbalstr, tintrow); } // We're working backward in time ... // Dead transactions should not affect the balance ... if (ct != ConfidenceType.DEAD) btcbal -= btc; } } public void handleRowClick(View view) { // Dispatch to the transaction viewer. String hash = (String) view.getTag(); Intent intent = new Intent(this, ViewTransactionActivity.class); intent.putExtra("hash", hash); startActivity(intent); } } // Local Variables: // mode: java // c-basic-offset: 4 // tab-width: 4 // End: