package ru.orangesoftware.financisto2.activity;
import android.app.Activity;
import android.app.AlertDialog;
import android.content.Context;
import android.content.DialogInterface;
import android.content.Intent;
import android.os.Bundle;
import android.view.Menu;
import android.view.MenuItem;
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.ImageButton;
import android.widget.TextView;
import java.util.Calendar;
import java.util.Collection;
import java.util.GregorianCalendar;
import ru.orangesoftware.financisto2.R;
import ru.orangesoftware.financisto2.db.DatabaseAdapter;
import ru.orangesoftware.financisto2.db.DatabaseAdapter_;
import ru.orangesoftware.financisto2.graph.Report2DChart;
import ru.orangesoftware.financisto2.model.Currency;
import ru.orangesoftware.financisto2.model.ReportDataByPeriod;
import ru.orangesoftware.financisto2.report.AccountByPeriodReport;
import ru.orangesoftware.financisto2.report.CategoryByPeriodReport;
import ru.orangesoftware.financisto2.report.PayeeByPeriodReport;
import ru.orangesoftware.financisto2.report.ProjectByPeriodReport;
import ru.orangesoftware.financisto2.utils.CurrencyCache;
import ru.orangesoftware.financisto2.utils.MyPreferences;
import ru.orangesoftware.financisto2.utils.PinProtection;
import ru.orangesoftware.financisto2.utils.Utils;
import ru.orangesoftware.financisto2.view.Report2DChartView;
/**
* Activity to display 2D Reports.
* @author Abdsandryk
*/
public class Report2DChartActivity extends Activity {
// 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 db;
// 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;
/** Called when the activity is first created. */
@Override
public void onCreate(Bundle savedInstanceState) {
// initialize activity
super.onCreate(savedInstanceState);
setContentView(R.layout.report_2d);
// get report type
int type = 0;
Intent intent = getIntent();
if (intent != null) {
type = intent.getIntExtra(Report2DChart.REPORT_TYPE, 0);
}
this.reportType = type;
init();
}
/**
* Initialize activity.
*/
private void init() {
// database adapter to query data
db = DatabaseAdapter_.getInstance_(this);
// 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, db, startPeriod, periodLength, currency);
break;
case Report2DChart.REPORT_CATEGORY_BY_PERIOD:
reportData = new CategoryByPeriodReport(this, db, startPeriod, periodLength, currency);
break;
case Report2DChart.REPORT_PAYEE_BY_PERIOD:
reportData = new PayeeByPeriodReport(this, db, startPeriod, periodLength, currency);
break;
case Report2DChart.REPORT_PROJECT_BY_PERIOD:
reportData = new ProjectByPeriodReport(this, db, startPeriod, periodLength, currency);
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));
adjustLabels();
}
if (built && (prefCurNotSet || prefPerNotSet)) {
alertPreferencesNotSet(prefCurNotSet, prefPerNotSet);
}
// previous filter button
bPrevious = (ImageButton) findViewById(R.id.bt_filter_previous);
bPrevious.setOnClickListener(new OnClickListener(){
@Override
public void onClick(View arg0) {
if (reportData.previousFilter()) {
refreshView();
}
}
});
// next filter button
bNext = (ImageButton) findViewById(R.id.bt_filter_next);
bNext.setOnClickListener(new OnClickListener(){
@Override
public void onClick(View arg0) {
if (reportData.nextFilter()) {
refreshView();
}
}
});
// period length
((TextView)findViewById(R.id.report_period)).setOnClickListener(new OnClickListener() {
@Override
public void onClick(View arg0) {
// pop up options to choose the period
changePeriodLength(selectedPeriod);
}
});
((TextView)findViewById(R.id.report_period)).setFocusable(true);
}
/**
* 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);
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);
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;
new AlertDialog.Builder(this)
.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()) {
((TextView)findViewById(R.id.report_empty)).setVisibility(View.GONE);
Report2DChartView view = ((Report2DChartView)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)findViewById(R.id.report_2d_chart)).setCurrency(currency);
((Report2DChartView)findViewById(R.id.report_2d_chart)).setVisibility(View.VISIBLE);
// set labels
view.refresh();
} else {
((TextView)findViewById(R.id.report_empty)).setVisibility(View.VISIBLE);
((Report2DChartView)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() {
// Filter name
((TextView)findViewById(R.id.report_filter_name)).setText(reportData.getFilterName());
// Period
((TextView)findViewById(R.id.report_period)).setText(reportData.getPeriodLengthString(this));
}
/**
* Fill statistics panel based on report data
*/
private void fillStatistics() {
boolean considerNull = MyPreferences.considerNullResultsInReport(this);
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)findViewById(R.id.report_max_result)).setText(Utils.amountToString(reportData.getCurrency(), max.longValue()));
((TextView)findViewById(R.id.report_min_result)).setText(Utils.amountToString(reportData.getCurrency(), min.longValue()));
// sum and mean
((TextView)findViewById(R.id.report_mean_result)).setText(Utils.amountToString(reportData.getCurrency(), mean.longValue()));
((TextView)findViewById(R.id.report_mean_result)).setTextColor(Report2DChartView.meanColor);
((TextView)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);
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
protected 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));
if (changed) {
// rebuild data
reportData.rebuild(this, db, 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);
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);
if (periodLength == 0) {
periodLength = ReportDataByPeriod.DEFAULT_PERIOD;
prefPerNotSet = true;
}
return periodLength;
}
@Override
protected void onPause() {
super.onPause();
PinProtection.lock(this);
}
@Override
protected void onResume() {
super.onResume();
PinProtection.unlock(this);
}
}