/* * 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.home; import android.animation.ObjectAnimator; import android.app.Activity; import android.content.Intent; import android.database.Cursor; import android.graphics.Color; import android.os.Bundle; import android.support.annotation.Nullable; import android.support.v4.app.Fragment; import android.support.v4.app.LoaderManager; import android.support.v4.content.Loader; import android.support.v7.app.AppCompatActivity; import android.view.ContextMenu; import android.view.LayoutInflater; import android.view.Menu; import android.view.MenuInflater; import android.view.MenuItem; import android.view.View; import android.view.View.OnClickListener; import android.view.ViewGroup; import android.view.animation.DecelerateInterpolator; import android.widget.Button; import android.widget.ExpandableListView; import android.widget.FrameLayout; import android.widget.ImageView; import android.widget.LinearLayout; import android.widget.ProgressBar; import android.widget.TextView; import android.widget.Toast; import com.melnykov.fab.FloatingActionButton; import com.money.manager.ex.account.AccountEditActivity; import com.money.manager.ex.common.AmountInputDialog; import com.money.manager.ex.common.MmxBaseFragmentActivity; import com.money.manager.ex.common.events.AmountEnteredEvent; import com.money.manager.ex.core.ContextMenuIds; import com.money.manager.ex.core.InfoKeys; import com.money.manager.ex.datalayer.InfoRepositorySql; import com.money.manager.ex.datalayer.Select; import com.money.manager.ex.home.events.AccountsTotalLoadedEvent; import com.money.manager.ex.home.events.RequestAccountFragmentEvent; import com.money.manager.ex.home.events.RequestPortfolioFragmentEvent; import com.money.manager.ex.home.events.RequestWatchlistFragmentEvent; import com.money.manager.ex.home.events.UsernameLoadedEvent; import com.money.manager.ex.servicelayer.AccountService; import com.money.manager.ex.common.MmxCursorLoader; import com.money.manager.ex.core.TransactionTypes; import com.money.manager.ex.servicelayer.InfoService; import com.money.manager.ex.settings.LookAndFeelSettings; import com.money.manager.ex.settings.SettingsActivity; import com.money.manager.ex.transactions.CheckingTransactionEditActivity; import com.money.manager.ex.Constants; import com.money.manager.ex.MoneyManagerApplication; import com.money.manager.ex.R; import com.money.manager.ex.account.AccountTypes; import com.money.manager.ex.datalayer.AccountRepository; import com.money.manager.ex.database.DatabaseMigrator14To20; import com.money.manager.ex.database.QueryAccountBills; import com.money.manager.ex.database.QueryReportIncomeVsExpenses; import com.money.manager.ex.search.SearchActivity; import com.money.manager.ex.settings.AppSettings; import com.money.manager.ex.settings.PreferenceConstants; import com.money.manager.ex.currency.CurrencyService; import com.money.manager.ex.transactions.IntentDataParameters; import com.money.manager.ex.utils.MmxDatabaseUtils; import com.money.manager.ex.view.RobotoTextView; import com.money.manager.ex.viewmodels.IncomeVsExpenseReportEntity; import org.apache.commons.io.FilenameUtils; import org.greenrobot.eventbus.EventBus; import org.greenrobot.eventbus.Subscribe; import java.util.ArrayList; import java.util.Calendar; import java.util.HashMap; import java.util.List; import java.util.concurrent.Callable; import javax.inject.Inject; import dagger.Lazy; import icepick.Icepick; import icepick.State; import info.javaperformance.money.Money; import info.javaperformance.money.MoneyFactory; import rx.Single; import rx.SingleSubscriber; import rx.android.schedulers.AndroidSchedulers; import rx.schedulers.Schedulers; import timber.log.Timber; /** * The starting fragment that contains the accounts groups with accounts and their balances. */ public class HomeFragment extends Fragment implements LoaderManager.LoaderCallbacks<Cursor> { // private static final int LOADER_USER_NAME = 1; private static final int LOADER_ACCOUNT_BILLS = 2; private static final int LOADER_INCOME_EXPENSES = 4; private static final String TAG_BALANCE_ACCOUNT = "HomeFragment:BalanceAccount"; private static final int REQUEST_BALANCE_ACCOUNT = 1; @Inject Lazy<InfoRepositorySql> infoRepositorySqlLazy; private CurrencyService mCurrencyService; private boolean mHideReconciled; // This is the collapsible list of account groups with accounts. private ExpandableListView mExpandableListView; private ViewGroup linearHome, linearFooter, linearWelcome; private TextView txtTotalAccounts, txtFooterSummary, txtFooterSummaryReconciled; private ProgressBar prgAccountBills; private FloatingActionButton mFloatingActionButton; /** * List of account types in lowercase (i.e. checking, investment, ...) */ private List<String> mAccountTypes = new ArrayList<>(); private HashMap<String, List<QueryAccountBills>> mAccountsByType = new HashMap<>(); private HashMap<String, QueryAccountBills> mTotalsByType = new HashMap<>(); private Money mGrandTotal = MoneyFactory.fromDouble(0); private Money mGrandReconciled = MoneyFactory.fromDouble(0); @State int accountBalancedId = Constants.NOT_SET; private QueryAccountBills accountBeingBalanced = null; @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); MoneyManagerApplication.getApp().iocComponent.inject(this); mCurrencyService = new CurrencyService(getActivity().getApplicationContext()); refreshSettings(); // The fragment is using a custom option in the actionbar menu. setHasOptionsMenu(true); // restore number input binaryDialog reference, if any if (savedInstanceState != null) { // this.accountBalancedId = savedInstanceState.getInt(TAG_BALANCE_ACCOUNT); Icepick.restoreInstanceState(this, savedInstanceState); } } @Override public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { if (container == null) return null; // inflate layout View view = inflater.inflate(R.layout.home_fragment, container, false); linearHome = (FrameLayout) view.findViewById(R.id.linearLayoutHome); txtTotalAccounts = (TextView) view.findViewById(R.id.textViewTotalAccounts); createWelcomeView(view); setUpAccountsList(view); prgAccountBills = (ProgressBar) view.findViewById(R.id.progressAccountBills); mFloatingActionButton = (FloatingActionButton) view.findViewById(R.id.fab); mFloatingActionButton.setOnClickListener(new OnClickListener() { @Override public void onClick(View v) { Intent intent = new Intent(getActivity(), CheckingTransactionEditActivity.class); intent.setAction(Intent.ACTION_INSERT); startActivity(intent); } }); return view; } @Override public void onViewCreated(View view, @Nullable Bundle savedInstanceState) { super.onViewCreated(view, savedInstanceState); mFloatingActionButton.attachToListView(mExpandableListView); } @Override public void onStart() { super.onStart(); EventBus.getDefault().register(this); } @Override public void onStop() { EventBus.getDefault().unregister(this); super.onStop(); } // Loader event handlers @Override public Loader<Cursor> onCreateLoader(int id, Bundle args) { MmxCursorLoader result; Select query; switch (id) { case LOADER_ACCOUNT_BILLS: setListViewAccountBillsVisible(false); LookAndFeelSettings settings = new AppSettings(getContext()).getLookAndFeelSettings(); // compose whereClause String where = ""; // check if show only open accounts if (settings.getViewOpenAccounts()) { where = "LOWER(" + QueryAccountBills.STATUS + ")='open'"; } // check if show fav accounts if (settings.getViewFavouriteAccounts()) { where = "LOWER(" + QueryAccountBills.FAVORITEACCT + ")='true'"; } QueryAccountBills queryAccountBills = new QueryAccountBills(getActivity()); query = new Select(queryAccountBills.getAllColumns()) .where(where) .orderBy(QueryAccountBills.ACCOUNTTYPE + ", upper(" + QueryAccountBills.ACCOUNTNAME + ")"); result = new MmxCursorLoader(getActivity(), queryAccountBills.getUri(), query); break; case LOADER_INCOME_EXPENSES: // todo: Get custom period. pref_income_expense_footer_period // String period = new AppSettings(getContext()).getBehaviourSettings().getIncomeExpensePeriod(); // String transactionsFilter = generator.getWhereClauseForPeriod(period); // report.filterTransactionsSource(transactionsFilter); String whereStatement = IncomeVsExpenseReportEntity.Month + "=" + Integer.toString(Calendar.getInstance().get(Calendar.MONTH) + 1) + " AND " + IncomeVsExpenseReportEntity.YEAR + "=" + Integer.toString(Calendar.getInstance().get(Calendar.YEAR)); QueryReportIncomeVsExpenses report = new QueryReportIncomeVsExpenses(getActivity()); query = new Select(report.getAllColumns()) .where(whereStatement); result = new MmxCursorLoader(getActivity(), report.getUri(), query); break; default: result = null; } return result; } @Override public void onLoaderReset(Loader<Cursor> loader) { switch (loader.getId()) { case LOADER_ACCOUNT_BILLS: txtTotalAccounts.setText(mCurrencyService.getBaseCurrencyFormatted(MoneyFactory.fromString("0"))); setListViewAccountBillsVisible(false); mAccountsByType.clear(); mTotalsByType.clear(); mAccountTypes.clear(); break; } } @Override public void onLoadFinished(Loader<Cursor> loader, Cursor data) { switch (loader.getId()) { case LOADER_ACCOUNT_BILLS: try { renderAccountsList(data); } catch (Exception e) { Timber.e(e, "rendering account list"); } // set total for accounts in the main Drawer. EventBus.getDefault().post(new AccountsTotalLoadedEvent(txtTotalAccounts.getText().toString())); break; case LOADER_INCOME_EXPENSES: double income = 0, expenses = 0; if (data != null) { while (data.moveToNext()) { expenses = data.getDouble(data.getColumnIndex(IncomeVsExpenseReportEntity.Expenses)); income = data.getDouble(data.getColumnIndex(IncomeVsExpenseReportEntity.Income)); } } TextView txtIncome = (TextView) getActivity().findViewById(R.id.textViewIncome); TextView txtExpenses = (TextView) getActivity().findViewById(R.id.textViewExpenses); TextView txtDifference = (TextView) getActivity().findViewById(R.id.textViewDifference); // set value if (txtIncome != null) txtIncome.setText(mCurrencyService.getCurrencyFormatted(mCurrencyService.getBaseCurrencyId(), MoneyFactory.fromDouble(income))); if (txtExpenses != null) txtExpenses.setText(mCurrencyService.getCurrencyFormatted(mCurrencyService.getBaseCurrencyId(), MoneyFactory.fromDouble(Math.abs(expenses)))); if (txtDifference != null) txtDifference.setText(mCurrencyService.getCurrencyFormatted(mCurrencyService.getBaseCurrencyId(), MoneyFactory.fromDouble(income - Math.abs(expenses)))); // manage progressbar final ProgressBar barIncome = (ProgressBar) getActivity().findViewById(R.id.progressBarIncome); final ProgressBar barExpenses = (ProgressBar) getActivity().findViewById(R.id.progressBarExpenses); if (barIncome != null && barExpenses != null) { barIncome.setMax((int) (Math.abs(income) + Math.abs(expenses))); barExpenses.setMax((int) (Math.abs(income) + Math.abs(expenses))); if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.HONEYCOMB) { ObjectAnimator animationIncome = ObjectAnimator.ofInt(barIncome, "progress", (int) Math.abs(income)); animationIncome.setDuration(1000); // 0.5 second animationIncome.setInterpolator(new DecelerateInterpolator()); animationIncome.start(); ObjectAnimator animationExpenses = ObjectAnimator.ofInt(barExpenses, "progress", (int) Math.abs(expenses)); animationExpenses.setDuration(1000); // 0.5 second animationExpenses.setInterpolator(new DecelerateInterpolator()); animationExpenses.start(); } else { barIncome.setProgress((int) Math.abs(income)); barExpenses.setProgress((int) Math.abs(expenses)); } } break; } // Close the cursor. MmxDatabaseUtils.closeCursor(data); } // Menu @Override public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) { inflater.inflate(R.menu.home_menu, menu); super.onCreateOptionsMenu(menu, inflater); } @Override public boolean onOptionsItemSelected(MenuItem item) { boolean result = false; switch (item.getItemId()) { case R.id.menu_search: startActivity(new Intent(getActivity(), SearchActivity.class)); result = true; break; } if (result) { return true; } else { return super.onOptionsItemSelected(item); } } // End menu. @Override public void onResume() { super.onResume(); refreshSettings(); // Toolbar Activity parent = getActivity(); if (parent instanceof AppCompatActivity) { AppCompatActivity activity = (AppCompatActivity) getActivity(); // show title activity.getSupportActionBar().setDisplayShowTitleEnabled(true); // Show db name in toolbar. String dbPath = new AppSettings(activity).getDatabaseSettings().getDatabasePath(); String dbFileName = FilenameUtils.getBaseName(dbPath); activity.getSupportActionBar().setSubtitle(dbFileName); } // reload data. startLoaders(); } // Context menu /** * Context menu for account entries. */ @Override public void onCreateContextMenu(ContextMenu menu, View v, ContextMenu.ContextMenuInfo menuInfo) { super.onCreateContextMenu(menu, v, menuInfo); if (!(v instanceof ExpandableListView)) return; ExpandableListView.ExpandableListContextMenuInfo info = (ExpandableListView.ExpandableListContextMenuInfo) menuInfo; int type = ExpandableListView.getPackedPositionType(info.packedPosition); int groupPosition = ExpandableListView.getPackedPositionGroup(info.packedPosition); int childPosition = ExpandableListView.getPackedPositionChild(info.packedPosition); // ignore long-press on group items. if (type != ExpandableListView.PACKED_POSITION_TYPE_CHILD) return; // get adapter. HomeAccountsExpandableAdapter accountsAdapter = (HomeAccountsExpandableAdapter) mExpandableListView.getExpandableListAdapter(); Object childItem = accountsAdapter.getChild(groupPosition, childPosition); QueryAccountBills account = (QueryAccountBills) childItem; // menu.setHeaderIcon(android.R.drawable.ic_menu_manage); menu.setHeaderTitle(account.getAccountName()); String[] menuItems = getResources().getStringArray(R.array.context_menu_account_dashboard); for(String menuItem : menuItems) { menu.add(menuItem); } // balance account should work only for transaction accounts. AccountService service = new AccountService(getActivity()); List<String> accountTypes = service.getTransactionAccountTypeNames(); String accountType = account.getAccountType(); if (accountTypes.contains(accountType)) { menu.add(R.string.balance_account); } // Investment menu items. if (accountType.equals(AccountTypes.INVESTMENT.toString())) { menu.add(Menu.NONE, ContextMenuIds.Portfolio.getId(), 0, getString(R.string.portfolio)); } } @Override public boolean onContextItemSelected(android.view.MenuItem item) { boolean result = false; // get account id QueryAccountBills account = getSelectedAccount(item); if (account == null) return false; int accountId = account.getAccountId(); // get the action String menuItemTitle = item.getTitle().toString(); if (menuItemTitle.equalsIgnoreCase(getString(R.string.edit))) { Intent intent = new Intent(getActivity(), AccountEditActivity.class); intent.putExtra(AccountEditActivity.KEY_ACCOUNT_ID, accountId); intent.setAction(Intent.ACTION_EDIT); startActivity(intent); result = true; } if (menuItemTitle.equalsIgnoreCase(getString(R.string.balance_account))) { startBalanceAccount(account); } if (menuItemTitle.equalsIgnoreCase(getString(R.string.portfolio))) { EventBus.getDefault().post(new RequestPortfolioFragmentEvent(accountId)); } return result; } // Other @Override public void onSaveInstanceState(Bundle outState) { // outState.putInt(TAG_BALANCE_ACCOUNT, this.accountBalancedId); Icepick.saveInstanceState(this, outState); } // Events @Subscribe public void onEvent(AmountEnteredEvent event) { QueryAccountBills account = this.getAccountBeingBalanced(); Money currentBalance = MoneyFactory.fromDouble(account.getTotal()); // calculate the diff. Money newBalance = event.amount; if (newBalance.compareTo(currentBalance) == 0) return; Money difference; TransactionTypes transactionType; if (newBalance.compareTo(currentBalance) > 0) { // new balance > current balance difference = newBalance.subtract(currentBalance); transactionType = TransactionTypes.Deposit; } else { // new balance < current balance difference = currentBalance.subtract(newBalance); transactionType = TransactionTypes.Withdrawal; } // open a new transaction screen to create a transaction to balance to the entered amount. Intent intent = new Intent(getContext(), CheckingTransactionEditActivity.class); intent.setAction(Intent.ACTION_INSERT); // add balance and transaction type and payee IntentDataParameters params = new IntentDataParameters(); params.accountName = account.getAccountName(); params.transactionType = transactionType; params.payeeName = getContext().getString(R.string.balance_adjustment); params.amount = difference; params.categoryName = getContext().getString(R.string.cash); intent.setData(params.toUri()); getContext().startActivity(intent); } // Public public void startLoaders() { LoaderManager loaderManager = getLoaderManager(); // loaderManager.restartLoader(LOADER_USER_NAME, null, this); loadUsername(); loaderManager.restartLoader(LOADER_ACCOUNT_BILLS, null, this); loaderManager.restartLoader(LOADER_INCOME_EXPENSES, null, this); } public void startBalanceAccount(QueryAccountBills account) { setAccountBeingBalanced(account); // get the amount via input binaryDialog. int currencyId = account.getCurrencyId(); AmountInputDialog dialog = AmountInputDialog.getInstance(REQUEST_BALANCE_ACCOUNT, MoneyFactory.fromString("0"), currencyId, true); dialog.setTargetFragment(this, REQUEST_BALANCE_ACCOUNT); dialog.show(getActivity().getSupportFragmentManager(), TAG_BALANCE_ACCOUNT); // the task continues in onFinishedInputAmountDialog } // Private custom methods. private void addFooterToExpandableListView(double curTotal, double curReconciled) { // manage footer list view if (linearFooter == null) { linearFooter = (LinearLayout) getActivity().getLayoutInflater().inflate(R.layout.item_account_bills, null); // text view into layout txtFooterSummary = (TextView) linearFooter.findViewById(R.id.textViewItemAccountTotal); txtFooterSummaryReconciled = (TextView) linearFooter.findViewById(R.id.textViewItemAccountTotalReconciled); if(mHideReconciled) { txtFooterSummaryReconciled.setVisibility(View.GONE); } // set text TextView txtTextSummary = (TextView) linearFooter.findViewById(R.id.textViewItemAccountName); txtTextSummary.setText(R.string.summary); // invisible image ImageView imgSummary = (ImageView) linearFooter.findViewById(R.id.imageViewAccountType); imgSummary.setVisibility(View.INVISIBLE); // set color textview txtTextSummary.setTextColor(Color.GRAY); txtFooterSummary.setTextColor(Color.GRAY); if(!mHideReconciled) { txtFooterSummaryReconciled.setTextColor(Color.GRAY); } } // remove footer mExpandableListView.removeFooterView(linearFooter); // set text txtTotalAccounts.setText(mCurrencyService.getBaseCurrencyFormatted(MoneyFactory.fromDouble(curTotal))); txtFooterSummary.setText(txtTotalAccounts.getText()); if(!mHideReconciled) { txtFooterSummaryReconciled.setText(mCurrencyService.getBaseCurrencyFormatted(MoneyFactory.fromDouble(curReconciled))); } // add footer mExpandableListView.addFooterView(linearFooter, null, false); } private void createWelcomeView(View view) { linearWelcome = (ViewGroup) view.findViewById(R.id.linearLayoutWelcome); // basic preferences Button buttonSettings = (Button) view.findViewById(R.id.buttonSettings); if (buttonSettings != null) { buttonSettings.setOnClickListener(new OnClickListener() { @Override public void onClick(View v) { startActivity(new Intent(getActivity(), SettingsActivity.class)); } }); } // Show current database TextView currentDatabaseTextView = (TextView) view.findViewById(R.id.currentDatabaseTextView); if (currentDatabaseTextView != null) { String path = MoneyManagerApplication.getDatabasePath(getActivity()); currentDatabaseTextView.setText(path); } // add account button Button btnAddAccount = (Button) view.findViewById(R.id.buttonAddAccount); if (btnAddAccount != null) { btnAddAccount.setOnClickListener(new OnClickListener() { @Override public void onClick(View v) { Intent intent = new Intent(getActivity(), AccountEditActivity.class); intent.setAction(Intent.ACTION_INSERT); startActivity(intent); } }); } // Database migration v1.4 -> v2.0 location. setUpMigrationButton(view); } private QueryAccountBills getAccountBeingBalanced() { if (this.accountBeingBalanced == null) { AccountRepository repository = new AccountRepository(getContext()); this.accountBeingBalanced = repository.loadAccountBills(this.accountBalancedId); } return this.accountBeingBalanced; } /** * Load username on the background thread, set the username and then fire an event. * This should be moved to the MainActivity, which actually handles the event and updates the UI. */ private void loadUsername() { ((MmxBaseFragmentActivity) getActivity()).compositeSubscription.add( Single.fromCallable(new Callable<String>() { @Override public String call() throws Exception { InfoService infoService = new InfoService(getActivity()); return infoService.getInfoValue(InfoKeys.USERNAME); } }) .subscribeOn(Schedulers.io()) .observeOn(AndroidSchedulers.mainThread()) .subscribe(new SingleSubscriber<String>() { @Override public void onSuccess(String value) { MoneyManagerApplication.getApp().setUserName(value); EventBus.getDefault().post(new UsernameLoadedEvent()); } @Override public void onError(Throwable error) { Timber.e(error, "error loading username"); } }) ); } private void setAccountBeingBalanced(QueryAccountBills account) { this.accountBeingBalanced = account; accountBalancedId = account.getAccountId(); } /** * @param visible if visible set true show the listview; false show progressbar */ private void setListViewAccountBillsVisible(boolean visible) { if (visible) { mExpandableListView.setVisibility(View.VISIBLE); prgAccountBills.setVisibility(View.GONE); } else { mExpandableListView.setVisibility(View.GONE); prgAccountBills.setVisibility(View.VISIBLE); } } private void setUpAccountsList(View view) { mExpandableListView = getExpandableListView(view); // Handle clicking on an account. mExpandableListView.setOnChildClickListener(new ExpandableListView.OnChildClickListener() { @Override public boolean onChildClick(ExpandableListView parent, View v, int groupPosition, int childPosition, long id) { QueryAccountBills selectedAccount = mAccountsByType.get(mAccountTypes.get(groupPosition)) .get(childPosition); if (selectedAccount == null) return false; int accountId = selectedAccount.getAccountId(); String accountType = mAccountTypes.get(groupPosition); Object event; if (accountType.equalsIgnoreCase(AccountTypes.INVESTMENT.toString())) { event = new RequestWatchlistFragmentEvent(accountId); } else { event = new RequestAccountFragmentEvent(accountId); } EventBus.getDefault().post(event); return true; } }); // store preferences when groups are collapsed/expanded mExpandableListView.setOnGroupCollapseListener(new ExpandableListView.OnGroupCollapseListener() { @Override public void onGroupCollapse(int groupPosition) { // update collapsed group setting final boolean groupVisible = false; // update each group visibility into its own preferences. AppSettings settings = new AppSettings(getActivity()); String key = getSettingsKeyFromGroupPosition(groupPosition); // store value. settings.set(key, groupVisible); } }); mExpandableListView.setOnGroupExpandListener(new ExpandableListView.OnGroupExpandListener() { @Override public void onGroupExpand(int groupPosition) { // update expanded group setting final boolean groupVisible = true; // update each group visibility into its own preferences. AppSettings settings = new AppSettings(getActivity()); String key = getSettingsKeyFromGroupPosition(groupPosition); // store value. settings.set(key, groupVisible); } }); registerForContextMenu(mExpandableListView); } private String getSettingsKeyFromGroupPosition(int groupPosition) { // get group name from position String accountType = mAccountTypes.get(groupPosition); String key = getActivity().getString(PreferenceConstants.PREF_DASHBOARD_GROUP_VISIBLE); key += "-" + accountType; return key; } private void setVisibilityOfAccountGroups() { // set visibility of the account groups. AppSettings settings = new AppSettings(getContext()); // Expand groups based on their visibility preferences. for (int i = 0; i < mAccountTypes.size(); i++) { // Check saved visibility preferences. Some groups might be collapsed. String key = getSettingsKeyFromGroupPosition(i); Boolean expanded = settings.get(key, true); if(expanded) { mExpandableListView.expandGroup(i); } } } private void setUpMigrationButton(View view) { // check if there is a database at the old location. final DatabaseMigrator14To20 migrator = new DatabaseMigrator14To20(getActivity()); boolean legacyDataExists = migrator.legacyDataExists(); // hide option if there is no old database. if (!legacyDataExists) return; // otherwise show the options LinearLayout panel = (LinearLayout) view.findViewById(R.id.panelMigration); panel.setVisibility(View.VISIBLE); // e events, etc. Button migrateDatabaseButton = (Button) view.findViewById(R.id.buttonMigrateDatabase); if (migrateDatabaseButton != null) { if (!migrator.legacyDataExists()) { // add handler OnClickListener migrateClickListener = new OnClickListener() { @Override public void onClick(View view) { boolean migrationSuccess = migrator.migrateLegacyDatabase(); if (migrationSuccess) { Toast.makeText(getActivity(), R.string.database_migrate_14_to_20_success, Toast.LENGTH_LONG).show(); } else { Toast.makeText(getActivity(), R.string.database_migrate_14_to_20_failure, Toast.LENGTH_LONG).show(); } } }; migrateDatabaseButton.setOnClickListener(migrateClickListener); } else { // hide migration notification. RobotoTextView textMigrate = (RobotoTextView) view.findViewById(R.id.textMigrateDatabase); textMigrate.setVisibility(View.GONE); migrateDatabaseButton.setVisibility(View.GONE); } } } private QueryAccountBills getSelectedAccount(android.view.MenuItem item){ ExpandableListView.ExpandableListContextMenuInfo info = null; ContextMenu.ContextMenuInfo menuInfo = item.getMenuInfo(); // clicking any context item in child fragments will also come here. We need only // the context menu items from the Home fragment. if (menuInfo instanceof ExpandableListView.ExpandableListContextMenuInfo) { info = (ExpandableListView.ExpandableListContextMenuInfo) menuInfo; } if (info == null) return null; int groupPos, childPos; int type = ExpandableListView.getPackedPositionType(info.packedPosition); if (type != ExpandableListView.PACKED_POSITION_TYPE_CHILD) return null; // Get the account. groupPos = ExpandableListView.getPackedPositionGroup(info.packedPosition); childPos = ExpandableListView.getPackedPositionChild(info.packedPosition); HomeAccountsExpandableAdapter accountsAdapter = (HomeAccountsExpandableAdapter) mExpandableListView.getExpandableListAdapter(); QueryAccountBills account = null; try { account = (QueryAccountBills) accountsAdapter.getChild(groupPos, childPos); } catch (Exception e) { Timber.e(e, "getting the selected account id"); } return account; } private void renderAccountsList(Cursor cursor) { linearHome.setVisibility(cursor != null && cursor.getCount() > 0 ? View.VISIBLE : View.GONE); linearWelcome.setVisibility(linearHome.getVisibility() == View.GONE ? View.VISIBLE : View.GONE); mAccountsByType.clear(); mTotalsByType.clear(); mAccountTypes.clear(); mGrandTotal = MoneyFactory.fromDouble(0); mGrandReconciled = MoneyFactory.fromDouble(0); // display individual accounts with balances if (cursor != null) { showAccountTotals(cursor); } // write accounts total addFooterToExpandableListView(mGrandTotal.toDouble(), mGrandReconciled.toDouble()); // create adapter HomeAccountsExpandableAdapter expandableAdapter = new HomeAccountsExpandableAdapter(getActivity(), mAccountTypes, mAccountsByType, mTotalsByType, mHideReconciled); // set adapter and shown mExpandableListView.setAdapter(expandableAdapter); setVisibilityOfAccountGroups(); setListViewAccountBillsVisible(true); } private void showAccountTotals(Cursor cursor) { while (cursor.moveToNext()) { QueryAccountBills accountTransaction = new QueryAccountBills(getActivity()); accountTransaction.setValueFromCursor(cursor); double total = accountTransaction.getTotalBaseConvRate(); mGrandTotal = mGrandTotal.add(MoneyFactory.fromDouble(total)); double totalReconciled = accountTransaction.getReconciledBaseConvRate(); mGrandReconciled = mGrandReconciled.add(MoneyFactory.fromDouble(totalReconciled)); String accountType = accountTransaction.getAccountType().toLowerCase(); QueryAccountBills totalForType; if (mAccountTypes.indexOf(accountType) == -1) { // add to the list of account types mAccountTypes.add(accountType); totalForType = new QueryAccountBills(getActivity()); totalForType.setAccountType(accountType); // set group title if (AccountTypes.CASH.name().equalsIgnoreCase(accountType)) { totalForType.setAccountName(getString(R.string.cash_accounts)); } else if (AccountTypes.CHECKING.toString().equalsIgnoreCase(accountType)) { totalForType.setAccountName(getString(R.string.bank_accounts)); } else if (AccountTypes.TERM.toString().equalsIgnoreCase(accountType)) { totalForType.setAccountName(getString(R.string.term_accounts)); } else if (AccountTypes.CREDIT_CARD.toString().equalsIgnoreCase(accountType)) { totalForType.setAccountName(getString(R.string.credit_card_accounts)); } else if (AccountTypes.INVESTMENT.toString().equalsIgnoreCase(accountType)) { totalForType.setAccountName(getString(R.string.investment_accounts)); } mTotalsByType.put(accountType, totalForType); } totalForType = mTotalsByType.get(accountType); double reconciledBaseConversionRate = totalForType.getReconciledBaseConvRate() + accountTransaction.getReconciledBaseConvRate(); totalForType.setReconciledBaseConvRate(reconciledBaseConversionRate); double totalBaseConversionRate = totalForType.getTotalBaseConvRate() + accountTransaction.getTotalBaseConvRate(); totalForType.setTotalBaseConvRate(totalBaseConversionRate); List<QueryAccountBills> listOfAccountsOfType = mAccountsByType.get(accountType); if (listOfAccountsOfType == null) { listOfAccountsOfType = new ArrayList<>(); mAccountsByType.put(accountType, listOfAccountsOfType); } if (!listOfAccountsOfType.contains(accountTransaction)) { listOfAccountsOfType.add(accountTransaction); } } } private ExpandableListView getExpandableListView(View view) { mExpandableListView = (ExpandableListView) view.findViewById(R.id.listViewAccountBills); return mExpandableListView; } private void refreshSettings() { AppSettings settings = new AppSettings(getActivity()); mHideReconciled = settings.getLookAndFeelSettings().getHideReconciledAmounts(); if (txtFooterSummaryReconciled != null) { txtFooterSummaryReconciled.setVisibility(mHideReconciled ? View.GONE : View.VISIBLE); } } }