org.totschnig.myexpenses.activity.MyExpenses.java Source code

Java tutorial

Introduction

Here is the source code for org.totschnig.myexpenses.activity.MyExpenses.java

Source

/*   This file is part of My Expenses.
 *   My Expenses 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.
 *
 *   My Expenses 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 My Expenses.  If not, see <http://www.gnu.org/licenses/>.
*/

package org.totschnig.myexpenses.activity;

import android.app.ProgressDialog;
import android.content.Context;
import android.content.Intent;
import android.content.res.Configuration;
import android.content.res.Resources;
import android.database.Cursor;
import android.database.sqlite.SQLiteException;
import android.graphics.Typeface;
import android.net.Uri;
import android.os.Build;
import android.os.Bundle;
import android.support.v4.app.Fragment;
import android.support.v4.app.FragmentManager;
import android.support.v4.app.LoaderManager;
import android.support.v4.content.CursorLoader;
import android.support.v4.content.Loader;
import android.support.v4.view.GravityCompat;
import android.support.v4.view.MenuItemCompat;
import android.support.v4.view.ViewPager;
import android.support.v4.view.ViewPager.OnPageChangeListener;
import android.support.v4.widget.DrawerLayout;
import android.support.v7.app.ActionBarDrawerToggle;
import android.support.v7.preference.PreferenceManager;
import android.support.v7.widget.CardView;
import android.support.v7.widget.PopupMenu;
import android.support.v7.widget.Toolbar;
import android.text.ClipboardManager;
import android.text.TextUtils;
import android.util.Log;
import android.util.TypedValue;
import android.view.LayoutInflater;
import android.view.Menu;
import android.view.MenuInflater;
import android.view.MenuItem;
import android.view.SubMenu;
import android.view.View;
import android.view.View.OnClickListener;
import android.view.ViewGroup;
import android.view.Window;
import android.view.WindowManager;
import android.widget.AdapterView;
import android.widget.AdapterView.OnItemClickListener;
import android.widget.TextView;
import android.widget.Toast;

import org.apache.commons.lang3.ArrayUtils;
import org.totschnig.myexpenses.BuildConfig;
import org.totschnig.myexpenses.MyApplication;
import org.totschnig.myexpenses.model.AccountGrouping;
import org.totschnig.myexpenses.model.CurrencyEnum;
import org.totschnig.myexpenses.preference.PrefKey;
import org.totschnig.myexpenses.R;
import org.totschnig.myexpenses.dialog.BalanceDialogFragment;
import org.totschnig.myexpenses.dialog.ConfirmationDialogFragment;
import org.totschnig.myexpenses.dialog.ConfirmationDialogFragment.ConfirmationDialogListener;
import org.totschnig.myexpenses.dialog.ContribInfoDialogFragment;
import org.totschnig.myexpenses.dialog.EditTextDialog;
import org.totschnig.myexpenses.dialog.EditTextDialog.EditTextDialogListener;
import org.totschnig.myexpenses.dialog.ExportDialogFragment;
import org.totschnig.myexpenses.dialog.MessageDialogFragment;
import org.totschnig.myexpenses.dialog.ProgressDialogFragment;
import org.totschnig.myexpenses.dialog.RemindRateDialogFragment;
import org.totschnig.myexpenses.dialog.TransactionDetailFragment;
import org.totschnig.myexpenses.dialog.WelcomeDialogFragment;
import org.totschnig.myexpenses.fragment.ContextualActionBarFragment;
import org.totschnig.myexpenses.fragment.TransactionList;
import org.totschnig.myexpenses.model.Account;
import org.totschnig.myexpenses.model.Grouping;
import org.totschnig.myexpenses.model.AccountType;
import org.totschnig.myexpenses.model.AggregateAccount;
import org.totschnig.myexpenses.model.ContribFeature;
import org.totschnig.myexpenses.model.Money;
import org.totschnig.myexpenses.model.Template;
import org.totschnig.myexpenses.model.Transaction;
import org.totschnig.myexpenses.provider.TransactionDatabase;
import org.totschnig.myexpenses.provider.TransactionProvider;
import org.totschnig.myexpenses.provider.filter.CommentCriteria;
import org.totschnig.myexpenses.provider.filter.Criteria;
import org.totschnig.myexpenses.task.TaskExecutionFragment;
import org.totschnig.myexpenses.ui.CursorFragmentPagerAdapter;
import org.totschnig.myexpenses.ui.FragmentPagerAdapter;
import org.totschnig.myexpenses.ui.SimpleCursorAdapter;
import org.totschnig.myexpenses.util.AcraHelper;
import org.totschnig.myexpenses.util.AdHandler;
import org.totschnig.myexpenses.util.FileUtils;
import org.totschnig.myexpenses.util.Result;
import org.totschnig.myexpenses.util.Utils;

import java.io.Serializable;
import java.util.ArrayList;
import java.util.Currency;
import java.util.Locale;

import se.emilsjolander.stickylistheaders.StickyListHeadersAdapter;
import se.emilsjolander.stickylistheaders.StickyListHeadersListView;

import static org.totschnig.myexpenses.provider.DatabaseConstants.KEY_ACCOUNTID;
import static org.totschnig.myexpenses.provider.DatabaseConstants.KEY_CLEARED_TOTAL;
import static org.totschnig.myexpenses.provider.DatabaseConstants.KEY_COLOR;
import static org.totschnig.myexpenses.provider.DatabaseConstants.KEY_CURRENCY;
import static org.totschnig.myexpenses.provider.DatabaseConstants.KEY_CURRENT_BALANCE;
import static org.totschnig.myexpenses.provider.DatabaseConstants.KEY_DESCRIPTION;
import static org.totschnig.myexpenses.provider.DatabaseConstants.KEY_HAS_CLEARED;
import static org.totschnig.myexpenses.provider.DatabaseConstants.KEY_HAS_EXPORTED;
import static org.totschnig.myexpenses.provider.DatabaseConstants.KEY_HAS_FUTURE;
import static org.totschnig.myexpenses.provider.DatabaseConstants.KEY_LABEL;
import static org.totschnig.myexpenses.provider.DatabaseConstants.KEY_OPENING_BALANCE;
import static org.totschnig.myexpenses.provider.DatabaseConstants.KEY_RECONCILED_TOTAL;
import static org.totschnig.myexpenses.provider.DatabaseConstants.KEY_ROWID;
import static org.totschnig.myexpenses.provider.DatabaseConstants.KEY_SORT_KEY;
import static org.totschnig.myexpenses.provider.DatabaseConstants.KEY_SUM_EXPENSES;
import static org.totschnig.myexpenses.provider.DatabaseConstants.KEY_SUM_INCOME;
import static org.totschnig.myexpenses.provider.DatabaseConstants.KEY_SUM_TRANSFERS;
import static org.totschnig.myexpenses.provider.DatabaseConstants.KEY_TOTAL;
import static org.totschnig.myexpenses.provider.DatabaseConstants.KEY_TRANSACTIONID;
import static org.totschnig.myexpenses.provider.DatabaseConstants.KEY_TYPE;

/**
 * This is the main activity where all expenses are listed
 * From the menu subactivities (Insert, Reset, SelectAccount, Help, Settings)
 * are called
 *
 * @author Michael Totschnig
 */
public class MyExpenses extends LaunchActivity
        implements OnPageChangeListener, LoaderManager.LoaderCallbacks<Cursor>, EditTextDialogListener,
        ConfirmationDialogFragment.ConfirmationDialogCheckedListener, ConfirmationDialogListener, ContribIFace {

    public static final int TYPE_TRANSACTION = 0;
    public static final int TYPE_TRANSFER = 1;
    public static final int TYPE_SPLIT = 2;

    public static final long TRESHOLD_REMIND_RATE = 47L;
    public static final long TRESHOLD_REMIND_CONTRIB = 113L;

    public static final int ACCOUNTS_CURSOR = -1;

    private LoaderManager mManager;

    int mCurrentPosition = -1;
    private Cursor mAccountsCursor;

    private MyViewPagerAdapter mViewPagerAdapter;
    private StickyListHeadersAdapter mDrawerListAdapter;
    private ViewPager myPager;
    private long mAccountId = 0;
    int mAccountCount = 0;
    private AdHandler adHandler;
    private Toolbar mToolbar;
    private String mCurrentBalance;
    private SubMenu sortMenu;

    public enum HelpVariant {
        crStatus
    }

    private void setHelpVariant() {
        Account account = Account.getInstanceFromDb(mAccountId);
        helpVariant = account == null || account.type.equals(AccountType.CASH) ? null : HelpVariant.crStatus;
    }

    /**
     * stores the number of transactions that have been
     * created in the db, updated after each creation of
     * a new transaction
     */
    private long sequenceCount = 0;
    private int colorAggregate;
    private StickyListHeadersListView mDrawerList;
    private DrawerLayout mDrawerLayout;
    private ActionBarDrawerToggle mDrawerToggle;

    private int columnIndexRowId, columnIndexColor, columnIndexCurrency, columnIndexDescription, columnIndexLabel;
    boolean indexesCalculated = false;
    private long idFromNotification = 0;
    private String mExportFormat = null;
    public boolean setupComplete;
    private AccountGrouping mAccountGrouping;

    /* (non-Javadoc)
     * Called when the activity is first created.
     * @see android.app.Activity#onCreate(android.os.Bundle)
     */
    @Override
    public void onCreate(Bundle savedInstanceState) {
        setTheme(MyApplication.getThemeId());
        Resources.Theme theme = getTheme();
        TypedValue value = new TypedValue();
        theme.resolveAttribute(R.attr.colorAggregate, value, true);
        colorAggregate = value.data;
        int prev_version = PrefKey.CURRENT_VERSION.getInt(-1);
        if (prev_version == -1) {
            //prevent preference change listener from firing when preference file is created
            if (MyApplication.getInstance().isInstrumentationTest()) {
                PreferenceManager.setDefaultValues(this, MyApplication.getTestId(), Context.MODE_PRIVATE,
                        R.xml.preferences, true);
            } else {
                PreferenceManager.setDefaultValues(this, R.xml.preferences, false);
            }
        }

        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        adHandler = new AdHandler(this);
        adHandler.init();

        mDrawerLayout = (DrawerLayout) findViewById(R.id.drawer_layout);
        mDrawerList = (StickyListHeadersListView) findViewById(R.id.left_drawer);
        mToolbar = setupToolbar(false);
        mToolbar.addView(getLayoutInflater().inflate(R.layout.custom_title, mToolbar, false));
        if (mDrawerLayout != null) {
            mDrawerToggle = new ActionBarDrawerToggle(this, mDrawerLayout, mToolbar, R.string.drawer_open,
                    R.string.drawer_close) {

                /**
                 * Called when a drawer has settled in a completely closed state.
                 */
                public void onDrawerClosed(View view) {
                    super.onDrawerClosed(view);
                    TransactionList tl = getCurrentFragment();
                    if (tl != null)
                        tl.onDrawerClosed();
                    //ActivityCompat.invalidateOptionsMenu(MyExpenses.this); // creates call to onPrepareOptionsMenu()
                }

                /**
                 * Called when a drawer has settled in a completely open state.
                 */
                public void onDrawerOpened(View drawerView) {
                    super.onDrawerOpened(drawerView);
                    TransactionList tl = getCurrentFragment();
                    if (tl != null)
                        tl.onDrawerOpened();
                    //ActivityCompat.invalidateOptionsMenu(MyExpenses.this); // creates call to onPrepareOptionsMenu()
                }

                @Override
                public void onDrawerSlide(View drawerView, float slideOffset) {
                    super.onDrawerSlide(drawerView, 0); // this disables the animation
                }
            };

            // Set the drawer toggle as the DrawerListener
            mDrawerLayout.addDrawerListener(mDrawerToggle);
        }
        String[] from = new String[] { KEY_DESCRIPTION, KEY_LABEL, KEY_OPENING_BALANCE, KEY_SUM_INCOME,
                KEY_SUM_EXPENSES, KEY_SUM_TRANSFERS, KEY_CURRENT_BALANCE, KEY_TOTAL, KEY_CLEARED_TOTAL,
                KEY_RECONCILED_TOTAL };
        // and an array of the fields we want to bind those fields to
        int[] to = new int[] { R.id.description, R.id.label, R.id.opening_balance, R.id.sum_income,
                R.id.sum_expenses, R.id.sum_transfer, R.id.current_balance, R.id.total, R.id.cleared_total,
                R.id.reconciled_total };
        mDrawerListAdapter = new MyGroupedAdapter(this, R.layout.account_row, null, from, to, 0);

        Toolbar accountsMenu = (Toolbar) findViewById(R.id.accounts_menu);
        accountsMenu.setTitle(R.string.pref_manage_accounts_title);
        accountsMenu.inflateMenu(R.menu.accounts);
        accountsMenu.inflateMenu(R.menu.sort);

        //Sort submenu
        MenuItem menuItem = accountsMenu.getMenu().findItem(R.id.SORT_COMMAND);
        MenuItemCompat.setShowAsAction(menuItem, MenuItemCompat.SHOW_AS_ACTION_NEVER);
        sortMenu = menuItem.getSubMenu();
        sortMenu.findItem(R.id.SORT_CUSTOM_COMMAND).setVisible(true);

        //Grouping submenu
        SubMenu groupingMenu = accountsMenu.getMenu().findItem(R.id.GROUPING_ACCOUNTS_COMMAND).getSubMenu();
        AccountGrouping accountGrouping;
        try {
            accountGrouping = AccountGrouping.valueOf(PrefKey.ACCOUNT_GROUPING.getString("TYPE"));
        } catch (IllegalArgumentException e) {
            accountGrouping = AccountGrouping.TYPE;
        }
        MenuItem activeItem;
        switch (accountGrouping) {
        case CURRENCY:
            activeItem = groupingMenu.findItem(R.id.GROUPING_ACCOUNTS_CURRENCY_COMMAND);
            break;
        case NONE:
            activeItem = groupingMenu.findItem(R.id.GROUPING_ACCOUNTS_NONE_COMMAND);
            break;
        default:
            activeItem = groupingMenu.findItem(R.id.GROUPING_ACCOUNTS_TYPE_COMMAND);
            break;
        }
        activeItem.setChecked(true);

        accountsMenu.setOnMenuItemClickListener(new Toolbar.OnMenuItemClickListener() {
            @Override
            public boolean onMenuItemClick(MenuItem item) {
                return handleSortOption(item) || handleAccountsGrouping(item)
                        || dispatchCommand(item.getItemId(), null);
            }
        });

        mDrawerList.setAdapter(mDrawerListAdapter);
        mDrawerList.setAreHeadersSticky(false);
        mDrawerList.setOnItemClickListener(new OnItemClickListener() {
            @Override
            public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
                if (mAccountId != id) {
                    moveToPosition(position);
                    ((SimpleCursorAdapter) mDrawerListAdapter).notifyDataSetChanged();
                    closeDrawer();
                }
            }
        });

        requireFloatingActionButtonWithContentDescription(Utils.concatResStrings(this, ". ",
                R.string.menu_create_transaction, R.string.menu_create_transfer, R.string.menu_create_split));
        if (prev_version == -1) {
            getSupportActionBar().hide();
            initialSetup();
            return;
        }
        if (savedInstanceState != null) {
            mExportFormat = savedInstanceState.getString("exportFormat");
            mAccountId = savedInstanceState.getLong(KEY_ACCOUNTID, 0L);
        } else {
            Bundle extras = getIntent().getExtras();
            if (extras != null) {
                mAccountId = Utils.getFromExtra(extras, KEY_ROWID, 0);
                idFromNotification = extras.getLong(KEY_TRANSACTIONID, 0);
                //detail fragment from notification should only be shown upon first instantiation from notification
                if (idFromNotification != 0) {
                    FragmentManager fm = getSupportFragmentManager();
                    if (fm.findFragmentByTag(TransactionDetailFragment.class.getName()) == null) {
                        TransactionDetailFragment.newInstance(idFromNotification).show(fm,
                                TransactionDetailFragment.class.getName());
                        getIntent().removeExtra(KEY_TRANSACTIONID);
                    }
                }
            }
        }
        if (mAccountId == 0)
            mAccountId = PrefKey.CURRENT_ACCOUNT.getLong(0L);
        setup();
    }

    private void initialSetup() {
        FragmentManager fm = getSupportFragmentManager();
        if (fm.findFragmentByTag(ProtectionDelegate.ASYNC_TAG) == null) {
            fm.beginTransaction().add(WelcomeDialogFragment.newInstance(), "WELCOME")
                    .add(TaskExecutionFragment.newInstance(TaskExecutionFragment.TASK_REQUIRE_ACCOUNT,
                            new Long[] { 0L }, null), ProtectionDelegate.ASYNC_TAG)
                    .commit();
            setupComplete = false;
        }
    }

    private void setup() {
        newVersionCheck();
        //SharedPreferencesCompat.apply(mSettings.edit().remove("restoreOnInstallAsked"));
        Resources.Theme theme = getTheme();
        TypedValue margin = new TypedValue();
        theme.resolveAttribute(R.attr.pageMargin, margin, true);
        mViewPagerAdapter = new MyViewPagerAdapter(this, getSupportFragmentManager(), null);
        myPager = (ViewPager) this.findViewById(R.id.viewpager);
        myPager.setAdapter(this.mViewPagerAdapter);
        myPager.setOnPageChangeListener(this);
        myPager.setPageMargin((int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, 10,
                getResources().getDisplayMetrics()));
        myPager.setPageMarginDrawable(margin.resourceId);
        mManager = getSupportLoaderManager();
        mManager.initLoader(ACCOUNTS_CURSOR, null, this);
    }

    private void moveToPosition(int position) {
        if (myPager.getCurrentItem() == position)
            setCurrentAccount(position);
        else
            myPager.setCurrentItem(position, false);
    }

    @Override
    public boolean onPrepareOptionsMenu(Menu menu) {
        super.onPrepareOptionsMenu(menu);
        MenuItem balanceItem = menu.findItem(R.id.BALANCE_COMMAND);
        if (balanceItem != null) {
            boolean showBalanceCommand = false;
            if (mAccountId > 0 && mAccountsCursor != null && !mAccountsCursor.isClosed()
                    && mAccountsCursor.moveToPosition(mCurrentPosition)) {
                try {
                    if (AccountType.valueOf(mAccountsCursor
                            .getString(mAccountsCursor.getColumnIndexOrThrow(KEY_TYPE))) != AccountType.CASH) {
                        showBalanceCommand = true;
                    }
                } catch (IllegalArgumentException ex) {
                    /*aggregate*/}
            }
            Utils.menuItemSetEnabledAndVisible(balanceItem, showBalanceCommand);
        }

        MenuItem groupingItem = menu.findItem(R.id.GROUPING_COMMAND);
        if (groupingItem != null) {
            SubMenu groupingMenu = groupingItem.getSubMenu();

            Account account = Account.getInstanceFromDb(mAccountId);
            if (account != null) {
                Utils.configureGroupingMenu(groupingMenu, account.grouping);
            }
        }
        return true;
    }

    @Override
    public boolean onCreateOptionsMenu(Menu menu) {
        MenuInflater inflater = getMenuInflater();
        inflater.inflate(R.menu.expenses, menu);
        inflater.inflate(R.menu.grouping, menu);
        super.onCreateOptionsMenu(menu);
        return true;
    }

    /* (non-Javadoc)
    * check if we should show one of the reminderDialogs
    * @see android.app.Activity#onActivityResult(int, int, android.content.Intent)
    */
    @Override
    protected void onActivityResult(int requestCode, int resultCode, Intent intent) {
        super.onActivityResult(requestCode, resultCode, intent);
        if (requestCode == EDIT_TRANSACTION_REQUEST && resultCode == RESULT_OK) {
            long nextReminder;
            sequenceCount = intent.getLongExtra(ContribInfoDialogFragment.KEY_SEQUENCE_COUNT, 0);
            if (Utils.IS_FLAVOURED) {
                nextReminder = PrefKey.NEXT_REMINDER_RATE.getLong(TRESHOLD_REMIND_RATE);
                if (nextReminder != -1 && sequenceCount >= nextReminder) {
                    RemindRateDialogFragment f = new RemindRateDialogFragment();
                    f.setCancelable(false);
                    f.show(getSupportFragmentManager(), "REMIND_RATE");
                    return;
                }
            }
            if (!MyApplication.getInstance().getLicenceHandler().isContribEnabled()) {
                nextReminder = PrefKey.NEXT_REMINDER_CONTRIB.getLong(TRESHOLD_REMIND_CONTRIB);
                if (nextReminder != -1 && sequenceCount >= nextReminder) {
                    CommonCommands.showContribInfoDialog(this, sequenceCount);
                    return;
                }
            }
            adHandler.onEditTransactionResult();
        }
        if (requestCode == CREATE_ACCOUNT_REQUEST && resultCode == RESULT_OK) {
            mAccountId = intent.getLongExtra(KEY_ROWID, 0);
        }
    }

    public void addFilterCriteria(Integer id, Criteria c) {
        TransactionList tl = getCurrentFragment();
        if (tl != null) {
            tl.addFilterCriteria(id, c);
        }
    }

    /**
     * start ExpenseEdit Activity for a new transaction/transfer/split
     * Originally the form for transaction is rendered, user can change from spinner in toolbar
     */
    private void createRow() {
        Intent i = new Intent(this, ExpenseEdit.class);
        i.putExtra(MyApplication.KEY_OPERATION_TYPE, MyExpenses.TYPE_TRANSACTION);
        //if we are called from an aggregate cursor, we also hand over the currency
        if (mAccountId < 0 && mAccountsCursor != null && mAccountsCursor.moveToPosition(mCurrentPosition)) {
            i.putExtra(KEY_CURRENCY, mAccountsCursor.getString(columnIndexCurrency));
        } else {
            //if accountId is 0 ExpenseEdit will retrieve the first entry from the accounts table
            i.putExtra(KEY_ACCOUNTID, mAccountId);
        }
        startActivityForResult(i, EDIT_TRANSACTION_REQUEST);
    }

    /**
     * @param command
     * @param tag
     * @return true if command has been handled
     */
    public boolean dispatchCommand(int command, Object tag) {
        Intent i;
        TransactionList tl;
        switch (command) {
        case R.id.DISTRIBUTION_COMMAND:
            tl = getCurrentFragment();
            if (tl != null && tl.mappedCategories) {
                contribFeatureRequested(ContribFeature.DISTRIBUTION, null);
            } else {
                MessageDialogFragment
                        .newInstance(0, R.string.dialog_command_disabled_distribution,
                                MessageDialogFragment.Button.okButton(), null, null)
                        .show(getSupportFragmentManager(), "BUTTON_DISABLED_INFO");
            }
            return true;
        case R.id.CREATE_COMMAND:
            createRow();
            return true;
        case R.id.BALANCE_COMMAND:
            tl = getCurrentFragment();
            if (tl != null && hasCleared()) {
                mAccountsCursor.moveToPosition(mCurrentPosition);
                Currency currency = Utils.getSaveInstance(mAccountsCursor.getString(columnIndexCurrency));
                Bundle bundle = new Bundle();
                bundle.putLong(KEY_ROWID, mAccountsCursor.getLong(columnIndexRowId));
                bundle.putString(KEY_LABEL, mAccountsCursor.getString(columnIndexLabel));
                bundle.putString(KEY_RECONCILED_TOTAL, Utils.formatCurrency(new Money(currency,
                        mAccountsCursor.getLong(mAccountsCursor.getColumnIndex(KEY_RECONCILED_TOTAL)))));
                bundle.putString(KEY_CLEARED_TOTAL, Utils.formatCurrency(new Money(currency,
                        mAccountsCursor.getLong(mAccountsCursor.getColumnIndex(KEY_CLEARED_TOTAL)))));
                BalanceDialogFragment.newInstance(bundle).show(getSupportFragmentManager(), "BALANCE_ACCOUNT");
            } else {
                MessageDialogFragment
                        .newInstance(0, R.string.dialog_command_disabled_balance,
                                MessageDialogFragment.Button.okButton(), null, null)
                        .show(getSupportFragmentManager(), "BUTTON_DISABLED_INFO");
            }
            return true;
        case R.id.RESET_COMMAND:
            tl = getCurrentFragment();
            if (tl != null && tl.hasItems) {
                Result appDirStatus = Utils.checkAppDir();
                if (appDirStatus.success) {
                    ExportDialogFragment.newInstance(mAccountId, tl.isFiltered())
                            .show(this.getSupportFragmentManager(), "WARNING_RESET");
                } else {
                    Toast.makeText(getBaseContext(), appDirStatus.print(this), Toast.LENGTH_LONG).show();
                }
            } else {
                MessageDialogFragment
                        .newInstance(0, R.string.dialog_command_disabled_reset_account,
                                MessageDialogFragment.Button.okButton(), null, null)
                        .show(getSupportFragmentManager(), "BUTTON_DISABLED_INFO");
            }
            return true;
        case R.id.BACKUP_COMMAND:
            startActivity(new Intent("myexpenses.intent.backup"));
            return true;
        /*    case R.id.HANDLE_RESTORE_ON_INSTALL_COMMAND:
              if ((Boolean) tag) {
                if (MyApplication.backupRestore()) {
                  //if we have successfully restored, we relaunch in order to force password check if needed
                  i = getBaseContext().getPackageManager()
          .getLaunchIntentForPackage( getBaseContext().getPackageName() );
                  i.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
                  startActivity(i);
                  break;
                }
              }
              initialSetup();
              return true;*/
        case R.id.REMIND_NO_RATE_COMMAND:
            PrefKey.NEXT_REMINDER_RATE.putLong(-1);
            return true;
        case R.id.REMIND_LATER_RATE_COMMAND:
            PrefKey.NEXT_REMINDER_RATE.putLong(sequenceCount + TRESHOLD_REMIND_RATE);
            return true;
        case R.id.HELP_COMMAND_DRAWER:
            i = new Intent(this, Help.class);
            i.putExtra(Help.KEY_CONTEXT, "NavigationDrawer");
            //for result is needed since it allows us to inspect the calling activity
            startActivity(i);
            return true;
        case R.id.HELP_COMMAND:
            setHelpVariant();
            break;
        case R.id.MANAGE_PLANS_COMMAND:
            i = new Intent(this, ManageTemplates.class);
            startActivity(i);
            return true;
        case R.id.CREATE_ACCOUNT_COMMAND:
            if (mAccountCount == 0) {
                Toast.makeText(this, R.string.account_list_not_yet_loaded, Toast.LENGTH_LONG).show();
            }
            //we need the accounts to be loaded in order to evaluate if the limit has been reached
            else if (ContribFeature.ACCOUNTS_UNLIMITED.hasAccess() || mAccountCount < 5) {
                closeDrawer();
                i = new Intent(this, AccountEdit.class);
                if (tag != null)
                    i.putExtra(KEY_CURRENCY, (String) tag);
                startActivityForResult(i, CREATE_ACCOUNT_REQUEST);
            } else {
                CommonCommands.showContribDialog(this, ContribFeature.ACCOUNTS_UNLIMITED, null);
            }
            return true;
        case R.id.DELETE_ACCOUNT_COMMAND_DO:
            //reset mAccountId will prevent the now defunct account being used in an immediately following "new transaction"
            mAccountId = 0;
            startTaskExecution(TaskExecutionFragment.TASK_DELETE_ACCOUNT, new Long[] { (Long) tag }, null, 0);
            return true;
        case R.id.SHARE_COMMAND:
            i = new Intent();
            i.setAction(Intent.ACTION_SEND);
            i.putExtra(Intent.EXTRA_TEXT, getString(R.string.tell_a_friend_message, BuildConfig.PLATTFORM));
            i.setType("text/plain");
            startActivity(Intent.createChooser(i, getResources().getText(R.string.menu_share)));
            return true;
        case R.id.CANCEL_CALLBACK_COMMAND:
            finishActionMode();
            return true;
        case R.id.OPEN_PDF_COMMAND:
            i = new Intent();
            i.setAction(Intent.ACTION_VIEW);
            Uri data = Uri.parse((String) tag);
            Log.d("DEBUG", data.toString());
            i.setDataAndType(data, "application/pdf");
            if (!Utils.isIntentAvailable(this, i)) {
                Toast.makeText(this, R.string.no_app_handling_pdf_available, Toast.LENGTH_LONG).show();
            } else {
                startActivity(i);
            }
            return true;
        case R.id.QUIT_COMMAND:
            finish();
            return true;
        case R.id.EDIT_ACCOUNT_COMMAND:
            closeDrawer();
            int position = (Integer) tag;
            mAccountsCursor.moveToPosition(position);
            long accountId = mAccountsCursor.getLong(columnIndexRowId);
            if (accountId > 0) { //do nothing if accidentally we are positioned at an aggregate account
                i = new Intent(this, AccountEdit.class);
                i.putExtra(KEY_ROWID, accountId);
                startActivityForResult(i, EDIT_ACCOUNT_REQUEST);
            }
            return true;
        case R.id.DELETE_ACCOUNT_COMMAND:
            closeDrawer();
            position = (Integer) tag;
            mAccountsCursor.moveToPosition(position);
            accountId = mAccountsCursor.getLong(columnIndexRowId);
            //do nothing if accidentally we are positioned at an aggregate account or try to delete the last account
            if (mAccountsCursor.getCount() > 1 && accountId > 0) {
                MessageDialogFragment.newInstance(R.string.dialog_title_warning_delete_account,
                        getString(R.string.warning_delete_account, mAccountsCursor.getString(columnIndexLabel)),
                        new MessageDialogFragment.Button(R.string.menu_delete, R.id.DELETE_ACCOUNT_COMMAND_DO,
                                accountId),
                        null, MessageDialogFragment.Button.noButton())
                        .show(getSupportFragmentManager(), "DELETE_ACCOUNT");
            }
            return true;
        }
        return super.dispatchCommand(command, tag);
    }

    private void closeDrawer() {
        if (mDrawerLayout != null)
            mDrawerLayout.closeDrawers();
    }

    private class MyViewPagerAdapter extends CursorFragmentPagerAdapter {
        public MyViewPagerAdapter(Context context, FragmentManager fm, Cursor cursor) {
            super(context, fm, cursor);
        }

        public String getFragmentName(int currentPosition) {
            return FragmentPagerAdapter.makeFragmentName(R.id.viewpager, getItemId(currentPosition));
        }

        @Override
        public Fragment getItem(Context context, Cursor cursor) {
            long accountId = cursor.getLong(columnIndexRowId);
            //calling the constructors, puts the objects into the cache from where the fragment can
            //retrieve it, without needing to create a new cursor
            Account.fromCacheOrFromCursor(cursor);
            return TransactionList.newInstance(accountId);
        }
    }

    @Override
    public void onPageSelected(int position) {
        finishActionMode();
        mCurrentPosition = position;
        setCurrentAccount(position);
    }

    public void finishActionMode() {
        if (mCurrentPosition != -1 && Build.VERSION.SDK_INT >= 11) {
            ContextualActionBarFragment f = (ContextualActionBarFragment) getSupportFragmentManager()
                    .findFragmentByTag(mViewPagerAdapter.getFragmentName(mCurrentPosition));
            if (f != null)
                f.finishActionMode();
        }
    }

    @SuppressWarnings("incomplete-switch")
    @Override
    public void contribFeatureCalled(ContribFeature feature, Serializable tag) {
        switch (feature) {
        case DISTRIBUTION:
            Account a = Account.getInstanceFromDb(mAccountId);
            recordUsage(feature);
            Intent i = new Intent(this, ManageCategories.class);
            i.setAction("myexpenses.intent.distribution");
            i.putExtra(KEY_ACCOUNTID, mAccountId);
            if (tag != null) {
                int year = (int) ((Long) tag / 1000);
                int groupingSecond = (int) ((Long) tag % 1000);
                i.putExtra("grouping", a != null ? a.grouping : Grouping.NONE);
                i.putExtra("groupingYear", year);
                i.putExtra("groupingSecond", groupingSecond);
            }
            startActivity(i);
            break;
        case SPLIT_TRANSACTION:
            if (tag != null) {
                startTaskExecution(TaskExecutionFragment.TASK_SPLIT, (Object[]) tag, null, 0);
            }
            break;
        case PRINT:
            TransactionList tl = getCurrentFragment();
            if (tl != null) {
                Bundle args = new Bundle();
                args.putSparseParcelableArray(TransactionList.KEY_FILTER, tl.getFilterCriteria());
                args.putLong(KEY_ROWID, mAccountId);
                getSupportFragmentManager().beginTransaction()
                        .add(TaskExecutionFragment.newInstancePrint(args), ProtectionDelegate.ASYNC_TAG)
                        .add(ProgressDialogFragment.newInstance(R.string.progress_dialog_printing),
                                ProtectionDelegate.PROGRESS_TAG)
                        .commit();
            }
            break;
        }
    }

    @Override
    public void contribFeatureNotCalled(ContribFeature feature) {
    }

    @Override
    public Loader<Cursor> onCreateLoader(int id, Bundle bundle) {
        switch (id) {
        case ACCOUNTS_CURSOR:
            Uri.Builder builder = TransactionProvider.ACCOUNTS_URI.buildUpon();
            builder.appendQueryParameter(TransactionProvider.QUERY_PARAMETER_MERGE_CURRENCY_AGGREGATES, "1");
            return new CursorLoader(this, builder.build(), null, null, null, null) {
                @Override
                public Cursor loadInBackground() {
                    try {
                        return super.loadInBackground();
                    } catch (TransactionDatabase.SQLiteDowngradeFailedException
                            | TransactionDatabase.SQLiteUpgradeFailedException e) {
                        AcraHelper.report(e);
                        String msg = e instanceof TransactionDatabase.SQLiteDowngradeFailedException
                                ? ("Database cannot be downgraded from a newer version. Please either uninstall MyExpenses, "
                                        + "before reinstalling, or upgrade to a new version.")
                                : "Database upgrade failed. Please contact support@myexpenses.mobi !";
                        MessageDialogFragment f = MessageDialogFragment.newInstance(0, msg,
                                new MessageDialogFragment.Button(android.R.string.ok, R.id.QUIT_COMMAND, null),
                                null, null);
                        f.setCancelable(false);
                        f.show(getSupportFragmentManager(), "DOWN_OR_UP_GRADE");
                        return null;
                    } catch (SQLiteException e) {
                        String msg = String.format(
                                "Loading of transactions failed (%s). Probably the sum of the entered amounts exceeds the storage limit !",
                                e.getMessage());
                        MessageDialogFragment f = MessageDialogFragment.newInstance(0, msg,
                                new MessageDialogFragment.Button(android.R.string.ok, R.id.QUIT_COMMAND, null),
                                null, null);
                        f.setCancelable(false);
                        f.show(getSupportFragmentManager(), "SQLITE_EXCEPTION");
                        return null;
                    }
                }
            };
        }
        return null;
    }

    /**
     * set the Current account to the one in the requested position of mAccountsCursor
     *
     * @param position
     */
    private void setCurrentAccount(int position) {
        mAccountsCursor.moveToPosition(position);
        long newAccountId = mAccountsCursor.getLong(columnIndexRowId);
        if (mAccountId != newAccountId) {
            PrefKey.CURRENT_ACCOUNT.putLong(newAccountId);
        }
        int color = newAccountId < 0 ? colorAggregate : mAccountsCursor.getInt(columnIndexColor);
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
            Window window = getWindow();
            //noinspection InlinedApi
            window.clearFlags(WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS);
            //noinspection InlinedApi
            window.addFlags(WindowManager.LayoutParams.FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS);
            int color700 = Utils.get700Tint(color);
            window.setStatusBarColor(color700);
            if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
                //noinspection InlinedApi
                getWindow().getDecorView().setSystemUiVisibility(
                        Utils.isBrightColor(color700) ? View.SYSTEM_UI_FLAG_LIGHT_STATUS_BAR : 0);
            }
        }
        Utils.setBackgroundTintListOnFab(floatingActionButton, color);
        mAccountId = newAccountId;
        setBalance();
        mDrawerList.setItemChecked(position, true);
        supportInvalidateOptionsMenu();
    }

    @Override
    public void onLoadFinished(Loader<Cursor> loader, Cursor cursor) {
        switch (loader.getId()) {
        case ACCOUNTS_CURSOR:
            //we postpone this until cursor is loaded, because prefkey is updated in migration to db schema 56
            Utils.configureSortMenu(sortMenu, PrefKey.SORT_ORDER_ACCOUNTS.getString("USAGES"));
            mAccountCount = 0;
            mAccountsCursor = cursor;
            if (mAccountsCursor == null) {
                return;
            }
            //when account grouping is changed in setting, cursor is reloaded,
            //and we need to refresh the value here
            try {
                mAccountGrouping = AccountGrouping.valueOf(PrefKey.ACCOUNT_GROUPING.getString("TYPE"));
            } catch (IllegalArgumentException e) {
                mAccountGrouping = AccountGrouping.TYPE;
            }
            ((SimpleCursorAdapter) mDrawerListAdapter).swapCursor(mAccountsCursor);
            //swaping the cursor is altering the accountId, if the
            //sort order has changed, but we want to move to the same account as before
            long cacheAccountId = mAccountId;
            mViewPagerAdapter.swapCursor(cursor);
            mAccountId = cacheAccountId;
            if (!indexesCalculated) {
                columnIndexRowId = mAccountsCursor.getColumnIndex(KEY_ROWID);
                columnIndexColor = mAccountsCursor.getColumnIndex(KEY_COLOR);
                columnIndexCurrency = mAccountsCursor.getColumnIndex(KEY_CURRENCY);
                columnIndexDescription = mAccountsCursor.getColumnIndex(KEY_DESCRIPTION);
                columnIndexLabel = mAccountsCursor.getColumnIndex(KEY_LABEL);
                indexesCalculated = true;
            }
            if (mAccountsCursor.moveToFirst()) {
                int position = 0;
                while (mAccountsCursor.isAfterLast() == false) {
                    long accountId = mAccountsCursor.getLong(columnIndexRowId);
                    if (accountId == mAccountId) {
                        position = mAccountsCursor.getPosition();
                    }
                    if (accountId > 0) {
                        mAccountCount++;
                    }
                    mAccountsCursor.moveToNext();
                }
                mCurrentPosition = position;
                moveToPosition(mCurrentPosition);
                //should be triggered through onPageSelected
                //setCurrentAccount(mCurrentPosition);
            }
        }
    }

    @Override
    public void onLoaderReset(Loader<Cursor> arg0) {
        if (arg0.getId() == ACCOUNTS_CURSOR) {
            mViewPagerAdapter.swapCursor(null);
            ((SimpleCursorAdapter) mDrawerListAdapter).swapCursor(null);
            mCurrentPosition = -1;
            mAccountsCursor = null;
        }
    }

    @Override
    public void onPageScrollStateChanged(int arg0) {
        // TODO Auto-generated method stub

    }

    @Override
    public void onPageScrolled(int arg0, float arg1, int arg2) {
        // TODO Auto-generated method stub

    }

    @Override
    public void onFinishEditDialog(Bundle args) {
        String result = args.getString(EditTextDialog.KEY_RESULT);
        switch (args.getInt(EditTextDialog.KEY_REQUEST_CODE)) {
        case TEMPLATE_TITLE_REQUEST:
            if ((new Template(Transaction.getInstanceFromDb(args.getLong(KEY_ROWID)), result)).save() == null) {
                Toast.makeText(getBaseContext(), "Error while saving template", Toast.LENGTH_LONG).show();
            } else {
                Toast.makeText(getBaseContext(), getString(R.string.template_create_success, result),
                        Toast.LENGTH_LONG).show();
            }
            finishActionMode();
            break;
        case FILTER_COMMENT_REQUEST:
            addFilterCriteria(R.id.FILTER_COMMENT_COMMAND, new CommentCriteria(result));
            break;
        }
    }

    @Override
    public void onCancelEditDialog() {
        finishActionMode();
    }

    @Override
    public void onPostExecute(int taskId, Object o) {
        Integer successCount;
        String msg;
        super.onPostExecute(taskId, o);
        switch (taskId) {
        /*    case TaskExecutionFragment.TASK_CLONE:
              successCount = (Integer) o;
              msg = successCount == 0 ?  getString(R.string.clone_transaction_error) :
                getResources().getQuantityString(R.plurals.clone_transaction_success, successCount, successCount);
              Toast.makeText(this,msg, Toast.LENGTH_LONG).show();
              break;*/
        case TaskExecutionFragment.TASK_SPLIT:
            successCount = (Integer) o;
            msg = successCount == 0 ? getString(R.string.split_transaction_error)
                    : getResources().getQuantityString(R.plurals.split_transaction_success, successCount,
                            successCount);
            Toast.makeText(this, msg, Toast.LENGTH_LONG).show();
            break;
        case TaskExecutionFragment.TASK_REQUIRE_ACCOUNT:
            setupComplete = true;
            getSupportActionBar().show();
            FragmentManager fm = getSupportFragmentManager();
            setup();
            WelcomeDialogFragment wdf = ((WelcomeDialogFragment) fm.findFragmentByTag("WELCOME"));
            if (wdf != null) {
                wdf.setSetupComplete();
            }
            break;
        case TaskExecutionFragment.TASK_EXPORT:
            ArrayList<Uri> files = (ArrayList<Uri>) o;
            if (files != null && !files.isEmpty())
                Utils.share(this, files, PrefKey.SHARE_TARGET.getString("").trim(),
                        "text/" + mExportFormat.toLowerCase(Locale.US));
            break;
        case TaskExecutionFragment.TASK_PRINT:
            Result result = (Result) o;
            if (result.success) {
                recordUsage(ContribFeature.PRINT);
                MessageDialogFragment f = MessageDialogFragment.newInstance(0,
                        getString(result.getMessage(), FileUtils.getPath(this, (Uri) result.extra[0])),
                        new MessageDialogFragment.Button(R.string.menu_open, R.id.OPEN_PDF_COMMAND,
                                ((Uri) result.extra[0]).toString()),
                        null, MessageDialogFragment.Button.nullButton(android.R.string.cancel));
                f.setCancelable(false);
                f.show(getSupportFragmentManager(), "PRINT_RESULT");
            } else {
                Toast.makeText(this, result.print(this), Toast.LENGTH_LONG).show();
            }
            break;
        }
    }

    public boolean hasExported() {
        //in case we are called before the accounts cursor is loaded, we return false
        if (mAccountsCursor == null || mAccountsCursor.getCount() == 0)
            return false;
        mAccountsCursor.moveToPosition(mCurrentPosition);
        return mAccountsCursor.getInt(mAccountsCursor.getColumnIndexOrThrow(KEY_HAS_EXPORTED)) > 0;
    }

    private boolean hasCleared() {
        //in case we are called before the accounts cursor is loaded, we return false
        if (mAccountsCursor == null || mAccountsCursor.getCount() == 0)
            return false;
        mAccountsCursor.moveToPosition(mCurrentPosition);
        return mAccountsCursor.getInt(mAccountsCursor.getColumnIndexOrThrow(KEY_HAS_CLEARED)) > 0;
    }

    private void setConvertedAmount(TextView tv, Currency currency) {
        tv.setText(Utils.convAmount(tv.getText().toString(), currency));
    }

    @Override
    protected void onPostCreate(Bundle savedInstanceState) {
        super.onPostCreate(savedInstanceState);
        // Sync the toggle state after onRestoreInstanceState has occurred.
        if (mDrawerToggle != null)
            mDrawerToggle.syncState();
    }

    @Override
    public void onConfigurationChanged(Configuration newConfig) {
        super.onConfigurationChanged(newConfig);
        if (mDrawerToggle != null)
            mDrawerToggle.onConfigurationChanged(newConfig);
    }

    @Override
    public boolean onOptionsItemSelected(MenuItem item) {
        // Pass the event to ActionBarDrawerToggle, if it returns
        // true, then it has handled the app icon touch event
        if (mDrawerToggle != null && mDrawerToggle.onOptionsItemSelected(item)) {
            return true;
        }
        // Handle your other action bar items...

        if (handleGrouping(item))
            return true;

        return super.onOptionsItemSelected(item);
    }

    private void setBalance() {
        long balance = mAccountsCursor.getLong(mAccountsCursor.getColumnIndex(KEY_CURRENT_BALANCE));
        mCurrentBalance = Utils.formatCurrency(
                new Money(Utils.getSaveInstance(mAccountsCursor.getString(columnIndexCurrency)), balance));
        TextView balanceTextView = (TextView) mToolbar.findViewById(R.id.end);
        balanceTextView.setTextColor(balance < 0 ? colorExpense : colorIncome);
        balanceTextView.setText(mCurrentBalance);
    }

    public void setTitle(String title) {
        ((TextView) mToolbar.findViewById(R.id.action_bar_title)).setText(title);
    }

    public TransactionList getCurrentFragment() {
        if (mViewPagerAdapter == null)
            return null;
        return (TransactionList) getSupportFragmentManager()
                .findFragmentByTag(mViewPagerAdapter.getFragmentName(mCurrentPosition));
    }

    public class MyGroupedAdapter extends SimpleCursorAdapter implements StickyListHeadersAdapter {
        public static final int CARD_ELEVATION_DIP = 24;
        LayoutInflater inflater;

        public MyGroupedAdapter(Context context, int layout, Cursor c, String[] from, int[] to, int flags) {
            super(context, layout, c, from, to, flags);
            inflater = LayoutInflater.from(MyExpenses.this);
        }

        @Override
        public View getHeaderView(int position, View convertView, ViewGroup parent) {
            if (convertView == null) {
                convertView = inflater.inflate(R.layout.accounts_header, parent, false);
            }
            Cursor c = getCursor();
            c.moveToPosition(position);
            long headerId = getHeaderId(position);
            TextView sectionLabelTV = (TextView) convertView.findViewById(R.id.sectionLabel);
            switch (mAccountGrouping) {
            case CURRENCY:
                sectionLabelTV.setText(CurrencyEnum.valueOf(c.getString(columnIndexCurrency)).toString());
                break;
            case NONE:
                sectionLabelTV
                        .setText(headerId == 0 ? R.string.pref_manage_accounts_title : R.string.menu_aggregates);
                break;
            case TYPE:
                int headerRes;
                if (headerId == AccountType.values().length) {
                    headerRes = R.string.menu_aggregates;
                } else {
                    headerRes = AccountType.values()[(int) headerId].toStringResPlural();
                }
                sectionLabelTV.setText(headerRes);
            default:
                break;

            }
            return convertView;
        }

        @Override
        public long getHeaderId(int position) {
            Cursor c = getCursor();
            c.moveToPosition(position);
            switch (mAccountGrouping) {
            case CURRENCY:
                return CurrencyEnum.valueOf(c.getString(columnIndexCurrency)).ordinal();
            case NONE:
                return c.getLong(columnIndexRowId) > 0 ? 0 : 1;
            case TYPE:
                AccountType type;
                try {
                    type = AccountType.valueOf(c.getString(c.getColumnIndexOrThrow(KEY_TYPE)));
                    return type.ordinal();
                } catch (IllegalArgumentException ex) {
                    return AccountType.values().length;
                }
            }
            return 0;
        }

        @Override
        public View getView(final int position, View convertView, ViewGroup parent) {
            View row = super.getView(position, convertView, parent);
            final Cursor c = getCursor();
            c.moveToPosition(position);

            View v = row.findViewById(R.id.color1);
            TextView labelTv = (TextView) row.findViewById(R.id.label);
            final View accountMenu = row.findViewById(R.id.account_menu);

            Currency currency = Utils.getSaveInstance(c.getString(columnIndexCurrency));
            final long rowId = c.getLong(columnIndexRowId);
            long sum_transfer = c.getLong(c.getColumnIndex(KEY_SUM_TRANSFERS));

            boolean isHighlighted = rowId == mAccountId;
            boolean has_future = c.getInt(c.getColumnIndex(KEY_HAS_FUTURE)) > 0;
            final boolean isAggregate = rowId < 0;
            final int count = c.getCount();
            boolean hide_cr;
            int colorInt;

            ((CardView) row.findViewById(R.id.card))
                    .setCardElevation(isHighlighted ? TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP,
                            CARD_ELEVATION_DIP, getResources().getDisplayMetrics()) : 0);
            labelTv.setTypeface(Typeface.create(labelTv.getTypeface(), Typeface.NORMAL),
                    isHighlighted ? Typeface.BOLD : Typeface.NORMAL);
            if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
                row.findViewById(R.id.selected_indicator).setVisibility(isHighlighted ? View.VISIBLE : View.GONE);
            }
            if (isAggregate) {
                accountMenu.setVisibility(View.INVISIBLE);
                accountMenu.setOnClickListener(null);
            } else {
                accountMenu.setVisibility(View.VISIBLE);
                boolean upVisible = false, downVisible = false;
                if (PrefKey.SORT_ORDER_ACCOUNTS.getString(SORT_ORDER_USAGES).equals(SORT_ORDER_CUSTOM)) {
                    if (position > 0 && getHeaderId(position - 1) == getHeaderId(position)) {
                        getCursor().moveToPosition(position - 1);
                        if (c.getLong(columnIndexRowId) > 0)
                            upVisible = true; //ignore if previous is aggregate
                    }
                    if (position + 1 < getCount() && getHeaderId(position + 1) == getHeaderId(position)) {
                        getCursor().moveToPosition(position + 1);
                        if (c.getLong(columnIndexRowId) > 0)
                            downVisible = true;
                    }
                    getCursor().moveToPosition(position);
                }
                final boolean finalUpVisible = upVisible, finalDownVisible = downVisible;
                accountMenu.setOnClickListener(new OnClickListener() {
                    @Override
                    public void onClick(View v) {
                        PopupMenu popup = new PopupMenu(MyExpenses.this, accountMenu);
                        popup.inflate(R.menu.accounts_context);
                        Menu menu = popup.getMenu();
                        menu.findItem(R.id.DELETE_ACCOUNT_COMMAND).setVisible(count > 1);
                        menu.findItem(R.id.UP_COMMAND).setVisible(finalUpVisible);
                        menu.findItem(R.id.DOWN_COMMAND).setVisible(finalDownVisible);
                        popup.setOnMenuItemClickListener(new PopupMenu.OnMenuItemClickListener() {

                            @Override
                            public boolean onMenuItemClick(MenuItem item) {
                                return handleSwap(item.getItemId(), position)
                                        || dispatchCommand(item.getItemId(), position);
                            }

                            private boolean handleSwap(int itemId, int position) {
                                if (itemId != R.id.UP_COMMAND && itemId != R.id.DOWN_COMMAND)
                                    return false;
                                Cursor c = getCursor();
                                c.moveToPosition(position);
                                String sortKey1 = c.getString(c.getColumnIndex(KEY_SORT_KEY));
                                c.moveToPosition(itemId == R.id.UP_COMMAND ? position - 1 : position + 1);
                                String sortKey2 = c.getString(c.getColumnIndex(KEY_SORT_KEY));
                                startTaskExecution(TaskExecutionFragment.TASK_SWAP_SORT_KEY,
                                        new String[] { sortKey1, sortKey2 }, null, R.string.progress_dialog_saving);
                                return true;
                            }
                        });
                        popup.show();
                    }
                });
            }

            if (isAggregate) {
                hide_cr = true;
                if (mAccountGrouping == AccountGrouping.CURRENCY) {
                    labelTv.setText(R.string.menu_aggregates);
                }
                colorInt = colorAggregate;
            } else {
                //for deleting we need the position, because we need to find out the account's label
                try {
                    hide_cr = AccountType.valueOf(c.getString(c.getColumnIndexOrThrow(KEY_TYPE)))
                            .equals(AccountType.CASH);
                } catch (IllegalArgumentException ex) {
                    hide_cr = true;
                }
                colorInt = c.getInt(columnIndexColor);
            }
            row.findViewById(R.id.TransferRow).setVisibility(sum_transfer == 0 ? View.GONE : View.VISIBLE);
            row.findViewById(R.id.TotalRow).setVisibility(has_future ? View.VISIBLE : View.GONE);
            row.findViewById(R.id.ClearedRow).setVisibility(hide_cr ? View.GONE : View.VISIBLE);
            row.findViewById(R.id.ReconciledRow).setVisibility(hide_cr ? View.GONE : View.VISIBLE);
            if (c.getLong(columnIndexRowId) > 0) {
                setConvertedAmount((TextView) row.findViewById(R.id.sum_transfer), currency);
            }
            v.setBackgroundColor(colorInt);
            setConvertedAmount((TextView) row.findViewById(R.id.opening_balance), currency);
            setConvertedAmount((TextView) row.findViewById(R.id.sum_income), currency);
            setConvertedAmount((TextView) row.findViewById(R.id.sum_expenses), currency);
            setConvertedAmount((TextView) row.findViewById(R.id.current_balance), currency);
            setConvertedAmount((TextView) row.findViewById(R.id.total), currency);
            setConvertedAmount((TextView) row.findViewById(R.id.reconciled_total), currency);
            setConvertedAmount((TextView) row.findViewById(R.id.cleared_total), currency);
            String description = c.getString(columnIndexDescription);
            row.findViewById(R.id.description)
                    .setVisibility(TextUtils.isEmpty(description) ? View.GONE : View.VISIBLE);
            return row;
        }
    }

    protected void onSaveInstanceState(Bundle outState) {
        super.onSaveInstanceState(outState);
        //detail fragment from notification should only be shown once
        if (idFromNotification != 0) {
            outState.putLong("idFromNotification", 0);
        }
        outState.putString("exportFormat", mExportFormat);
        outState.putLong(KEY_ACCOUNTID, mAccountId);
    }

    @Override
    public void onPositive(Bundle args) {
        switch (args.getInt(ConfirmationDialogFragment.KEY_COMMAND_POSITIVE)) {
        case R.id.START_EXPORT_COMMAND:
            mExportFormat = args.getString("format");
            args.putSparseParcelableArray(TransactionList.KEY_FILTER, getCurrentFragment().getFilterCriteria());
            getSupportFragmentManager().beginTransaction()
                    .add(TaskExecutionFragment.newInstanceExport(args), ProtectionDelegate.ASYNC_TAG)
                    .add(ProgressDialogFragment.newInstance(R.string.pref_category_title_export, 0,
                            ProgressDialog.STYLE_SPINNER, true), ProtectionDelegate.PROGRESS_TAG)
                    .commit();
            break;
        case R.id.BALANCE_COMMAND_DO:
            startTaskExecution(TaskExecutionFragment.TASK_BALANCE, new Long[] { args.getLong(KEY_ROWID) },
                    args.getBoolean("deleteP"), 0);
            break;
        case R.id.DELETE_COMMAND_DO:
            //Confirmation dialog was shown without Checkbox, because it was called with only void transactions
            onPositive(args, false);
        }
    }

    @Override
    public void onPositive(Bundle args, boolean checked) {
        switch (args.getInt(ConfirmationDialogFragment.KEY_COMMAND_POSITIVE)) {
        case R.id.DELETE_COMMAND_DO:
            finishActionMode();
            startTaskExecution(TaskExecutionFragment.TASK_DELETE_TRANSACTION,
                    ArrayUtils.toObject(args.getLongArray(TaskExecutionFragment.KEY_OBJECT_IDS)),
                    Boolean.valueOf(checked), R.string.progress_dialog_deleting);
        }
    }

    @Override
    public void onNegative(Bundle args) {
        int command = args.getInt(ConfirmationDialogFragment.KEY_COMMAND_NEGATIVE);
        if (command != 0) {
            dispatchCommand(command, null);
        }
    }

    @Override
    public void onDismissOrCancel(Bundle args) {
    }

    @Override
    protected void onResume() {
        super.onResume();
        adHandler.onResume();
    }

    @Override
    public void onDestroy() {
        adHandler.onDestroy();
        super.onDestroy();
    }

    @Override
    protected void onPause() {
        adHandler.onPause();
        super.onPause();
    }

    public void onBackPressed() {
        if (mDrawerLayout != null && mDrawerLayout.isDrawerOpen(GravityCompat.START)) {
            mDrawerLayout.closeDrawer(GravityCompat.START);
        } else {
            super.onBackPressed();
        }
    }

    public void copyToClipBoard(View view) {
        ClipboardManager clipboard = (ClipboardManager) getSystemService(Context.CLIPBOARD_SERVICE);
        clipboard.setText(mCurrentBalance);
        Toast.makeText(this, R.string.copied_to_clipboard, Toast.LENGTH_LONG).show();
    }

    protected boolean handleSortOption(MenuItem item) {
        String newSortOrder = Utils.getSortOrderFromMenuItemId(item.getItemId());
        if (newSortOrder != null) {
            if (!item.isChecked()) {
                PrefKey.SORT_ORDER_ACCOUNTS.putString(newSortOrder);
                item.setChecked(true);

                if (mManager.getLoader(ACCOUNTS_CURSOR) != null && !mManager.getLoader(ACCOUNTS_CURSOR).isReset()) {
                    mManager.restartLoader(ACCOUNTS_CURSOR, null, this);
                } else {
                    mManager.initLoader(ACCOUNTS_CURSOR, null, this);
                }
                if (item.getItemId() == R.id.SORT_CUSTOM_COMMAND) {
                    MessageDialogFragment
                            .newInstance(R.string.dialog_title_information, R.string.dialog_info_custom_sort,
                                    MessageDialogFragment.Button.okButton(), null, null)
                            .show(getSupportFragmentManager(), "CUSTOM_SORT_INFO");
                }
            }
            return true;
        }
        return false;
    }

    protected boolean handleAccountsGrouping(MenuItem item) {
        AccountGrouping newGrouping = null;
        switch (item.getItemId()) {
        case R.id.GROUPING_ACCOUNTS_CURRENCY_COMMAND:
            newGrouping = AccountGrouping.CURRENCY;
            break;
        case R.id.GROUPING_ACCOUNTS_TYPE_COMMAND:
            newGrouping = AccountGrouping.TYPE;
            break;
        case R.id.GROUPING_ACCOUNTS_NONE_COMMAND:
            newGrouping = AccountGrouping.NONE;
            break;
        }
        if (newGrouping != null) {
            if (!item.isChecked()) {
                PrefKey.ACCOUNT_GROUPING.putString(newGrouping.name());
                item.setChecked(true);

                if (mManager.getLoader(ACCOUNTS_CURSOR) != null && !mManager.getLoader(ACCOUNTS_CURSOR).isReset())
                    mManager.restartLoader(ACCOUNTS_CURSOR, null, this);
                else
                    mManager.initLoader(ACCOUNTS_CURSOR, null, this);
            }
            return true;
        }
        return false;
    }

    protected boolean handleGrouping(MenuItem item) {
        Grouping newGrouping = Utils.getGroupingFromMenuItemId(item.getItemId());
        if (newGrouping != null) {
            if (!item.isChecked()) {
                PrefKey.ACCOUNT_GROUPING.putString(newGrouping.name());
                item.setChecked(true);
                if (mAccountId < 0) {
                    AggregateAccount.getInstanceFromDb(mAccountId).persistGrouping(newGrouping);
                } else {
                    Account.getInstanceFromDb(mAccountId).persistGrouping(newGrouping);
                }
            }
            return true;
        }
        return false;
    }
}