Java tutorial
/******************************************************************************* * Copyright (c) 2010 Denis Solonenko. * All rights reserved. This program and the accompanying materials * are made available under the terms of the GNU Public License v2.0 * which accompanies this distribution, and is available at * http://www.gnu.org/licenses/old-licenses/gpl-2.0.html * * Contributors: * Denis Solonenko - initial API and implementation * Abdsandryk Souza - implementing 2D chart reports * Emmanuel Florent - port to Android API 11+ ******************************************************************************/ package com.flowzr.activity; import java.util.Calendar; import java.util.Collection; import java.util.GregorianCalendar; import com.flowzr.R; import com.flowzr.db.DatabaseAdapter; import com.flowzr.db.MyEntityManager; import com.flowzr.filter.DateTimeCriteria; import com.flowzr.filter.WhereFilter; import com.flowzr.graph.Report2DChart; import com.flowzr.model.Currency; import com.flowzr.model.ReportDataByPeriod; import com.flowzr.report.*; import com.flowzr.utils.CurrencyCache; import com.flowzr.utils.MyPreferences; import com.flowzr.utils.PinProtection; import com.flowzr.utils.Utils; import com.flowzr.view.Report2DChartView; import android.app.Activity; import android.app.AlertDialog; import android.content.Context; import android.content.DialogInterface; import android.content.Intent; import android.database.Cursor; import android.os.Bundle; import android.support.v4.app.Fragment; import android.view.Menu; import android.view.MenuInflater; import android.view.MenuItem; import android.view.View; import android.view.View.OnClickListener; import android.widget.ImageButton; import android.widget.ListAdapter; import android.widget.TextView; public class Report2DChartActivity extends AbstractListFragment { // option menu references private static final int MENU_PREFERENCES = 1; // activity result identifier to get results back public static final int REPORT_PREFERENCES = 1; // Data to display private Report2DChart reportData; private DatabaseAdapter dbAdapter; private MyEntityManager em; // user interface elements private ImageButton bPrevious; private ImageButton bNext; private int selectedPeriod; private Currency currency; private Calendar startPeriod; private int reportType; // array of string report preferences to identify changes String[] initialPrefs; // boolean to check if preferred currency is set private boolean prefCurNotSet = false; // boolean to check if preferred period is set private boolean prefPerNotSet = false; public Report2DChartActivity() { super(R.layout.report_2d); } @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); // get report type int type = 0; Bundle bundle = getArguments(); if (bundle != null) { db = new DatabaseAdapter(getActivity()); db.open(); type = bundle.getInt(Report2DChart.REPORT_TYPE, 0); } this.reportType = type; } @Override public void onActivityCreated(Bundle savedInstanceState) { super.onActivityCreated(savedInstanceState); init(); } /** * Initialize activity. */ private void init() { // database adapter to query data dbAdapter = new DatabaseAdapter(this.getActivity()); dbAdapter.open(); em = dbAdapter.em(); // get report preferences to display chart // Reference Currency currency = getReferenceCurrency(); // Period of Reference int periodLength = getPeriodOfReference(); selectedPeriod = periodLength - 3; // check report preferences for reference month different of current month setStartPeriod(periodLength); boolean built = false; switch (reportType) { case Report2DChart.REPORT_ACCOUNT_BY_PERIOD: reportData = new AccountByPeriodReport(this.getActivity(), em, startPeriod, periodLength, currency); ((EntityListActivity) getActivity()) .setMyTitle(getResources().getString(R.string.report_by_account_by_period_summary)); break; case Report2DChart.REPORT_CATEGORY_BY_PERIOD: reportData = new CategoryByPeriodReport(this.getActivity(), em, startPeriod, periodLength, currency); ((EntityListActivity) getActivity()) .setMyTitle(getResources().getString(R.string.report_by_category_by_period_summary)); break; case Report2DChart.REPORT_PAYEE_BY_PERIOD: reportData = new PayeeByPeriodReport(this.getActivity(), em, startPeriod, periodLength, currency); ((EntityListActivity) getActivity()) .setMyTitle(getResources().getString(R.string.report_by_payee_by_period_summary)); break; case Report2DChart.REPORT_LOCATION_BY_PERIOD: reportData = new LocationByPeriodReport(this.getActivity(), em, startPeriod, periodLength, currency); ((EntityListActivity) getActivity()) .setMyTitle(getResources().getString(R.string.report_by_location_by_period_summary)); break; case Report2DChart.REPORT_PROJECT_BY_PERIOD: reportData = new ProjectByPeriodReport(this.getActivity(), em, startPeriod, periodLength, currency); ((EntityListActivity) getActivity()) .setMyTitle(getResources().getString(R.string.report_by_project_by_period_summary)); break; } if (reportData != null && reportData.hasFilter()) { refreshView(); built = true; } else { // There is no <location, project or category> available for filtering data. alertNoFilter(reportData.getNoFilterMessage(this.getActivity())); adjustLabels(); } if (built && (prefCurNotSet || prefPerNotSet)) { alertPreferencesNotSet(prefCurNotSet, prefPerNotSet); } // period length ((TextView) getView().findViewById(R.id.report_period)).setOnClickListener(new OnClickListener() { @Override public void onClick(View arg0) { // pop up options to choose the period changePeriodLength(selectedPeriod); } }); ((TextView) getView().findViewById(R.id.report_period)).setFocusable(true); } @Override public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) { menu.clear(); inflater.inflate(R.menu.report2d_actions, menu); super.onCreateOptionsMenu(menu, inflater); } @Override public boolean onOptionsItemSelected(MenuItem item) { // Handle presses on the action bar items switch (item.getItemId()) { case R.id.bt_filter_previous: if (reportData.previousFilter()) { refreshView(); } return true; case R.id.bt_filter_next: if (reportData.nextFilter()) { refreshView(); } return true; case R.id.settings: Intent intent = new Intent(this.getActivity(), ReportPreferencesActivity.class); startActivityForResult(intent, REPORT_PREFERENCES); return true; default: return false; } } /** * Display a message when preferences not set to alert the use of default values. * @param isCurrency Inform if currency is not set on report preferences. * @param isPeriod Inform if period is not set on report preferences. */ private void alertPreferencesNotSet(boolean isCurrency, boolean isPeriod) { // display message: preferences not set String message = ""; if (isCurrency) { if (isPeriod) { // neither currency neither period is set message = getResources().getString(R.string.report_preferences_not_set); } else { // only currency not set message = getResources().getString(R.string.currency_not_set); } } else { if (isPeriod) { // only period not set message = getResources().getString(R.string.period_not_set); } } AlertDialog.Builder dlgAlert = new AlertDialog.Builder(this.getActivity()); dlgAlert.setMessage(message); dlgAlert.setTitle(R.string.reports); dlgAlert.setPositiveButton(R.string.ok, null); dlgAlert.setCancelable(true); dlgAlert.create().show(); } /** * Alert message to warn that there is no filter available (no category, no project, no account or no location) * @param message Message warning the lack of filters by report type. */ private void alertNoFilter(String message) { AlertDialog.Builder dlgAlert = new AlertDialog.Builder(this.getActivity()); dlgAlert.setMessage(message); dlgAlert.setTitle(R.string.reports); dlgAlert.setPositiveButton(R.string.ok, null); dlgAlert.setCancelable(true); dlgAlert.create().show(); } /** * Display a list of period length options to redefine period, rebuild data and refresh view. * @param previousPeriod The previous selected period to check if data changed, rebuild data and refresh view. */ private void changePeriodLength(final int previousPeriod) { final Context context = this.getActivity(); new AlertDialog.Builder(this.getActivity()).setTitle(R.string.period) .setPositiveButton(R.string.ok, new DialogInterface.OnClickListener() { @Override public void onClick(DialogInterface dialog, int which) { processPeriodLengthChange(previousPeriod, true); } }).setSingleChoiceItems(reportData.getPeriodStrings(context), selectedPeriod, new DialogInterface.OnClickListener() { @Override public void onClick(DialogInterface dialog, int which) { selectedPeriod = which; } }) .show(); } /** * Process the period length change * @param previousPeriod The selected period length before changing * @param refresh True if requires refresh, false if refresh will be processed later */ private void processPeriodLengthChange(int previousPeriod, boolean refresh) { if (previousPeriod != selectedPeriod) { reportData.changePeriodLength(reportData.getPeriodOptions()[selectedPeriod]); setStartPeriod(reportData.getPeriodOptions()[selectedPeriod]); reportData.changeStartPeriod(startPeriod); if (refresh) refreshView(); } } /** * Update the view reflecting data changes */ private void refreshView() { // set data to plot if (reportData.hasDataToPlot()) { getView().findViewById(R.id.report_empty).setVisibility(View.GONE); Report2DChartView view = (Report2DChartView) getView().findViewById(R.id.report_2d_chart); view.setDataToPlot(reportData.getPoints(), reportData.getDataBuilder().getMaxValue(), reportData.getDataBuilder().getMinValue(), reportData.getDataBuilder().getAbsoluteMaxValue(), reportData.getDataBuilder().getAbsoluteMinValue(), reportData.getDataBuilder().getMeanExcludingNulls()); ((Report2DChartView) getView().findViewById(R.id.report_2d_chart)).setCurrency(currency); ((Report2DChartView) getView().findViewById(R.id.report_2d_chart)).setVisibility(View.VISIBLE); // set labels view.refresh(); } else { ((TextView) getView().findViewById(R.id.report_empty)).setVisibility(View.VISIBLE); ((Report2DChartView) getView().findViewById(R.id.report_2d_chart)).setVisibility(View.GONE); } // adjust report 2D user interface elements adjustLabels(); fillStatistics(); } /** * Adjust labels after changing report parameters */ private void adjustLabels() { // Period ((TextView) getView().findViewById(R.id.report_period)) .setText(reportData.getFilterName() + " " + reportData.getPeriodLengthString(this.getActivity())); } /** * Fill statistics panel based on report data */ private void fillStatistics() { boolean considerNull = MyPreferences.considerNullResultsInReport(this.getActivity()); Double max; Double min; Double mean; Double sum = new Double(reportData.getDataBuilder().getSum()); if (considerNull) { max = new Double(reportData.getDataBuilder().getMaxValue()); min = new Double(reportData.getDataBuilder().getMinValue()); mean = new Double(reportData.getDataBuilder().getMean()); if ((min * max >= 0)) { // absolute calculation (all points over the x axis) max = new Double(reportData.getDataBuilder().getAbsoluteMaxValue()); min = new Double(reportData.getDataBuilder().getAbsoluteMinValue()); mean = Math.abs(mean); sum = Math.abs(sum); } } else { // exclude impact of null values in statistics max = new Double(reportData.getDataBuilder().getMaxExcludingNulls()); min = new Double(reportData.getDataBuilder().getMinExcludingNulls()); mean = new Double(reportData.getDataBuilder().getMeanExcludingNulls()); if ((min * max >= 0)) { // absolute calculation (all points over the x axis) max = new Double(reportData.getDataBuilder().getAbsoluteMaxExcludingNulls()); min = new Double(reportData.getDataBuilder().getAbsoluteMinExcludingNulls()); mean = Math.abs(mean); sum = Math.abs(sum); } } // chart limits ((TextView) getView().findViewById(R.id.report_max_result)) .setText(Utils.amountToString(reportData.getCurrency(), max.longValue())); ((TextView) getView().findViewById(R.id.report_min_result)) .setText(Utils.amountToString(reportData.getCurrency(), min.longValue())); // sum and mean ((TextView) getView().findViewById(R.id.report_mean_result)) .setText(Utils.amountToString(reportData.getCurrency(), mean.longValue())); ((TextView) getView().findViewById(R.id.report_mean_result)).setTextColor(Report2DChartView.meanColor); //((TextView)getView().findViewById(R.id.report_sum_result)).setText(Utils.amountToString(reportData.getCurrency(), sum.longValue())); } /** * Gets the reference currency registered on preferences or, if not registered, gets the default currency. * @return The currency registered as a reference to display chart reports or the default currency if not configured yet. */ private Currency getReferenceCurrency() { Currency c = MyPreferences.getReferenceCurrency(this.getActivity()); if (c == null) { prefCurNotSet = true; Collection<Currency> currencies = CurrencyCache.getAllCurrencies(); if (currencies != null && currencies.size() > 0) { for (Currency currency : currencies) { if (currency.isDefault) { c = currency; break; } } if (c == null) { c = getNewDefaultCurrency(); } } else { c = getNewDefaultCurrency(); } } return c; } /** * Gets default currency when currency is not set in report preferences. * @return Default currency */ private Currency getNewDefaultCurrency() { return Currency.defaultCurrency(); } // @Override // public boolean onCreateOptionsMenu(Menu menu) { // super.onCreateOptionsMenu(menu); // MenuItem menuItem = menu.add(0, MENU_PREFERENCES, 0, R.string.preferences); // menuItem.setIcon(android.R.drawable.ic_menu_preferences); // return true; // } // // @Override // public boolean onOptionsItemSelected(MenuItem item) { // super.onOptionsItemSelected(item); // switch (item.getItemId()) { // case MENU_PREFERENCES: // // save preferences status before call report preferences activity // initialPrefs = MyPreferences.getReportPreferences(this); // // call report preferences activity asking for result when closed // Intent intent = new Intent(this, ReportPreferencesActivity.class); // startActivityForResult(intent, REPORT_PREFERENCES); // break; // } // return false; // // } @Override public void onActivityResult(int requestCode, int resultCode, Intent data) { // See which child activity is calling us back. if (initialPrefs != null) { boolean changed = preferencesChanged(initialPrefs, MyPreferences.getReportPreferences(this.getActivity())); if (changed) { // rebuild data reportData.rebuild(this.getActivity(), em, startPeriod, reportData.getPeriodOptions()[selectedPeriod], currency); refreshView(); } } } /** * Check if preferences changed * @param initial Preferences status before call Report Preferences Activity. * @param actual Current preferences status. * @return True if preferences changed, false otherwise. */ private boolean preferencesChanged(String[] initial, String[] actual) { boolean changed = false; // general report preferences // 0 reference currency if (!initial[0].equals(actual[0])) { // set reference currency currency = getReferenceCurrency(); changed = true; } // 1 period of reference if (!initial[1].equals(actual[1])) { // change period length to the one set in report preferences int refPeriodLength = getPeriodOfReference(); int previousPeriod = selectedPeriod; selectedPeriod = refPeriodLength - 3; processPeriodLengthChange(previousPeriod, false); changed = true; } // 2 reference month if (!initial[2].equals(actual[2])) { setStartPeriod(reportData.getPeriodOptions()[selectedPeriod]); changed = true; } // 3 consider nulls in statistics (affects statistics only > recalculate) if (!initial[3].equals(actual[3])) { // affects statistics only - recalculate changed = true; } // 4 include <no filter> (rebuild will regenerate the filter Ids list) if (!initial[4].equals(actual[4])) { // the change will be processed in rebuild changed = true; } if (reportType == Report2DChart.REPORT_CATEGORY_BY_PERIOD) { // include sub categories in list (rebuild will regenerate the filter Ids list) if (!initial[5].equals(actual[5])) { // the change will be processed in rebuild changed = true; } // add sub categories result to root categories result (affects statistics only > recalculate) if (!initial[6].equals(actual[6])) changed = true; } return changed; } /** * Set the start period based on given period length and reference month registered in report preferences. * Start period = Reference Month - periodLength months * @param periodLength The number of months to be represented in the 2D report. */ private void setStartPeriod(int periodLength) { int refMonth = MyPreferences.getReferenceMonth(this.getActivity()); Calendar now = Calendar.getInstance(); startPeriod = new GregorianCalendar(now.get(Calendar.YEAR), now.get(Calendar.MONTH), 1); if (refMonth != 0) { startPeriod.add(Calendar.MONTH, refMonth); } // move to start period (reference month - <periodLength> months) startPeriod.add(Calendar.MONTH, (-1) * periodLength + 1); } /** * Get the period of reference set in report preferences. * @return The number of months to be represented in the 2D report. */ private int getPeriodOfReference() { int periodLength = MyPreferences.getPeriodOfReference(this.getActivity()); if (periodLength == 0) { periodLength = ReportDataByPeriod.DEFAULT_PERIOD; prefPerNotSet = true; } return periodLength; } @Override public void onDestroy() { dbAdapter.close(); super.onDestroy(); } @Override protected Cursor createCursor() { // TODO Auto-generated method stub return null; } @Override protected ListAdapter createAdapter(Cursor cursor) { // TODO Auto-generated method stub return null; } @Override protected void deleteItem(View v, int position, long id) { // TODO Auto-generated method stub } @Override protected void editItem(View v, int position, long id) { // TODO Auto-generated method stub } @Override protected void viewItem(View v, int position, long id) { // TODO Auto-generated method stub } @Override protected String getMyTitle() { // TODO Auto-generated method stub return null; } }