com.money.manager.ex.common.AllDataListFragment.java Source code

Java tutorial

Introduction

Here is the source code for com.money.manager.ex.common.AllDataListFragment.java

Source

/*
 * Copyright (C) 2012-2016 The Android Money Manager Ex Project Team
 *
 * This program is free software; you can redistribute it and/or
 * modify it under the terms of the GNU General Public License
 * as published by the Free Software Foundation; either version 3
 * of the License, or (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */
package com.money.manager.ex.common;

import android.app.Activity;
import android.content.ContentValues;
import android.content.Intent;
import android.database.Cursor;
import android.database.DatabaseUtils;
import android.graphics.Typeface;
import android.os.Bundle;
import android.support.annotation.NonNull;
import android.support.v4.app.Fragment;
import android.support.v4.app.LoaderManager;
import android.support.v4.app.LoaderManager.LoaderCallbacks;
import android.support.v4.content.Loader;
import android.support.v4.widget.CursorAdapter;
import android.support.v7.app.ActionBar;
import android.text.TextUtils;
import android.util.SparseBooleanArray;
import android.view.LayoutInflater;
import android.view.Menu;
import android.view.MenuInflater;
import android.view.MenuItem;
import android.view.View;
import android.view.ViewGroup;
import android.widget.AdapterView;
import android.widget.AdapterView.OnItemClickListener;
import android.widget.LinearLayout;
import android.widget.ListAdapter;
import android.widget.ListView;
import android.widget.TextView;
import android.widget.Toast;

import com.afollestad.materialdialogs.DialogAction;
import com.afollestad.materialdialogs.MaterialDialog;
import com.mikepenz.google_material_typeface_library.GoogleMaterial;
import com.money.manager.ex.Constants;
import com.money.manager.ex.core.TransactionTypes;
import com.money.manager.ex.core.UIHelper;
import com.money.manager.ex.currency.CurrencyService;
import com.money.manager.ex.database.ITransactionEntity;
import com.money.manager.ex.datalayer.AccountTransactionRepository;
import com.money.manager.ex.datalayer.Select;
import com.money.manager.ex.datalayer.SplitCategoriesRepository;
import com.money.manager.ex.domainmodel.AccountTransaction;
import com.money.manager.ex.domainmodel.SplitCategory;
import com.money.manager.ex.sync.SyncManager;
import com.money.manager.ex.transactions.CheckingTransactionEditActivity;
import com.money.manager.ex.R;
import com.money.manager.ex.servicelayer.qif.QifExport;
import com.money.manager.ex.transactions.EditTransactionActivityConstants;
import com.money.manager.ex.search.SearchActivity;
import com.money.manager.ex.adapter.AllDataAdapter;
import com.money.manager.ex.adapter.AllDataAdapter.TypeCursor;
import com.money.manager.ex.home.DrawerMenuItem;
import com.money.manager.ex.home.DrawerMenuItemAdapter;
import com.money.manager.ex.core.ExportToCsvFile;
import com.money.manager.ex.database.QueryAllData;

import java.lang.reflect.Field;
import java.util.ArrayList;
import java.util.HashMap;

import info.javaperformance.money.Money;
import info.javaperformance.money.MoneyFactory;
import timber.log.Timber;

/**
 * Fragment that displays the transactions.
 */
public class AllDataListFragment extends BaseListFragment
        implements LoaderCallbacks<Cursor>, IAllDataMultiChoiceModeListenerCallbacks {

    private static final String ARG_ACCOUNT_ID = "AccountId";
    private static final String ARG_SHOW_FLOATING_BUTTON = "ShowFloatingButton";

    public static AllDataListFragment newInstance(int accountId) {
        return newInstance(accountId, true);
    }

    /**
     * Create a new instance of AllDataListFragment with accountId params
     *
     * @param accountId Id of account to display. If generic shown set -1
     * @return new instance AllDataListFragment
     */
    public static AllDataListFragment newInstance(int accountId, boolean showFloatingButton) {
        AllDataListFragment fragment = new AllDataListFragment();

        Bundle args = new Bundle();
        args.putInt(ARG_ACCOUNT_ID, accountId);
        args.putBoolean(ARG_SHOW_FLOATING_BUTTON, showFloatingButton);
        fragment.setArguments(args);

        return fragment;
    }

    public static final int ID_LOADER_ALL_DATA_DETAIL = 1;

    public static final String KEY_ARGUMENTS_WHERE = "SearchResultFragment:ArgumentsWhere";
    public static final String KEY_ARGUMENTS_SORT = "SearchResultFragment:ArgumentsSort";

    public int AccountId = Constants.NOT_SET;
    private LinearLayout footer;
    private LoaderManager.LoaderCallbacks<Cursor> mSearResultFragmentLoaderCallbacks;
    private boolean mAutoStarLoader = true;
    private boolean mShowHeader = false;
    private boolean mShowBalance = false;
    private AllDataMultiChoiceModeListener mMultiChoiceModeListener;
    private View mListHeader = null;
    private Bundle mArguments;
    private boolean mShowFooter = false;

    @Override
    public void onActivityCreated(Bundle savedInstanceState) {
        super.onActivityCreated(savedInstanceState);

        setEmptyText(getString(R.string.no_data));
        setListShown(false);

        // Read arguments
        this.AccountId = getArguments().getInt(ARG_ACCOUNT_ID);

        // Read header indicator directly from the activity.
        // todo: make this a parameter or a property.
        if (getActivity() instanceof SearchActivity) {
            SearchActivity activity = (SearchActivity) getActivity();
            setShownHeader(activity.ShowAccountHeaders);
        }

        // create adapter for data.
        AllDataAdapter adapter = new AllDataAdapter(getActivity(), null, TypeCursor.ALLDATA);
        adapter.setAccountId(this.AccountId);
        adapter.setShowAccountName(isShownHeader());
        adapter.setShowBalanceAmount(isShownBalance());

        // set multi-choice mode in the list view.
        mMultiChoiceModeListener = new AllDataMultiChoiceModeListener();
        mMultiChoiceModeListener.setListener(this);
        getListView().setChoiceMode(ListView.CHOICE_MODE_MULTIPLE_MODAL);
        getListView().setMultiChoiceModeListener(mMultiChoiceModeListener);

        // e item click
        getListView().setOnItemClickListener(new OnItemClickListener() {

            @Override
            public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
                if (getListAdapter() != null && getListAdapter() instanceof AllDataAdapter) {
                    Cursor cursor = ((AllDataAdapter) getListAdapter()).getCursor();
                    if (cursor.moveToPosition(position - (mListHeader != null ? 1 : 0))) {
                        startEditAccountTransactionActivity(cursor.getInt(cursor.getColumnIndex(QueryAllData.ID)));
                    }
                }
            }
        });

        // Header and footer must be added before setAdapter().

        // Add a header to the list view if one exists.
        if (getListAdapter() == null) {
            if (mListHeader != null)
                getListView().addHeaderView(mListHeader);
        }
        if (this.mShowFooter) {
            renderFooter();
        }

        // set adapter
        setListAdapter(adapter);

        // register context menu
        registerForContextMenu(getListView());

        // set animation progress
        setListShown(false);

        boolean showAddButton = getArguments().getBoolean(ARG_SHOW_FLOATING_BUTTON);
        if (showAddButton) {
            // Show floating action button.
            setFloatingActionButtonVisible(true);
            attachFloatingActionButtonToListView();
        }

        // start loader if asked to do so by the caller.
        if (isAutoStarLoader()) {
            loadData();
        }
    }

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

        setHasOptionsMenu(true);
    }

    @Override
    public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
        return super.onCreateView(inflater, container, savedInstanceState);
    }

    // Loader event handlers

    @Override
    public Loader<Cursor> onCreateLoader(int id, Bundle args) {
        if (getSearchResultFragmentLoaderCallbacks() != null)
            getSearchResultFragmentLoaderCallbacks().onCreateLoader(id, args);
        //animation
        setListShown(false);

        switch (id) {
        case ID_LOADER_ALL_DATA_DETAIL:
            // compose selection and sort
            String selection = "";
            if (args != null && args.containsKey(KEY_ARGUMENTS_WHERE)) {
                selection = args.getString(KEY_ARGUMENTS_WHERE);
            }
            //                String[] whereParams = new String[0];
            //                if (args != null && args.containsKey(KEY_ARGUMENTS_WHERE_PARAMS)) {
            //                    ArrayList<String> whereParamsList = args.getStringArrayList(KEY_ARGUMENTS_WHERE_PARAMS);
            //                    whereParams = whereParamsList.toArray(whereParams);
            //                }

            // set sort
            String sort = "";
            if (args != null && args.containsKey(KEY_ARGUMENTS_SORT)) {
                sort = args.getString(KEY_ARGUMENTS_SORT);
            }
            // create loader
            QueryAllData allData = new QueryAllData(getActivity());
            Select query = new Select(allData.getAllColumns()).where(selection).orderBy(sort);

            return new MmxCursorLoader(getActivity(), allData.getUri(), query);
        }
        return null;
    }

    @Override
    public void onLoaderReset(Loader<Cursor> loader) {
        LoaderManager.LoaderCallbacks<Cursor> parent = getSearchResultFragmentLoaderCallbacks();
        if (parent != null)
            parent.onLoaderReset(loader);

        //((CursorAdapter) getListAdapter()).swapCursor(null);
        ((CursorAdapter) getListAdapter()).changeCursor(null);
    }

    @Override
    public void onLoadFinished(Loader<Cursor> loader, Cursor data) {
        LoaderManager.LoaderCallbacks<Cursor> parent = getSearchResultFragmentLoaderCallbacks();
        if (parent != null)
            parent.onLoadFinished(loader, data);

        switch (loader.getId()) {
        case ID_LOADER_ALL_DATA_DETAIL:
            // Transactions list loaded.
            AllDataAdapter adapter = (AllDataAdapter) getListAdapter();
            //                adapter.swapCursor(data);
            adapter.changeCursor(data);
            if (isResumed()) {
                setListShown(true);
                if (data != null && data.getCount() <= 0 && getFloatingActionButton() != null)
                    getFloatingActionButton().show(true);
            } else {
                setListShownNoAnimation(true);
            }

            // reset the transaction groups (account name collection)
            adapter.resetAccountHeaderIndexes();

            // Show totals
            if (this.mShowFooter) {
                try {
                    this.updateFooter(data);
                } catch (Exception e) {
                    Timber.e(e, "displaying footer");
                }
            }
        }
    }

    // End loader event handlers

    /**
     * Add options to the action bar of the host activity.
     * This is not called in ActionBar Activity, i.e. Search.
     * @param menu
     * @param inflater
     */
    @Override
    public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) {
        super.onCreateOptionsMenu(menu, inflater);

        Activity activity = getActivity();
        if (activity == null)
            return;

        MenuItem itemExportToCsv = menu.findItem(R.id.menu_export_to_csv);
        if (itemExportToCsv != null)
            itemExportToCsv.setVisible(true);
        MenuItem itemSearch = menu.findItem(R.id.menu_search_transaction);

        if (itemSearch != null) {
            itemSearch
                    .setVisible(!activity.getClass().getSimpleName().equals(SearchActivity.class.getSimpleName()));
        }

        // show this on all transactions lists later?
        // show this menu only when on Search Activity for now.
        if (activity.getClass().getSimpleName().equals(SearchActivity.class.getSimpleName())) {
            // Add default menu options. todo: check why this is executed twice.
            // Includes menu item for .qif export
            MenuItem qifExport = menu.findItem(R.id.menu_qif_export);
            if (qifExport == null) {
                inflater.inflate(R.menu.menu_alldata_operations, menu);
            }
        }
    }

    /**
     * This is just to test:
     * http://stackoverflow.com/questions/15207305/getting-the-error-java-lang-illegalstateexception-activity-has-been-destroyed
     */
    @Override
    public void onDetach() {
        super.onDetach();
        try {
            Field childFragmentManager = Fragment.class.getDeclaredField("mChildFragmentManager");
            childFragmentManager.setAccessible(true);
            childFragmentManager.set(this, null);

        } catch (NoSuchFieldException e) {
            throw new RuntimeException(e);
        } catch (IllegalAccessException e) {
            throw new RuntimeException(e);
        }
    }

    @Override
    public void onDestroy() {
        if (mMultiChoiceModeListener != null)
            mMultiChoiceModeListener.onDestroyActionMode(null);
        super.onDestroy();
    }

    @Override
    public void onResume() {
        super.onResume();

    }

    @Override
    public void onStop() {
        super.onStop();
        try {
            MmxBaseFragmentActivity activity = (MmxBaseFragmentActivity) getActivity();
            if (activity != null) {
                ActionBar actionBar = activity.getSupportActionBar();
                if (actionBar != null) {
                    View customView = actionBar.getCustomView();
                    if (customView != null) {
                        actionBar.setCustomView(null);
                    }
                }
            }
        } catch (Exception e) {
            Timber.e(e, "stopping the all-data fragment");
        }
    }

    @Override
    public boolean onOptionsItemSelected(MenuItem item) {
        int itemId = item.getItemId();

        if (itemId == R.id.menu_export_to_csv) {
            exportDataToCSVFile();
            return true;
        }
        if (itemId == R.id.menu_qif_export) {
            // export visible transactions.
            exportToQif();
        }

        return super.onOptionsItemSelected(item);
    }

    @Override
    public String getSubTitle() {
        return null;
    }

    @Override
    public void onFloatingActionButtonClicked() {
        startEditAccountTransactionActivity(null);
    }

    // Multi-choice-mode listener callback handlers.

    /**
     * handler for multi-choice-mode listener
     */
    @Override
    public void onMultiChoiceCreated(android.view.Menu menu) {
        getActivity().getMenuInflater().inflate(R.menu.menu_all_data_adapter, menu);
    }

    @Override
    public void onDestroyActionMode() {
        if (getListAdapter() != null && getListAdapter() instanceof AllDataAdapter) {
            AllDataAdapter adapter = (AllDataAdapter) getListAdapter();
            adapter.clearPositionChecked();
            adapter.notifyDataSetChanged();
        }
    }

    @Override
    public void onDeleteClicked() {
        ArrayList<Integer> transIds = getTransactionIds();

        showDialogDeleteCheckingAccount(transIds);
    }

    @Override
    public void onChangeTransactionStatusClicked() {
        ArrayList<Integer> transIds = getTransactionIds();
        changeTransactionStatus(transIds);
    }

    @Override
    public void onTransactionStatusClicked(String status) {
        ArrayList<Integer> transIds = getTransactionIds();

        if (setStatusCheckingAccount(convertArrayListToArray(transIds), status)) {
            ((AllDataAdapter) getListAdapter()).clearPositionChecked();
            loadData();
        }
    }

    @Override
    public void onSelectAllRecordsClicked() {
        selectAllRecords();
    }

    @Override
    public void onDuplicateTransactionsClicked() {
        ArrayList<Integer> transIds = getTransactionIds();
        showDuplicateTransactionView(transIds);
    }

    @Override
    public void onItemCheckedStateChanged(int position, boolean checked) {
        if (getListHeader() != null) {
            position--;
        }

        if (getListAdapter() != null && getListAdapter() instanceof AllDataAdapter) {
            AllDataAdapter adapter = (AllDataAdapter) getListAdapter();
            adapter.setPositionChecked(position, checked);
            adapter.notifyDataSetChanged();
        }
    }

    // Methods

    public void displayRunningBalances(HashMap<Integer, Money> balances) {
        AllDataAdapter adapter = getAllDataAdapter();
        if (adapter == null)
            return;

        adapter.setBalances(balances);
    }

    /**
     * Export data to CSV file
     */
    public void exportDataToCSVFile() {
        exportDataToCSVFile("");
    }

    /**
     * Export data to CSV file
     *
     * @param prefixName prefix for the file
     */
    public void exportDataToCSVFile(String prefixName) {
        ExportToCsvFile csv = new ExportToCsvFile(getActivity(), (AllDataAdapter) getListAdapter());
        csv.setPrefixName(prefixName);
        csv.execute();
    }

    /**
     * @return the mSearResultFragmentLoaderCallbacks
     */
    public LoaderManager.LoaderCallbacks<Cursor> getSearchResultFragmentLoaderCallbacks() {
        return mSearResultFragmentLoaderCallbacks;
    }

    /**
     * @param searResultFragmentLoaderCallbacks the searResultFragmentLoaderCallbacks to set
     */
    public void setSearResultFragmentLoaderCallbacks(
            LoaderManager.LoaderCallbacks<Cursor> searResultFragmentLoaderCallbacks) {
        this.mSearResultFragmentLoaderCallbacks = searResultFragmentLoaderCallbacks;
    }

    /**
     * @return the mAutoStarLoader
     */
    public boolean isAutoStarLoader() {
        return mAutoStarLoader;
    }

    /**
     * @param mAutoStarLoader the mAutoStarLoader to set
     */
    public void setAutoStarLoader(boolean mAutoStarLoader) {
        this.mAutoStarLoader = mAutoStarLoader;
    }

    /**
     * @return the mShowHeader
     */
    public boolean isShownHeader() {
        return mShowHeader;
    }

    /**
     * Start loader into fragment
     */
    public void loadData() {
        loadData(getLatestArguments());
    }

    public void loadData(Bundle arguments) {
        // set the account id in the data adapter
        AllDataAdapter adapter = getAllDataAdapter();
        if (adapter != null) {
            adapter.setAccountId(this.AccountId);
            adapter.setBalances(null);
        }

        // set the current arguments / account id
        setLatestArguments(arguments);
        // reload data with the latest arguments.
        getLoaderManager().restartLoader(ID_LOADER_ALL_DATA_DETAIL, arguments, this);
    }

    /**
     * @param mShownHeader the mShowHeader to set
     */
    public void setShownHeader(boolean mShownHeader) {
        this.mShowHeader = mShownHeader;
    }

    public View getListHeader() {
        return mListHeader;
    }

    public void setListHeader(View mHeaderList) {
        this.mListHeader = mHeaderList;
    }

    /**
     * @return the mShowBalance
     */
    public boolean isShownBalance() {
        return mShowBalance;
    }

    /**
     * @param mShownBalance the mShowBalance to set
     */
    public void setShownBalance(boolean mShownBalance) {
        this.mShowBalance = mShownBalance;

        AllDataAdapter adapter = getAllDataAdapter();
        if (adapter == null) {
            return;
        }

        adapter.setShowBalanceAmount(mShownBalance);
    }

    public void showTotalsFooter() {
        this.mShowFooter = true;
    }

    // Private methods.

    private void renderFooter() {
        this.footer = (LinearLayout) View.inflate(getActivity(), R.layout.item_generic_report_2_columns, null);

        TextView txtColumn1 = (TextView) footer.findViewById(R.id.textViewColumn1);
        TextView txtColumn2 = (TextView) footer.findViewById(R.id.textViewColumn2);

        txtColumn1.setText(R.string.total);
        txtColumn1.setTypeface(null, Typeface.BOLD_ITALIC);
        txtColumn2.setText(R.string.total);
        txtColumn2.setTypeface(null, Typeface.BOLD_ITALIC);

        ListView listView = getListView();
        listView.addFooterView(footer);
    }

    private void updateFooter(Cursor data) {
        if (data == null)
            return;

        String display;

        // number of records
        display = Integer.toString(data.getCount()) + " " + getString(R.string.records) + ", ";

        // sum

        Money total = MoneyFactory.fromString("0");

        if (data.getCount() != 0) {
            total = getTotalFromCursor(data);
        }

        TextView txtColumn2 = (TextView) this.footer.findViewById(R.id.textViewColumn2);

        CurrencyService currencyService = new CurrencyService(getContext());
        display += currencyService.getBaseCurrencyFormatted(total);

        txtColumn2.setText(display);
    }

    private Money getTotalFromCursor(Cursor cursor) {
        Money total = MoneyFactory.fromString("0");
        int originalPosition = cursor.getPosition();
        AllDataAdapter adapter = getAllDataAdapter();
        CurrencyService currencyService = new CurrencyService(getContext());
        int baseCurrencyId = currencyService.getBaseCurrencyId();
        ContentValues values = new ContentValues();

        int currencyId;
        Money amount;
        Money converted;
        String transType;
        TransactionTypes transactionType;

        cursor.moveToPosition(Constants.NOT_SET);

        while (cursor.moveToNext()) {
            values.clear();

            // Read needed data.
            DatabaseUtils.cursorStringToContentValues(cursor, adapter.TRANSACTIONTYPE, values);
            DatabaseUtils.cursorIntToContentValues(cursor, adapter.CURRENCYID, values);
            DatabaseUtils.cursorIntToContentValues(cursor, adapter.TOCURRENCYID, values);
            DatabaseUtils.cursorDoubleToCursorValues(cursor, adapter.AMOUNT, values);
            DatabaseUtils.cursorDoubleToCursorValues(cursor, adapter.TOAMOUNT, values);

            transType = values.getAsString(adapter.TRANSACTIONTYPE);
            transactionType = TransactionTypes.valueOf(transType);

            if (transactionType.equals(TransactionTypes.Transfer)) {
                currencyId = values.getAsInteger(adapter.TOCURRENCYID);
                amount = MoneyFactory.fromString(values.getAsString(adapter.TOAMOUNT));
            } else {
                currencyId = values.getAsInteger(adapter.CURRENCYID);
                amount = MoneyFactory.fromString(values.getAsString(adapter.AMOUNT));
            }

            converted = currencyService.doCurrencyExchange(baseCurrencyId, amount, currencyId);
            total = total.add(converted);
        }

        cursor.moveToPosition(originalPosition);

        return total;
    }

    private boolean setStatusCheckingAccount(int[] transId, String status) {
        // check if status = "U" convert to empty string
        if (TextUtils.isEmpty(status) || "U".equalsIgnoreCase(status))
            status = "";

        SyncManager sync = new SyncManager(getActivity());
        // Pause synchronization while bulk processing.
        sync.disableAutoUpload();

        for (int id : transId) {
            // content value for updates
            ContentValues values = new ContentValues();
            // set new state
            values.put(ITransactionEntity.STATUS, status.toUpperCase());

            AccountTransactionRepository repo = new AccountTransactionRepository(getActivity());

            // update
            int updateResult = getActivity().getContentResolver().update(repo.getUri(), values,
                    AccountTransaction.TRANSID + "=?", new String[] { Integer.toString(id) });
            if (updateResult <= 0) {
                Toast.makeText(getActivity(), R.string.db_update_failed, Toast.LENGTH_LONG).show();

                sync.enableAutoUpload();
                sync.dataChanged();

                return false;
            }
        }

        // Now notify Dropbox about modifications.
        sync.enableAutoUpload();
        sync.dataChanged();

        return true;
    }

    /**
     * @param transactionIds primary key of transaction
     */
    private void showDialogDeleteCheckingAccount(final ArrayList<Integer> transactionIds) {
        // create alert binaryDialog and set title and message
        MaterialDialog.Builder alertDialog = new MaterialDialog.Builder(getContext())
                .title(R.string.delete_transaction)
                .icon(new UIHelper(getActivity()).getIcon(GoogleMaterial.Icon.gmd_warning))
                .content(getResources().getQuantityString(R.plurals.plurals_delete_transactions,
                        transactionIds.size(), transactionIds.size()));
        //        alert.setIcon(R.drawable.ic_action_warning_light);

        // set listener button positive
        alertDialog.positiveText(android.R.string.ok);
        alertDialog.onPositive(new MaterialDialog.SingleButtonCallback() {
            @Override
            public void onClick(@NonNull MaterialDialog dialog, @NonNull DialogAction which) {
                SyncManager sync = new SyncManager(getActivity());

                // Pause sync notification while bulk processing.
                sync.disableAutoUpload();

                for (int transactionId : transactionIds) {
                    // First delete any splits. See if there are any split records.
                    SplitCategoriesRepository splitRepo = new SplitCategoriesRepository(getActivity());
                    Cursor curSplit = getActivity().getContentResolver().query(splitRepo.getUri(), null,
                            SplitCategory.TRANSID + "=" + Integer.toString(transactionId), null,
                            SplitCategory.SPLITTRANSID);
                    int splitCount = curSplit.getCount();
                    curSplit.close();

                    if (splitCount > 0) {
                        int deleteResult = getActivity().getContentResolver().delete(splitRepo.getUri(),
                                SplitCategory.TRANSID + "=?", new String[] { Integer.toString(transactionId) });
                        if (deleteResult != splitCount) {
                            Toast.makeText(getActivity(), R.string.db_delete_failed, Toast.LENGTH_SHORT).show();

                            // Now notify Dropbox about modifications.
                            sync.enableAutoUpload();
                            sync.dataChanged();

                            return;
                        }
                    }

                    // Delete the transaction.

                    AccountTransactionRepository repo = new AccountTransactionRepository(getActivity());

                    int deleteResult = getActivity().getContentResolver().delete(repo.getUri(),
                            AccountTransaction.TRANSID + "=?", new String[] { Integer.toString(transactionId) });
                    if (deleteResult == 0) {
                        Toast.makeText(getActivity(), R.string.db_delete_failed, Toast.LENGTH_SHORT).show();

                        // Now notify Dropbox about modifications.
                        sync.enableAutoUpload();
                        sync.dataChanged();

                        return;
                    }
                }

                // Now notify Dropbox about modifications.
                sync.enableAutoUpload();
                sync.dataChanged();

                // restart loader
                loadData();
            }
        });
        // set listener negative button
        alertDialog.negativeText(android.R.string.cancel);
        alertDialog.onNegative(new MaterialDialog.SingleButtonCallback() {
            @Override
            public void onClick(@NonNull MaterialDialog dialog, @NonNull DialogAction which) {
                dialog.cancel();
            }
        });

        alertDialog.build().show();
    }

    /**
     * start the activity of transaction management
     *
     * @param transId null set if you want to do a new transaction, or transaction id
     */
    private void startEditAccountTransactionActivity(Integer transId) {
        // create intent, set Account ID
        Intent intent = new Intent(getActivity(), CheckingTransactionEditActivity.class);
        // check transId not null
        if (transId != null) {
            intent.putExtra(EditTransactionActivityConstants.KEY_TRANS_ID, transId);
            intent.setAction(Intent.ACTION_EDIT);
        } else {
            intent.putExtra(EditTransactionActivityConstants.KEY_ACCOUNT_ID, this.AccountId);
            intent.setAction(Intent.ACTION_INSERT);
        }
        // launch activity
        startActivity(intent);
    }

    private AllDataAdapter getAllDataAdapter() {
        AllDataAdapter adapter = null;

        ListAdapter listAdapter = getListAdapter();
        if (listAdapter != null && listAdapter instanceof AllDataAdapter) {
            adapter = (AllDataAdapter) getListAdapter();
        }

        return adapter;
    }

    private void selectAllRecords() {
        AllDataAdapter adapter = getAllDataAdapter();
        if (adapter == null)
            return;

        // Clear selection first.
        adapter.clearPositionChecked();

        int numRecords = adapter.getCount();
        for (int i = 0; i < numRecords; i++) {
            adapter.setPositionChecked(i, true);
        }

        adapter.notifyDataSetChanged();
    }

    private ArrayList<Integer> getTransactionIds() {
        final ArrayList<Integer> transIds = new ArrayList<>();

        AllDataAdapter adapter = getAllDataAdapter();
        if (adapter == null)
            return transIds;

        Cursor cursor = adapter.getCursor();
        if (cursor != null) {
            // get checked items & count from the adapter, not from the list view.
            // List view only contains the one that was tapped, ignoring the Select All.
            //                SparseBooleanArray positionChecked = getListView().getCheckedItemPositions();
            SparseBooleanArray positionChecked = adapter.getPositionsChecked();
            //                int checkedItemsCount = getListView().getCheckedItemCount();
            int checkedItemsCount = positionChecked.size();

            for (int i = 0; i < checkedItemsCount; i++) {
                int position = positionChecked.keyAt(i);
                // This screws up the selection?
                //                    if (getListHeader() != null)
                //                        position--;
                if (cursor.moveToPosition(position)) {
                    transIds.add(cursor.getInt(cursor.getColumnIndex(QueryAllData.ID)));
                }
            }
        }

        return transIds;
    }

    private void changeTransactionStatus(final ArrayList<Integer> transIds) {
        final DrawerMenuItemAdapter adapter = new DrawerMenuItemAdapter(getActivity());
        //        final Core core = new Core(getActivity().getApplicationContext());
        final Boolean isDarkTheme = new UIHelper(getActivity()).isUsingDarkTheme();

        // add status
        adapter.add(new DrawerMenuItem().withId(R.id.menu_none).withText(getString(R.string.status_none))
                .withIcon(isDarkTheme ? R.drawable.ic_action_help_dark : R.drawable.ic_action_help_light)
                .withShortcut(""));
        adapter.add(
                new DrawerMenuItem().withId(R.id.menu_reconciled).withText(getString(R.string.status_reconciled))
                        .withIcon(isDarkTheme ? R.drawable.ic_action_done_dark : R.drawable.ic_action_done_light)
                        .withShortcut("R"));
        adapter.add(new DrawerMenuItem().withId(R.id.menu_follow_up).withText(getString(R.string.status_follow_up))
                .withIcon(isDarkTheme ? R.drawable.ic_action_alarm_on_dark : R.drawable.ic_action_alarm_on_light)
                .withShortcut("F"));
        adapter.add(new DrawerMenuItem().withId(R.id.menu_duplicate).withText(getString(R.string.status_duplicate))
                .withIcon(isDarkTheme ? R.drawable.ic_action_copy_dark : R.drawable.ic_action_copy_light)
                .withShortcut("D"));
        adapter.add(new DrawerMenuItem().withId(R.id.menu_void).withText(getString(R.string.status_void))
                .withIcon(isDarkTheme ? R.drawable.ic_action_halt_dark : R.drawable.ic_action_halt_light)
                .withShortcut("V"));

        // open binaryDialog
        final MaterialDialog dialog = new MaterialDialog.Builder(getActivity())
                .title(getString(R.string.change_status)).adapter(adapter, null).build();

        ListView listView = dialog.getListView();
        if (listView != null)
            listView.setOnItemClickListener(new AdapterView.OnItemClickListener() {
                @Override
                public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
                    DrawerMenuItem item = adapter.getItem(position);
                    switch (item.getId()) {
                    case R.id.menu_none:
                    case R.id.menu_reconciled:
                    case R.id.menu_follow_up:
                    case R.id.menu_duplicate:
                    case R.id.menu_void:
                        String status = item.getShortcut();
                        if (setStatusCheckingAccount(convertArrayListToArray(transIds), status)) {
                            ((AllDataAdapter) getListAdapter()).clearPositionChecked();
                            loadData();
                        }
                    }
                    dialog.dismiss();
                }
            });
        dialog.show();
    }

    private void showDuplicateTransactionView(ArrayList<Integer> transIds) {
        // validation
        int transactionCount = transIds.size();
        if (transactionCount <= 0)
            return;

        int[] ids = convertArrayListToArray(transIds);
        Intent[] intents = new Intent[transactionCount];
        for (int i = 0; i < transactionCount; i++) {
            intents[i] = new Intent(getActivity(), CheckingTransactionEditActivity.class);
            intents[i].putExtra(EditTransactionActivityConstants.KEY_TRANS_ID, ids[i]);
            intents[i].setAction(Intent.ACTION_PASTE);
        }
        getActivity().startActivities(intents);
    }

    // end multi-choice-mode listener callback handlers.

    private void exportToQif() {
        AllDataAdapter adapter = (AllDataAdapter) getListAdapter();
        QifExport qif = new QifExport(getActivity());
        qif.export(adapter);
    }

    private int[] convertArrayListToArray(ArrayList<Integer> list) {
        int[] result = new int[list.size()];
        for (int i = 0; i < list.size(); i++) {
            result[i] = list.get(i);
        }
        return result;
    }

    /**
     * Returns the latest-set arguments. This is because the original arguments, when the
     * fragment was created, can not be altered.
     * But, when an account changes, we need to modify them. The new arguments are passed
     * through the call to loadData().
     * @return
     */
    private Bundle getLatestArguments() {
        if (mArguments == null) {
            mArguments = getArguments();
        }
        return mArguments;
    }

    private void setLatestArguments(Bundle arguments) {
        mArguments = arguments;
    }

}