com.brq.wallet.activity.modern.AccountsFragment.java Source code

Java tutorial

Introduction

Here is the source code for com.brq.wallet.activity.modern.AccountsFragment.java

Source

/*
 * Copyright 2013, 2014 Megion Research and Development GmbH
 *
 * Licensed under the Microsoft Reference Source License (MS-RSL)
 *
 * This license governs use of the accompanying software. If you use the software, you accept this license.
 * If you do not accept the license, do not use the software.
 *
 * 1. Definitions
 * The terms "reproduce," "reproduction," and "distribution" have the same meaning here as under U.S. copyright law.
 * "You" means the licensee of the software.
 * "Your company" means the company you worked for when you downloaded the software.
 * "Reference use" means use of the software within your company as a reference, in read only form, for the sole purposes
 * of debugging your products, maintaining your products, or enhancing the interoperability of your products with the
 * software, and specifically excludes the right to distribute the software outside of your company.
 * "Licensed patents" means any Licensor patent claims which read directly on the software as distributed by the Licensor
 * under this license.
 *
 * 2. Grant of Rights
 * (A) Copyright Grant- Subject to the terms of this license, the Licensor grants you a non-transferable, non-exclusive,
 * worldwide, royalty-free copyright license to reproduce the software for reference use.
 * (B) Patent Grant- Subject to the terms of this license, the Licensor grants you a non-transferable, non-exclusive,
 * worldwide, royalty-free patent license under licensed patents for reference use.
 *
 * 3. Limitations
 * (A) No Trademark License- This license does not grant you any rights to use the Licensors name, logo, or trademarks.
 * (B) If you begin patent litigation against the Licensor over patents that you think may apply to the software
 * (including a cross-claim or counterclaim in a lawsuit), your license to the software ends automatically.
 * (C) The software is licensed "as-is." You bear the risk of using it. The Licensor gives no express warranties,
 * guarantees or conditions. You may have additional consumer rights under your local laws which this license cannot
 * change. To the extent permitted under your local laws, the Licensor excludes the implied warranties of merchantability,
 * fitness for a particular purpose and non-infringement.
 */

package com.brq.wallet.activity.modern;

import android.app.Activity;
import android.app.AlertDialog;
import android.app.ProgressDialog;
import android.content.DialogInterface;
import android.content.Intent;
import android.os.AsyncTask;
import android.os.Bundle;
import android.support.v4.app.ActivityCompat;
import android.support.v4.app.Fragment;
import android.support.v7.app.ActionBarActivity;
import android.support.v7.view.ActionMode;
import android.support.v7.view.ActionMode.Callback;
import android.text.InputType;
import android.util.TypedValue;
import android.view.*;
import android.view.View.OnClickListener;
import android.widget.CheckBox;
import android.widget.EditText;
import android.widget.LinearLayout;
import android.widget.LinearLayout.LayoutParams;
import android.widget.TextView;

import com.brq.wallet.Utils;
import com.brq.wallet.activity.AddAccountActivity;
import com.brq.wallet.activity.AddCoinapultAccountActivity;
import com.brq.wallet.activity.util.EnterAddressLabelUtil;
import com.brq.wallet.event.AccountChanged;
import com.brq.wallet.event.BalanceChanged;
import com.brq.wallet.event.ExtraAccountsChanged;
import com.brq.wallet.event.ReceivingAddressChanged;
import com.brq.wallet.event.SyncStarted;
import com.brq.wallet.event.SyncStopped;
import com.google.common.base.Optional;
import com.google.common.base.Preconditions;
import com.google.common.base.Strings;
import com.google.common.collect.Lists;
import com.mrd.bitlib.model.Address;
import com.brq.wallet.MbwManager;
import com.brq.wallet.R;
import com.brq.wallet.activity.MessageSigningActivity;
import com.brq.wallet.activity.export.VerifyBackupActivity;
import com.brq.wallet.activity.util.ToggleableCurrencyDisplay;
import com.brq.wallet.coinapult.CoinapultAccount;
import com.brq.wallet.coinapult.CoinapultManager;
import com.brq.wallet.persistence.MetadataStorage;
import com.mycelium.wapi.model.Balance;
import com.mycelium.wapi.wallet.*;
import com.mycelium.wapi.wallet.bip44.Bip44Account;
import com.mycelium.wapi.wallet.bip44.Bip44PubOnlyAccount;
import com.mycelium.wapi.wallet.currency.CurrencySum;
import com.mycelium.wapi.wallet.single.SingleAddressAccount;
import com.squareup.otto.Subscribe;

import java.util.List;
import java.util.UUID;

public class AccountsFragment extends Fragment {

    public static final int ADD_RECORD_RESULT_CODE = 0;

    private WalletManager walletManager;

    private MetadataStorage _storage;
    private MbwManager _mbwManager;
    private LayoutInflater _layoutInflater;
    private int _separatorColor;
    private LayoutParams _separatorLayoutParameters;
    private LayoutParams _titleLayoutParameters;
    private LayoutParams _outerLayoutParameters;
    private LayoutParams _innerLayoutParameters;
    private WalletAccount _focusedAccount;
    private Toaster _toaster;
    private ProgressDialog _progress;

    /**
     * Called when the activity is first created.
     */
    @SuppressWarnings("deprecation")
    @Override
    public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
        View ret = inflater.inflate(R.layout.records_activity, container, false);
        _layoutInflater = inflater;

        _separatorColor = getResources().getColor(R.color.darkgrey);
        _separatorLayoutParameters = new LayoutParams(LayoutParams.FILL_PARENT, getDipValue(1), 1);
        _titleLayoutParameters = new LayoutParams(LayoutParams.FILL_PARENT, LayoutParams.WRAP_CONTENT, 1);
        _outerLayoutParameters = new LayoutParams(LayoutParams.FILL_PARENT, LayoutParams.WRAP_CONTENT, 1);
        _outerLayoutParameters.bottomMargin = getDipValue(8);
        _innerLayoutParameters = new LayoutParams(LayoutParams.FILL_PARENT, LayoutParams.WRAP_CONTENT, 1);
        return ret;
    }

    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setHasOptionsMenu(true);
    }

    @Override
    public void onAttach(Activity activity) {
        _mbwManager = MbwManager.getInstance(activity);
        walletManager = _mbwManager.getWalletManager(false);
        _storage = _mbwManager.getMetadataStorage();
        _toaster = new Toaster(this);
        super.onAttach(activity);
    }

    @Override
    public void onResume() {
        _mbwManager.getEventBus().register(this);
        getView().findViewById(R.id.btUnlock).setOnClickListener(unlockClickedListener);
        update();
        _progress = new ProgressDialog(getActivity());
        super.onResume();
    }

    @Override
    public void onPause() {
        _progress.dismiss();
        _mbwManager.getEventBus().unregister(this);
        super.onPause();
    }

    private int getDipValue(int dip) {
        return (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, dip,
                getResources().getDisplayMetrics());
    }

    @Override
    public void setUserVisibleHint(boolean isVisibleToUser) {
        super.setUserVisibleHint(isVisibleToUser);
        if (!isVisibleToUser) {
            finishCurrentActionMode();
        }
    }

    @Override
    public void onActivityResult(final int requestCode, final int resultCode, final Intent intent) {
        ActivityCompat.invalidateOptionsMenu(getActivity());
        if (requestCode == ADD_RECORD_RESULT_CODE && resultCode == AddCoinapultAccountActivity.RESULT_COINAPULT) {
            UUID accountId = (UUID) intent.getSerializableExtra(AddAccountActivity.RESULT_KEY);
            CoinapultAccount account = (CoinapultAccount) _mbwManager.getWalletManager(false).getAccount(accountId);
            _mbwManager.setSelectedAccount(accountId);
            _focusedAccount = account;
            update();
            return;
        }

        if (requestCode == ADD_RECORD_RESULT_CODE && resultCode == Activity.RESULT_OK) {
            UUID accountid = (UUID) intent.getSerializableExtra(AddAccountActivity.RESULT_KEY);
            //check whether the account is active - we might have scanned the priv key for an archived watchonly
            WalletAccount account = _mbwManager.getWalletManager(false).getAccount(accountid);
            if (account.isActive()) {
                _mbwManager.setSelectedAccount(accountid);
            }
            _focusedAccount = account;
            update();
            setNameForNewAccount(_focusedAccount);
        } else {
            super.onActivityResult(requestCode, resultCode, intent);
        }
    }

    private void deleteAccount(final WalletAccount accountToDelete) {
        Preconditions.checkNotNull(accountToDelete);

        final View checkBoxView = View.inflate(getActivity(), R.layout.delkey_checkbox, null);
        final CheckBox keepAddrCheckbox = (CheckBox) checkBoxView.findViewById(R.id.checkbox);
        keepAddrCheckbox.setText(getString(R.string.keep_account_address));
        keepAddrCheckbox.setChecked(false);

        final AlertDialog.Builder deleteDialog = new AlertDialog.Builder(getActivity());
        deleteDialog.setTitle(R.string.delete_account_title);
        deleteDialog.setMessage(R.string.delete_account_message);

        // add checkbox only for SingleAddressAccounts and only if a private key is present
        final boolean hasPrivateData = accountToDelete instanceof ExportableAccount
                && ((ExportableAccount) accountToDelete).getExportData(AesKeyCipher.defaultKeyCipher()).privateData
                        .isPresent();

        if (accountToDelete instanceof SingleAddressAccount && hasPrivateData) {
            deleteDialog.setView(checkBoxView);
        }

        deleteDialog.setPositiveButton(R.string.yes, new DialogInterface.OnClickListener() {

            public void onClick(DialogInterface arg0, int arg1) {
                if (hasPrivateData) {
                    Long satoshis = getPotentialBalance(accountToDelete);
                    AlertDialog.Builder confirmDeleteDialog = new AlertDialog.Builder(getActivity());
                    confirmDeleteDialog.setTitle(R.string.confirm_delete_pk_title);

                    // Set the message. There are four combinations, with and without label, with and without BTC amount.
                    String label = _mbwManager.getMetadataStorage().getLabelByAccount(accountToDelete.getId());
                    String message;

                    // For active accounts we check whether there is money on them before deleting. we don't know if there
                    // is money on archived accounts
                    Optional<Address> receivingAddress = accountToDelete.getReceivingAddress();
                    if (accountToDelete.isActive() && satoshis != null && satoshis > 0) {
                        if (label != null && label.length() != 0) {
                            String address;
                            if (receivingAddress.isPresent()) {
                                address = receivingAddress.get().toMultiLineString();
                            } else {
                                address = "";
                            }
                            message = getString(R.string.confirm_delete_pk_with_balance_with_label, label, address,
                                    _mbwManager.getBtcValueString(satoshis));
                        } else {
                            message = getString(R.string.confirm_delete_pk_with_balance,
                                    receivingAddress.isPresent() ? receivingAddress.get().toMultiLineString() : "",
                                    _mbwManager.getBtcValueString(satoshis));
                        }
                    } else {
                        if (label != null && label.length() != 0) {
                            message = getString(R.string.confirm_delete_pk_without_balance_with_label, label,
                                    receivingAddress.isPresent() ? receivingAddress.get().toMultiLineString() : "");
                        } else {
                            message = getString(R.string.confirm_delete_pk_without_balance,
                                    receivingAddress.isPresent() ? receivingAddress.get().toMultiLineString() : "");
                        }
                    }
                    confirmDeleteDialog.setMessage(message);

                    confirmDeleteDialog.setPositiveButton(R.string.yes, new DialogInterface.OnClickListener() {

                        public void onClick(DialogInterface arg0, int arg1) {
                            if (keepAddrCheckbox.isChecked() && accountToDelete instanceof SingleAddressAccount) {
                                try {
                                    ((SingleAddressAccount) accountToDelete)
                                            .forgetPrivateKey(AesKeyCipher.defaultKeyCipher());
                                    _toaster.toast(R.string.private_key_deleted, false);
                                } catch (KeyCipher.InvalidKeyCipher e) {
                                    throw new RuntimeException(e);
                                }
                            } else {
                                try {
                                    walletManager.deleteUnrelatedAccount(accountToDelete.getId(),
                                            AesKeyCipher.defaultKeyCipher());
                                    _storage.deleteAccountMetadata(accountToDelete.getId());
                                } catch (KeyCipher.InvalidKeyCipher e) {
                                    throw new RuntimeException(e);
                                }
                                _mbwManager.setSelectedAccount(
                                        _mbwManager.getWalletManager(false).getActiveAccounts().get(0).getId());
                                _toaster.toast(R.string.account_deleted, false);
                            }
                            finishCurrentActionMode();
                            _mbwManager.getEventBus().post(new AccountChanged(accountToDelete.getId()));
                        }
                    });
                    confirmDeleteDialog.setNegativeButton(R.string.no, new DialogInterface.OnClickListener() {

                        public void onClick(DialogInterface arg0, int arg1) {
                        }
                    });
                    confirmDeleteDialog.show();
                } else {
                    // account has no private data - dont make a fuzz about it and just delete it
                    try {
                        walletManager.deleteUnrelatedAccount(accountToDelete.getId(),
                                AesKeyCipher.defaultKeyCipher());
                        _storage.deleteAccountMetadata(accountToDelete.getId());
                    } catch (KeyCipher.InvalidKeyCipher e) {
                        throw new RuntimeException(e);
                    }
                    finishCurrentActionMode();
                    _mbwManager.getEventBus().post(new AccountChanged(accountToDelete.getId()));
                    _toaster.toast(R.string.account_deleted, false);
                }
            }

            private Long getPotentialBalance(WalletAccount account) {
                if (account.isArchived()) {
                    return null;
                } else {
                    Balance balance = account.getBalance();
                    return balance.confirmed + balance.pendingChange + balance.pendingReceiving;
                }
            }

        });
        deleteDialog.setNegativeButton(R.string.no, new DialogInterface.OnClickListener() {

            public void onClick(DialogInterface arg0, int arg1) {
            }
        });
        deleteDialog.show();

    }

    private void finishCurrentActionMode() {
        if (currentActionMode != null) {
            currentActionMode.finish();
        }
    }

    private void setNameForNewAccount(WalletAccount account) {
        if (account == null || !isAdded()) {
            return;
        }
        String baseName = Utils.getNameForNewAccount(account, getActivity());
        //append counter if name already in use
        String defaultName = baseName;
        int num = 1;
        while (_storage.getAccountByLabel(defaultName).isPresent()) {
            defaultName = baseName + " (" + num++ + ')';
        }
        //we just put the default name into storage first, if there is none
        //if the user cancels entry or it gets somehow aborted, we at least have a valid entry
        if (_mbwManager.getMetadataStorage().getLabelByAccount(account.getId()).length() == 0) {
            _mbwManager.getMetadataStorage().storeAccountLabel(account.getId(), defaultName);
        }
        setLabelOnAccount(account, defaultName, false);
    }

    private void update() {
        if (!isAdded()) {
            return;
        }
        LinearLayout llRecords = (LinearLayout) getView().findViewById(R.id.llRecords);
        llRecords.removeAllViews();

        if (_mbwManager.isKeyManagementLocked()) {
            // Key management is locked
            getView().findViewById(R.id.svRecords).setVisibility(View.GONE);
            getView().findViewById(R.id.llLocked).setVisibility(View.VISIBLE);
        } else {
            // Make all the key management functionality available to experts
            getView().findViewById(R.id.svRecords).setVisibility(View.VISIBLE);
            getView().findViewById(R.id.llLocked).setVisibility(View.GONE);

            List<WalletAccount> activeHdAccounts = walletManager.getActiveMasterseedAccounts();
            List<WalletAccount> activeOtherAccounts = walletManager.getActiveOtherAccounts();

            List<WalletAccount> activeHdRecords = Utils.sortAccounts(activeHdAccounts, _storage);
            List<WalletAccount> activeOtherRecords = Utils.sortAccounts(activeOtherAccounts, _storage);
            List<WalletAccount> archivedRecords = Utils.sortAccounts(walletManager.getArchivedAccounts(), _storage);

            WalletAccount selectedAccount = _mbwManager.getSelectedAccount();

            CurrencySum totalSpendableBalance = new CurrencySum();
            String activeTitle = getString(R.string.active_hd_accounts_name)
                    + (activeHdRecords.isEmpty() ? " " + getString(R.string.active_accounts_empty) : "");
            CurrencySum spendableBalanceHdAccounts = getSpendableBalance(activeHdAccounts);
            LinearLayout activeHdAccountsView = createAccountViewList(activeTitle, activeHdRecords, selectedAccount,
                    spendableBalanceHdAccounts);
            llRecords.addView(activeHdAccountsView);

            totalSpendableBalance.add(spendableBalanceHdAccounts);

            if (!activeOtherRecords.isEmpty()) {
                CurrencySum spendableBalanceOtherAccounts = getSpendableBalance(activeOtherAccounts);
                LinearLayout activeOtherAccountsView = createAccountViewList(
                        getString(R.string.active_other_accounts_name), activeOtherRecords, selectedAccount,
                        spendableBalanceOtherAccounts);
                llRecords.addView(activeOtherAccountsView);

                totalSpendableBalance.add(spendableBalanceOtherAccounts);

                // only show a totals row, if both account type exits
                LinearLayout activeOtherSum = createActiveAccountBalanceSumView(totalSpendableBalance);
                llRecords.addView(activeOtherSum);
            }

            if (archivedRecords.size() > 0) {
                LinearLayout archived = createAccountViewList(getString(R.string.archive_name), archivedRecords,
                        selectedAccount, null);
                llRecords.addView(archived);
            }
        }
    }

    private LinearLayout createActiveAccountBalanceSumView(CurrencySum spendableBalance) {
        LinearLayout outer = new LinearLayout(getActivity());
        outer.setOrientation(LinearLayout.VERTICAL);
        outer.setLayoutParams(_outerLayoutParameters);

        LinearLayout inner = new LinearLayout(getActivity());
        inner.setOrientation(LinearLayout.VERTICAL);
        inner.setLayoutParams(_innerLayoutParameters);
        inner.requestLayout();

        // Add records
        RecordRowBuilder builder = new RecordRowBuilder(_mbwManager, getResources(), _layoutInflater);

        // Add item
        View item = builder.buildTotalView(outer, spendableBalance);
        inner.addView(item);

        // Add separator
        inner.addView(createSeparator());

        outer.addView(inner);
        return outer;
    }

    private CurrencySum getSpendableBalance(List<WalletAccount> accounts) {
        CurrencySum currencySum = new CurrencySum();
        for (WalletAccount account : accounts) {
            currencySum.add(account.getCurrencyBasedBalance().confirmed);
        }
        return currencySum;
    }

    private LinearLayout createAccountViewList(String title, List<WalletAccount> accounts,
            WalletAccount selectedAccount, CurrencySum spendableBalance) {
        LinearLayout outer = new LinearLayout(getActivity());
        outer.setOrientation(LinearLayout.VERTICAL);
        outer.setLayoutParams(_outerLayoutParameters);

        // Add title
        createTitle(outer, title, spendableBalance);

        if (accounts.isEmpty()) {
            return outer;
        }

        LinearLayout inner = new LinearLayout(getActivity());
        inner.setOrientation(LinearLayout.VERTICAL);
        inner.setLayoutParams(_innerLayoutParameters);
        inner.requestLayout();

        //      // Add records
        RecordRowBuilder builder = new RecordRowBuilder(_mbwManager, getResources(), _layoutInflater);
        for (WalletAccount account : accounts) {
            // Add separator
            inner.addView(createSeparator());

            // Add item
            boolean isSelected = account.equals(selectedAccount);
            View item = createAccountView(outer, account, isSelected, builder);
            inner.addView(item);
        }

        if (accounts.size() > 0) {
            // Add separator
            inner.addView(createSeparator());
        }

        outer.addView(inner);
        return outer;
    }

    private TextView createTitle(ViewGroup root, String title, CurrencySum balance) {
        View view = _layoutInflater.inflate(R.layout.accounts_title_view, root, true);
        TextView tvTitle = (TextView) view.findViewById(R.id.tvTitle);
        tvTitle.setText(title);

        ToggleableCurrencyDisplay tvBalance = (ToggleableCurrencyDisplay) view.findViewById(R.id.tvBalance);
        if (balance != null) {
            tvBalance.setEventBus(_mbwManager.getEventBus());
            tvBalance.setCurrencySwitcher(_mbwManager.getCurrencySwitcher());
            tvBalance.setValue(balance);
        } else {
            tvBalance.setVisibility(View.GONE);
        }

        return tvTitle;
    }

    private View createSeparator() {
        View v = new View(getActivity());
        v.setLayoutParams(_separatorLayoutParameters);
        v.setBackgroundColor(_separatorColor);
        v.setPadding(10, 0, 10, 0);
        return v;
    }

    private View createAccountView(LinearLayout parent, WalletAccount account, boolean isSelected,
            RecordRowBuilder recordRowBuilder) {
        boolean hasFocus = _focusedAccount != null && account.equals(_focusedAccount);
        View rowView = recordRowBuilder.buildRecordView(parent, account, isSelected, hasFocus);
        rowView.setOnClickListener(recordStarClickListener);
        rowView.findViewById(R.id.llAddress).setOnClickListener(recordAddressClickListener);
        return rowView;
    }

    private OnClickListener recordStarClickListener = new OnClickListener() {

        @Override
        public void onClick(View v) {
            _focusedAccount = (WalletAccount) v.getTag();
            if (_focusedAccount.isActive()) {
                _mbwManager.setSelectedAccount(_focusedAccount.getId());
            }
            update();
        }
    };

    private ActionMode currentActionMode;

    private OnClickListener recordAddressClickListener = new OnClickListener() {

        @Override
        public void onClick(View v) {
            final WalletAccount account = (WalletAccount) ((View) Preconditions.checkNotNull(v.getParent()))
                    .getTag();

            // Check whether a new account was selected
            if (!_mbwManager.getSelectedAccount().equals(account) && account.isActive()) {
                _mbwManager.setSelectedAccount(account.getId());
            }
            _focusedAccount = account;
            toastSelectedAccountChanged(account);
            updateIncludingMenus();
        }

    };

    private void updateIncludingMenus() {
        WalletAccount account = _focusedAccount;

        final List<Integer> menus = Lists.newArrayList();
        menus.add(R.menu.record_options_menu);

        if ((account instanceof SingleAddressAccount) || (account.isDerivedFromInternalMasterseed())) {
            menus.add(R.menu.record_options_menu_backup);
        }

        if (account instanceof SingleAddressAccount) {
            menus.add(R.menu.record_options_menu_backup_verify);
        }

        if (!account.isDerivedFromInternalMasterseed()) {
            menus.add(R.menu.record_options_menu_delete);
        }

        if (account.isActive() && account.canSpend() && !(account instanceof Bip44PubOnlyAccount)) {
            menus.add(R.menu.record_options_menu_sign);
        }

        if (account.isActive()) {
            menus.add(R.menu.record_options_menu_active);
        }

        if (account.isActive() && !(account instanceof CoinapultAccount)) {
            menus.add(R.menu.record_options_menu_outputs);
        }

        if (account instanceof CoinapultAccount) {
            menus.add(R.menu.record_options_menu_set_coinapult_mail);
        }

        if (account.isArchived()) {
            menus.add(R.menu.record_options_menu_archive);
        }

        if (account.isActive() && account instanceof ExportableAccount) {
            menus.add(R.menu.record_options_menu_export);
        }

        if (account.isActive() && account instanceof Bip44Account && !(account instanceof Bip44PubOnlyAccount)) {
            if (!((Bip44Account) account).hasHadActivity()) {
                //only allow to remove unused HD acounts from the view
                menus.add(R.menu.record_options_menu_hide_unused);
            }
        }

        if (RecordRowBuilder.showLegacyAccountWarning(account, _mbwManager)) {
            menus.add(R.menu.record_options_menu_ignore_warning);
        }

        if (account.getId().equals(_mbwManager.getLocalTraderManager().getLocalTraderAccountId())) {
            menus.add(R.menu.record_options_menu_detach);
        }

        ActionBarActivity parent = (ActionBarActivity) getActivity();

        Callback actionMode = new Callback() {
            @Override
            public boolean onCreateActionMode(ActionMode actionMode, Menu menu) {
                for (Integer res : menus) {
                    actionMode.getMenuInflater().inflate(res, menu);
                }
                return true;
            }

            @Override
            public boolean onPrepareActionMode(ActionMode actionMode, Menu menu) {
                return true;
            }

            @Override
            public boolean onActionItemClicked(ActionMode actionMode, MenuItem menuItem) {
                // If we are synchronizing, show "Synchronizing, please wait..." to avoid blocking behavior
                if (_mbwManager.getWalletManager(false).getState() == WalletManager.State.SYNCHRONIZING) {
                    _toaster.toast(R.string.synchronizing_please_wait, false);
                    return true;
                }
                int id = menuItem.getItemId();
                if (id == R.id.miActivate) {
                    activateSelected();
                    return true;
                } else if (id == R.id.miSetLabel) {
                    setLabelOnAccount(_focusedAccount, "", true);
                    return true;
                } else if (id == R.id.miDeleteRecord) {
                    deleteSelected();
                    return true;
                } else if (id == R.id.miArchive) {
                    archiveSelected();
                    return true;
                } else if (id == R.id.miHideUnusedAccount) {
                    hideSelected();
                    return true;
                } else if (id == R.id.miExport) {
                    exportSelectedPrivateKey();
                    return true;
                } else if (id == R.id.miIgnoreWarnings) {
                    ignoreSelectedPrivateKey();
                    return true;
                } else if (id == R.id.miSignMessage) {
                    signMessage();
                    return true;
                } else if (id == R.id.miDetach) {
                    detachFromLocalTrader();
                    return true;
                } else if (id == R.id.miShowOutputs) {
                    showOutputs();
                    return true;
                } else if (id == R.id.miMakeBackup) {
                    makeBackup();
                    return true;
                } else if (id == R.id.miSingleKeyBackupVerify) {
                    verifySingleKeyBackup();
                    return true;
                } else if (id == R.id.miRescan) {
                    rescan();
                    return true;
                } else if (id == R.id.miSetMail) {
                    setCoinapultMail();
                    return true;
                } else if (id == R.id.miVerifyMail) {
                    verifyCoinapultMail();
                    return true;
                }
                return false;
            }

            @Override
            public void onDestroyActionMode(ActionMode actionMode) {
                currentActionMode = null;
                // Loose focus
                if (_focusedAccount != null) {
                    _focusedAccount = null;
                    update();
                }
            }
        };
        currentActionMode = parent.startSupportActionMode(actionMode);
        // Late set the focused record. We have to do this after
        // startSupportActionMode above, as it calls onDestroyActionMode when
        // starting for some reason, and this would clear the focus and force
        // an update.
        _focusedAccount = account;

        update();
    }

    //todo: maybe move it to another class along with the other coinaspult mail stuff? would require passing the context for dialog boxes though.
    private void setCoinapultMail() {

        AlertDialog.Builder b = new AlertDialog.Builder(getActivity());
        b.setTitle(getString(R.string.coinapult_mail_question));
        View diaView = getActivity().getLayoutInflater().inflate(R.layout.ext_coinapult_mail, null);
        final EditText mailField = (EditText) diaView.findViewById(R.id.mail);
        mailField.setInputType(InputType.TYPE_CLASS_TEXT | InputType.TYPE_TEXT_VARIATION_EMAIL_ADDRESS);
        String email = _mbwManager.getMetadataStorage().getCoinapultMail();
        if (!email.isEmpty()) {
            mailField.setText(email);
        }
        b.setView(diaView);
        b.setPositiveButton(getString(R.string.button_done), new DialogInterface.OnClickListener() {
            @Override
            public void onClick(DialogInterface dialog, int which) {
                String mailText = mailField.getText().toString();
                if (Utils.isValidEmailAddress(mailText)) {
                    Optional<String> mail;
                    if (mailText.isEmpty()) {
                        mail = Optional.absent();
                    } else {
                        mail = Optional.of(mailText);
                    }
                    _progress.setCancelable(false);
                    _progress.setProgressStyle(ProgressDialog.STYLE_SPINNER);
                    _progress.setMessage(getString(R.string.coinapult_setting_email));
                    _progress.show();
                    _mbwManager.getMetadataStorage().setCoinapultMail(mailText);
                    new SetCoinapultMailAsyncTask(mail).execute();
                    dialog.dismiss();
                } else {
                    new Toaster(AccountsFragment.this).toast("Email address not valid", false);
                }
            }
        });
        b.setNegativeButton(getString(R.string.cancel), new DialogInterface.OnClickListener() {
            @Override
            public void onClick(DialogInterface dialog, int which) {
                dialog.dismiss();
            }
        });

        AlertDialog dialog = b.create();
        dialog.show();
    }

    private void verifyCoinapultMail() {
        AlertDialog.Builder b = new AlertDialog.Builder(getActivity());
        b.setTitle(getString(R.string.coinapult_mail_verification));
        final String email = _mbwManager.getMetadataStorage().getCoinapultMail();
        View diaView = getActivity().getLayoutInflater().inflate(R.layout.ext_coinapult_mail_verification, null);
        final EditText verificationTextField = (EditText) diaView.findViewById(R.id.mailVerification);

        // check if there is a probable verification link in the clipboard and if so, pre-fill the textbox
        String clipboardString = Utils.getClipboardString(getActivity());
        if (!Strings.isNullOrEmpty(clipboardString) && clipboardString.contains("coinapult.com")) {
            verificationTextField.setText(clipboardString);
        }

        b.setView(diaView);
        b.setPositiveButton(getString(R.string.button_done), new DialogInterface.OnClickListener() {
            @Override
            public void onClick(DialogInterface dialog, int which) {
                String verification = verificationTextField.getText().toString();
                _progress.setCancelable(false);
                _progress.setProgressStyle(ProgressDialog.STYLE_SPINNER);
                _progress.setMessage(getString(R.string.coinapult_verifying_email));
                _progress.show();
                new VerifyCoinapultMailAsyncTask(verification, email).execute();
                dialog.dismiss();
            }
        });
        b.setNegativeButton(getString(R.string.cancel), new DialogInterface.OnClickListener() {
            @Override
            public void onClick(DialogInterface dialog, int which) {
                dialog.dismiss();
            }
        });

        AlertDialog dialog = b.create();
        dialog.show();
    }

    private class SetCoinapultMailAsyncTask extends AsyncTask<Void, Integer, Boolean> {
        private Optional<String> mail;

        public SetCoinapultMailAsyncTask(Optional<String> mail) {
            this.mail = mail;
        }

        @Override
        protected Boolean doInBackground(Void... params) {
            return _mbwManager.getCoinapultManager().setMail(mail);
        }

        @Override
        protected void onPostExecute(Boolean success) {
            _progress.dismiss();
            if (success) {
                Utils.showSimpleMessageDialog(getActivity(), R.string.coinapult_set_mail_please_verify);
            } else {
                Utils.showSimpleMessageDialog(getActivity(), R.string.coinapult_set_mail_failed);
            }
        }
    }

    private class VerifyCoinapultMailAsyncTask extends AsyncTask<Void, Integer, Boolean> {
        private String link;
        private String email;

        public VerifyCoinapultMailAsyncTask(String link, String email) {
            this.link = link;
            this.email = email;
        }

        @Override
        protected Boolean doInBackground(Void... params) {
            return _mbwManager.getCoinapultManager().verifyMail(link, email);
        }

        @Override
        protected void onPostExecute(Boolean success) {
            _progress.dismiss();
            if (success) {
                Utils.showSimpleMessageDialog(getActivity(), R.string.coinapult_verify_mail_success);
            } else {
                Utils.showSimpleMessageDialog(getActivity(), R.string.coinapult_verify_mail_error);
            }
        }
    }

    private void verifySingleKeyBackup() {
        if (!AccountsFragment.this.isAdded()) {
            return;
        }

        if (_focusedAccount instanceof SingleAddressAccount) {
            //start legacy backup verification
            VerifyBackupActivity.callMe(getActivity());
        }
    }

    private void makeBackup() {
        if (!AccountsFragment.this.isAdded()) {
            return;
        }

        if (_focusedAccount.isDerivedFromInternalMasterseed()) {
            //start wordlist backup if a HD account or derived account was selected
            Utils.pinProtectedWordlistBackup(getActivity());
        } else if (_focusedAccount instanceof SingleAddressAccount) {
            //start legacy backup if a single key or watch only was selected
            Utils.pinProtectedBackup(getActivity());
        }
    }

    private void showOutputs() {
        Intent intent = new Intent(getActivity(), UnspentOutputsActivity.class);
        intent.putExtra("account", _focusedAccount.getId());
        startActivity(intent);
    }

    private void signMessage() {
        if (!AccountsFragment.this.isAdded()) {
            return;
        }
        _mbwManager.runPinProtectedFunction(AccountsFragment.this.getActivity(), new Runnable() {

            @Override
            public void run() {
                if (!AccountsFragment.this.isAdded()) {
                    return;
                }
                if (_focusedAccount instanceof CoinapultAccount) {
                    CoinapultManager coinapultManager = _mbwManager.getCoinapultManager();
                    MessageSigningActivity.callMe(getActivity(), coinapultManager.getAccountKey());
                } else if (_focusedAccount instanceof SingleAddressAccount) {
                    MessageSigningActivity.callMe(getActivity(), (SingleAddressAccount) _focusedAccount);
                } else {
                    Intent intent = new Intent(getActivity(), HDSigningActivity.class);
                    intent.putExtra("account", _focusedAccount.getId());
                    startActivity(intent);
                }
            }
        });
    }

    /**
     * Show a message to the user explaining what it means to select a different
     * address.
     */
    private void toastSelectedAccountChanged(WalletAccount account) {
        if (account.isArchived()) {
            _toaster.toast(getString(R.string.selected_archived_warning), true);
        } else if (account instanceof Bip44Account) {
            _toaster.toast(getString(R.string.selected_hd_info), true);
        } else if (account instanceof SingleAddressAccount) {
            _toaster.toast(getString(R.string.selected_single_info), true);
        }
    }

    @Override
    public boolean onOptionsItemSelected(MenuItem item) {
        // If we are synchronizing, show "Synchronizing, please wait..." to avoid blocking behavior
        if (_mbwManager.getWalletManager(false).getState() == WalletManager.State.SYNCHRONIZING) {
            _toaster.toast(R.string.synchronizing_please_wait, false);
            return true;
        }

        if (!isAdded()) {
            return true;
        }
        if (item.getItemId() == R.id.miAddRecord) {
            AddAccountActivity.callMe(this, ADD_RECORD_RESULT_CODE);
            return true;
        } else if (item.getItemId() == R.id.miAddFiatAccount) {
            Intent intent = AddCoinapultAccountActivity.getIntent(getActivity());
            this.startActivityForResult(intent, ADD_RECORD_RESULT_CODE);
            return true;
        } else if (item.getItemId() == R.id.miLockKeys) {
            lock();
            return true;
        }

        return super.onOptionsItemSelected(item);
    }

    private void setLabelOnAccount(final WalletAccount account, final String defaultName, boolean askForPin) {
        if (!AccountsFragment.this.isAdded()) {
            return;
        }
        if (askForPin) {
            _mbwManager.runPinProtectedFunction(AccountsFragment.this.getActivity(), new Runnable() {

                @Override
                public void run() {
                    if (!AccountsFragment.this.isAdded()) {
                        return;
                    }
                    EnterAddressLabelUtil.enterAccountLabel(getActivity(), account.getId(), defaultName, _storage);
                }

            });
        } else {
            EnterAddressLabelUtil.enterAccountLabel(getActivity(), account.getId(), defaultName, _storage);
        }
    }

    private void deleteSelected() {
        if (!AccountsFragment.this.isAdded()) {
            return;
        }
        if (_focusedAccount.isActive() && _mbwManager.getWalletManager(false).getActiveAccounts().size() < 2) {
            _toaster.toast(R.string.keep_one_active, false);
            return;
        }
        _mbwManager.runPinProtectedFunction(AccountsFragment.this.getActivity(), new Runnable() {

            @Override
            public void run() {
                if (!AccountsFragment.this.isAdded()) {
                    return;
                }
                deleteAccount(_focusedAccount);
            }

        });
    }

    private void rescan() {
        if (!AccountsFragment.this.isAdded()) {
            return;
        }
        _focusedAccount.dropCachedData();
        _mbwManager.getWalletManager(false).startSynchronization(SyncMode.FULL_SYNC_CURRENT_ACCOUNT_FORCED);
    }

    private void ignoreSelectedPrivateKey() {
        if (!AccountsFragment.this.isAdded()) {
            return;
        }
        _mbwManager.runPinProtectedFunction(AccountsFragment.this.getActivity(), new Runnable() {

            @Override
            public void run() {
                if (!AccountsFragment.this.isAdded()) {
                    return;
                }
                AlertDialog.Builder confirmDialog = new AlertDialog.Builder(getActivity());
                confirmDialog.setTitle(R.string.ignore_warnings_title);
                confirmDialog.setMessage(getString(R.string.ignore_warnings_description));
                confirmDialog.setPositiveButton(R.string.yes, new DialogInterface.OnClickListener() {

                    public void onClick(DialogInterface arg0, int arg1) {
                        _mbwManager.getMetadataStorage().setIgnoreLegacyWarning(_focusedAccount.getId(), true);
                        _mbwManager.getEventBus().post(new AccountChanged(_focusedAccount.getId()));
                    }
                });
                confirmDialog.setNegativeButton(R.string.no, new DialogInterface.OnClickListener() {
                    public void onClick(DialogInterface arg0, int arg1) {
                        _mbwManager.getMetadataStorage().setIgnoreLegacyWarning(_focusedAccount.getId(), false);
                        _mbwManager.getEventBus().post(new AccountChanged(_focusedAccount.getId()));
                    }
                });
                confirmDialog.show();
            }

        });
    }

    private void exportSelectedPrivateKey() {
        if (!AccountsFragment.this.isAdded()) {
            return;
        }
        _mbwManager.runPinProtectedFunction(AccountsFragment.this.getActivity(), new Runnable() {

            @Override
            public void run() {
                if (!AccountsFragment.this.isAdded()) {
                    return;
                }
                Utils.exportSelectedAccount(AccountsFragment.this.getActivity());
            }

        });
    }

    private void detachFromLocalTrader() {
        if (!AccountsFragment.this.isAdded()) {
            return;
        }
        _mbwManager.runPinProtectedFunction(AccountsFragment.this.getActivity(), new Runnable() {

            @Override
            public void run() {
                if (!AccountsFragment.this.isAdded()) {
                    return;
                }
                AlertDialog.Builder confirmDialog = new AlertDialog.Builder(getActivity());
                confirmDialog.setTitle(R.string.lt_detaching_title);
                confirmDialog.setMessage(getString(R.string.lt_detaching_question));
                confirmDialog.setPositiveButton(R.string.yes, new DialogInterface.OnClickListener() {

                    public void onClick(DialogInterface arg0, int arg1) {
                        _mbwManager.getLocalTraderManager().unsetLocalTraderAccount();
                        _toaster.toast(R.string.lt_detached, false);
                        update();
                    }
                });
                confirmDialog.setNegativeButton(R.string.no, new DialogInterface.OnClickListener() {

                    public void onClick(DialogInterface arg0, int arg1) {
                    }
                });
                confirmDialog.show();
            }

        });
    }

    private void activateSelected() {
        if (!AccountsFragment.this.isAdded()) {
            return;
        }
        _mbwManager.runPinProtectedFunction(AccountsFragment.this.getActivity(), new Runnable() {

            @Override
            public void run() {
                if (!AccountsFragment.this.isAdded()) {
                    return;
                }
                activate(_focusedAccount);
            }

        });
    }

    private void activate(WalletAccount account) {
        account.activateAccount();
        //setselected also broadcasts AccountChanged event
        _mbwManager.setSelectedAccount(account.getId());
        updateIncludingMenus();
        _toaster.toast(R.string.activated, false);
        _mbwManager.getWalletManager(false).startSynchronization();
    }

    private void archiveSelected() {
        if (!AccountsFragment.this.isAdded()) {
            return;
        }
        if (_mbwManager.getWalletManager(false).getActiveAccounts().size() < 2) {
            //this is the last active account, we dont allow archiving it
            _toaster.toast(R.string.keep_one_active, false);
            return;
        }
        if (_focusedAccount instanceof CoinapultAccount) {
            _mbwManager.runPinProtectedFunction(AccountsFragment.this.getActivity(), new Runnable() {

                @Override
                public void run() {
                    if (!AccountsFragment.this.isAdded()) {
                        return;
                    }

                    archive(_focusedAccount);
                }

            });
            return;
        }
        if (_focusedAccount instanceof Bip44Account) {
            Bip44Account account = (Bip44Account) _focusedAccount;
            if (!account.hasHadActivity()) {
                //this account is unused, we dont allow archiving it
                _toaster.toast(R.string.dont_allow_archiving_unused_notification, false);
                return;
            }
        }
        _mbwManager.runPinProtectedFunction(AccountsFragment.this.getActivity(), new Runnable() {

            @Override
            public void run() {
                if (!AccountsFragment.this.isAdded()) {
                    return;
                }

                archive(_focusedAccount);
            }

        });
    }

    private void hideSelected() {
        if (!AccountsFragment.this.isAdded()) {
            return;
        }
        if (_mbwManager.getWalletManager(false).getActiveAccounts().size() < 2) {
            //this is the last active account, we dont allow hiding it
            _toaster.toast(R.string.keep_one_active, false);
            return;
        }
        if (_focusedAccount instanceof Bip44Account) {
            final Bip44Account account = (Bip44Account) _focusedAccount;
            if (account.hasHadActivity()) {
                //this account is used, we don't allow hiding it
                _toaster.toast(R.string.dont_allow_hiding_used_notification, false);
                return;
            }

            _mbwManager.runPinProtectedFunction(this.getActivity(), new Runnable() {
                @Override
                public void run() {
                    _mbwManager.getWalletManager(false).removeUnusedBip44Account();
                    //in case user had labeled the account, delete the stored name
                    _storage.deleteAccountMetadata(account.getId());
                    //setselected also broadcasts AccountChanged event, which will cause an ui update
                    _mbwManager.setSelectedAccount(
                            _mbwManager.getWalletManager(false).getActiveAccounts().get(0).getId());
                    //we dont want to show the context menu for the automatically selected account
                    _focusedAccount = null;
                    finishCurrentActionMode();
                }
            });

        }
    }

    private void archive(final WalletAccount account) {
        AlertDialog.Builder confirmDialog = new AlertDialog.Builder(getActivity());
        confirmDialog.setTitle(R.string.archiving_account_title);
        confirmDialog.setMessage(getString(R.string.question_archive_account));
        confirmDialog.setPositiveButton(R.string.yes, new DialogInterface.OnClickListener() {

            public void onClick(DialogInterface arg0, int arg1) {
                account.archiveAccount();
                _mbwManager
                        .setSelectedAccount(_mbwManager.getWalletManager(false).getActiveAccounts().get(0).getId());
                _mbwManager.getEventBus().post(new AccountChanged(account.getId()));
                updateIncludingMenus();
                _toaster.toast(R.string.archived, false);
            }
        });
        confirmDialog.setNegativeButton(R.string.no, new DialogInterface.OnClickListener() {

            public void onClick(DialogInterface arg0, int arg1) {
            }
        });
        confirmDialog.show();
    }

    private void lock() {
        _mbwManager.setKeyManagementLocked(true);
        update();
        if (isAdded()) {
            getActivity().supportInvalidateOptionsMenu();
        }
    }

    OnClickListener unlockClickedListener = new OnClickListener() {

        @Override
        public void onClick(View v) {
            _mbwManager.runPinProtectedFunction(AccountsFragment.this.getActivity(), new Runnable() {

                @Override
                public void run() {
                    _mbwManager.setKeyManagementLocked(false);
                    update();
                    if (isAdded()) {
                        getActivity().supportInvalidateOptionsMenu();
                    }
                }

            });
        }
    };

    @Subscribe()
    public void onExtraAccountsChanged(ExtraAccountsChanged event) {
        update();
    }

    @Subscribe
    public void addressChanged(ReceivingAddressChanged event) {
        update();
    }

    @Subscribe
    public void balanceChanged(BalanceChanged event) {
        update();
    }

    @Subscribe
    public void syncStarted(SyncStarted event) {
        update();
    }

    @Subscribe
    public void syncStarting(SyncStopped event) {
        update();
    }

    @Subscribe
    public void accountChanged(AccountChanged event) {
        update();
    }

}