Back to project page My-Wallet-Android.
The source code is released under:
GNU General Public License
If you think the Android project My-Wallet-Android listed in this page is inappropriate, such as containing malicious code/tools or violating the copyright, please email info at java2s dot com, thanks.
/* * Copyright 2011-2012 the original author or authors. *// w w w . j av a 2 s . c o m * 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 piuk.blockchain.android.ui; import java.math.BigInteger; import java.text.DateFormat; 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 android.app.Activity; import android.content.BroadcastReceiver; import android.content.Context; import android.content.Intent; import android.content.IntentFilter; import android.database.ContentObserver; import android.graphics.Color; import android.graphics.Typeface; import android.os.Bundle; import android.os.Handler; import android.support.v4.app.Fragment; import android.support.v4.app.FragmentManager; import android.support.v4.app.FragmentStatePagerAdapter; import android.support.v4.app.LoaderManager.LoaderCallbacks; import android.support.v4.content.AsyncTaskLoader; import android.support.v4.content.Loader; import android.support.v4.view.ViewPager; import android.text.format.DateUtils; import android.view.ContextMenu; import android.view.ContextMenu.ContextMenuInfo; import android.view.LayoutInflater; import android.view.MenuItem; import android.view.View; import android.view.ViewGroup; import android.widget.AdapterView.AdapterContextMenuInfo; import android.widget.ArrayAdapter; import android.widget.ListAdapter; import android.widget.ListView; import android.widget.TextView; import com.google.bitcoin.core.AbstractWalletEventListener; import com.google.bitcoin.core.Address; import com.google.bitcoin.core.ScriptException; import com.google.bitcoin.core.Transaction; import com.google.bitcoin.core.TransactionConfidence; import com.google.bitcoin.core.TransactionConfidence.ConfidenceType; import com.google.bitcoin.core.Wallet; import com.google.bitcoin.core.WalletEventListener; import piuk.blockchain.R; import piuk.blockchain.android.AddressBookProvider; import piuk.blockchain.android.BlockchainService; import piuk.blockchain.android.WalletApplication; import piuk.blockchain.android.util.CircularProgressView; import piuk.blockchain.android.util.ViewPagerTabs; /** * @author Andreas Schildbach */ public final class WalletTransactionsFragment extends Fragment { @Override public View onCreateView(final LayoutInflater inflater, final ViewGroup container, final Bundle savedInstanceState) { final View view = inflater.inflate(R.layout.wallet_transactions_fragment, container, false); final ViewPagerTabs pagerTabs = (ViewPagerTabs) view.findViewById(R.id.transactions_pager_tabs); pagerTabs.addTabLabels(R.string.wallet_transactions_fragment_tab_received, R.string.wallet_transactions_fragment_tab_all, R.string.wallet_transactions_fragment_tab_sent); final PagerAdapter pagerAdapter = new PagerAdapter(getFragmentManager()); final ViewPager pager = (ViewPager) view.findViewById(R.id.transactions_pager); pager.setAdapter(pagerAdapter); pager.setOnPageChangeListener(pagerTabs); pager.setCurrentItem(1); pager.setPageMargin(2); pager.setPageMarginDrawable(R.color.background_less_bright); pagerTabs.onPageScrolled(1, 0, 0); // should not be needed return view; } private static class PagerAdapter extends FragmentStatePagerAdapter { public PagerAdapter(final FragmentManager fm) { super(fm); } @Override public int getCount() { return 3; } @Override public Fragment getItem(final int position) { return ListFragment.instance(position); } } private static class TransactionsLoader extends AsyncTaskLoader<List<Transaction>> { private final WalletApplication application; private TransactionsLoader(final Context context, final WalletApplication application) { super(context); this.application = application; } @Override protected void onStartLoading() { super.onStartLoading(); application.getWallet().addEventListener(walletEventListener); forceLoad(); } @Override protected void onStopLoading() { application.getWallet().removeEventListener(walletEventListener); super.onStopLoading(); } @Override public List<Transaction> loadInBackground() { final List<Transaction> transactions = new ArrayList<Transaction>(application.getWallet().getTransactions(true, false)); Collections.sort(transactions, TRANSACTION_COMPARATOR); return transactions; } private final WalletEventListener walletEventListener = new AbstractWalletEventListener() { @Override public void onChange(Wallet wallet) { try { forceLoad(); } catch (Exception e) { e.printStackTrace(); } } }; private static final Comparator<Transaction> TRANSACTION_COMPARATOR = new Comparator<Transaction>() { public int compare(final Transaction tx1, final Transaction tx2) { final boolean pending1 = tx1.getConfidence().getConfidenceType() == ConfidenceType.NOT_SEEN_IN_CHAIN; final boolean pending2 = tx2.getConfidence().getConfidenceType() == ConfidenceType.NOT_SEEN_IN_CHAIN; if (pending1 != pending2) return pending1 ? -1 : 1; final long time1 = tx1.getUpdateTime() != null ? tx1.getUpdateTime().getTime() : 0; final long time2 = tx2.getUpdateTime() != null ? tx2.getUpdateTime().getTime() : 0; if (time1 != time2) return time1 > time2 ? -1 : 1; return 0; } }; } public static class ListFragment extends android.support.v4.app.ListFragment implements LoaderCallbacks<List<Transaction>> { private WalletApplication application; private Activity activity; private ArrayAdapter<Transaction> adapter; private int mode; private int bestChainHeight; private final Handler handler = new Handler(); private final static String KEY_MODE = "mode"; public static ListFragment instance(final int mode) { final ListFragment fragment = new ListFragment(); final Bundle args = new Bundle(); args.putInt(KEY_MODE, mode); fragment.setArguments(args); return fragment; } private final ContentObserver contentObserver = new ContentObserver(handler) { @Override public void onChange(final boolean selfChange) { try { adapter.notifyDataSetChanged(); } catch (Exception e) { e.printStackTrace(); } } }; private final BroadcastReceiver broadcastReceiver = new BroadcastReceiver() { @Override public void onReceive(final Context context, final Intent intent) { bestChainHeight = intent.getIntExtra(BlockchainService.ACTION_BLOCKCHAIN_STATE_BEST_CHAIN_HEIGHT, 0); adapter.notifyDataSetChanged(); } }; @Override public void onAttach(final Activity activity) { super.onAttach(activity); this.activity = activity; application = (WalletApplication) activity.getApplication(); } @Override public void onCreate(final Bundle savedInstanceState) { super.onCreate(savedInstanceState); this.mode = getArguments().getInt(KEY_MODE); adapter = new ArrayAdapter<Transaction>(activity, 0){ final DateFormat dateFormat = android.text.format.DateFormat.getDateFormat(activity); final DateFormat timeFormat = android.text.format.DateFormat.getTimeFormat(activity); final int colorSignificant = getResources().getColor(R.color.significant); final int colorInsignificant = getResources().getColor(R.color.insignificant); final int colorSent = getResources().getColor(R.color.color_sent); final int colorReceived = getResources().getColor(R.color.color_received); @Override public View getView(final int position, View row, final ViewGroup parent) { if (row == null) row = getLayoutInflater(null).inflate(R.layout.transaction_row, null); final Transaction tx = getItem(position); final TransactionConfidence confidence = tx.getConfidence(); final ConfidenceType confidenceType = confidence.getConfidenceType(); try { final BigInteger value = tx.getValue(application.getWallet()); final boolean sent = value.signum() < 0; final int textColor; if (confidenceType == ConfidenceType.NOT_SEEN_IN_CHAIN) { textColor = colorInsignificant; } else if (confidenceType == ConfidenceType.BUILDING) { textColor = colorSignificant; } else if (confidenceType == ConfidenceType.NOT_IN_BEST_CHAIN) { textColor = colorSignificant; } else if (confidenceType == ConfidenceType.OVERRIDDEN_BY_DOUBLE_SPEND) { textColor = Color.RED; } else { textColor = colorInsignificant; } final String address; if (sent) if (tx.getOutputs().size() == 0) address = "Unknown"; else address = tx.getOutputs().get(0).getScriptPubKey().getToAddress().toString(); else if (tx.getInputs().size() == 0) address = "Generation"; else address = tx.getInputs().get(0).getFromAddress().toString(); final String label = AddressBookProvider.resolveLabel(activity.getContentResolver(), address); final TextView rowTime = (TextView) row.findViewById(R.id.transaction_row_time); final Date time = tx.getUpdateTime(); rowTime.setText(time != null ? (DateUtils.isToday(time.getTime()) ? timeFormat.format(time) : dateFormat.format(time)) : null); rowTime.setTextColor(textColor); final TextView rowLabel = (TextView) row.findViewById(R.id.transaction_row_address); rowLabel.setTextColor(textColor); rowLabel.setText(label != null ? label : address); rowLabel.setTypeface(label != null ? Typeface.DEFAULT : Typeface.MONOSPACE); final CurrencyAmountView rowValue = (CurrencyAmountView) row.findViewById(R.id.transaction_row_value); rowValue.setCurrencyCode(null); rowValue.setAmountSigned(true); rowValue.setTextColor(textColor); rowValue.setAmount(value); if (sent) { rowValue.setTextColor(colorSent); } else { rowValue.setTextColor(colorReceived); } return row; } catch (final ScriptException x) { throw new RuntimeException(x); } } }; setListAdapter(adapter); activity.getContentResolver().registerContentObserver(AddressBookProvider.CONTENT_URI, true, contentObserver); } @Override public void onActivityCreated(final Bundle savedInstanceState) { super.onActivityCreated(savedInstanceState); getLoaderManager().initLoader(0, null, this); } @Override public void onViewCreated(final View view, final Bundle savedInstanceState) { super.onViewCreated(view, savedInstanceState); setEmptyText(getString(mode == 2 ? R.string.wallet_transactions_fragment_empty_text_sent : R.string.wallet_transactions_fragment_empty_text_received)); registerForContextMenu(getListView()); } @Override public void onResume() { super.onResume(); activity.registerReceiver(broadcastReceiver, new IntentFilter(BlockchainService.ACTION_BLOCKCHAIN_STATE)); } @Override public void onPause() { activity.unregisterReceiver(broadcastReceiver); super.onPause(); } @Override public void onDestroy() { activity.getContentResolver().unregisterContentObserver(contentObserver); getLoaderManager().destroyLoader(0); super.onDestroy(); } @Override public void onListItemClick(final ListView l, final View v, final int position, final long id) { final Transaction tx = adapter.getItem(position); editAddress(tx); } // workaround http://code.google.com/p/android/issues/detail?id=20065 private static View lastContextMenuView; @Override public void onCreateContextMenu(final ContextMenu menu, final View v, final ContextMenuInfo menuInfo) { activity.getMenuInflater().inflate(R.menu.wallet_transactions_context, menu); lastContextMenuView = v; } @Override public boolean onContextItemSelected(final MenuItem item) { final AdapterContextMenuInfo menuInfo = (AdapterContextMenuInfo) item.getMenuInfo(); final ListAdapter adapter = ((ListView) lastContextMenuView).getAdapter(); final Transaction tx = (Transaction) adapter.getItem(menuInfo.position); switch (item.getItemId()) { case R.id.wallet_transactions_context_edit_address: editAddress(tx); return true; /*case R.id.wallet_transactions_context_show_transaction: TransactionActivity.show(activity, tx); return true; */ default: return false; } } private void editAddress(final Transaction tx) { try { final boolean sent = tx.getValue(application.getWallet()).signum() < 0; Address address = null; if (sent) { if (tx.getOutputs().size() == 0) return; address = tx.getOutputs().get(0).getScriptPubKey().getToAddress(); } else { if (tx.getInputs().size() == 0) return; address = tx.getInputs().get(0).getFromAddress(); } EditAddressBookEntryFragment.edit(getFragmentManager(), address.toString()); } catch (final ScriptException x) { // ignore click x.printStackTrace(); } } public Loader<List<Transaction>> onCreateLoader(final int id, final Bundle args) { return new TransactionsLoader(activity, application); } public void onLoadFinished(final Loader<List<Transaction>> loader, final List<Transaction> transactions) { adapter.clear(); try { for (final Transaction tx : transactions) { final boolean sent = tx.getValue(application.getWallet()).signum() < 0; if ((mode == 0 && !sent) || mode == 1 || (mode == 2 && sent)) adapter.add(tx); } } catch (final ScriptException x) { throw new RuntimeException(x); } } public void onLoaderReset(final Loader<List<Transaction>> loader) { adapter.clear(); } } }