/*
* 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);
}
}
}