package com.evancharlton.mileage; import java.text.DecimalFormat; import java.util.ArrayList; import java.util.Calendar; import java.util.HashMap; import java.util.Map; import android.app.Activity; import android.database.Cursor; import android.database.CursorIndexOutOfBoundsException; import android.os.Bundle; import android.view.View; import android.widget.AdapterView; import android.widget.SimpleCursorAdapter; import android.widget.Spinner; import android.widget.TextView; public class StatisticsView extends Activity { private HashMap<Integer, TextView> m_stats = new HashMap<Integer, TextView>(); private Spinner m_vehicles; private ArrayList<Double> m_amounts; private ArrayList<Double> m_costs; private ArrayList<Long> m_dates; private ArrayList<Double> m_miles; public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.statistics); initUI(); populateSpinner(); } private void initUI() { m_vehicles = (Spinner) findViewById(R.id.stats_vehicle_spinner); m_vehicles.setOnItemSelectedListener(new AdapterView.OnItemSelectedListener() { public void onItemSelected(AdapterView<?> arg0, View arg1, int arg2, long arg3) { calculateStatistics(m_vehicles.getSelectedItemId()); } public void onNothingSelected(AdapterView<?> arg0) { // uh, do nothing? } }); } private void getTextView(int id) { m_stats.put(id, (TextView) findViewById(id)); } private void populateSpinner() { Cursor c = managedQuery(Vehicles.CONTENT_URI, new String[] { Vehicles._ID, Vehicles.TITLE }, null, null, Vehicles.DEFAULT_SORT_ORDER); SimpleCursorAdapter vehicleAdapter = new SimpleCursorAdapter(this, android.R.layout.simple_spinner_item, c, new String[] { Vehicles.TITLE }, new int[] { android.R.id.text1 }); vehicleAdapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item); m_vehicles.setAdapter(vehicleAdapter); if (vehicleAdapter.getCount() == 1) { m_vehicles.setVisibility(View.GONE); calculateStatistics(vehicleAdapter.getItemId(0)); } } private void calculateStatistics(long id) { String[] projection = new String[] { FillUps.AMOUNT, FillUps.COST, FillUps.DATE, FillUps.MILEAGE }; Cursor c = managedQuery(FillUps.CONTENT_URI, projection, FillUps.VEHICLE_ID + " = ?", new String[] { String.valueOf(id) }, FillUps.DATE + " DESC, " + FillUps.MILEAGE + " DESC"); HashMap<Integer, String> calculatedData = new HashMap<Integer, String>(); m_amounts = new ArrayList<Double>(); m_costs = new ArrayList<Double>(); m_dates = new ArrayList<Long>(); m_miles = new ArrayList<Double>(); int count = 0; c.moveToFirst(); int num = c.getCount(); while (num > 0) { try { m_amounts.add(c.getDouble(0)); m_costs.add(c.getDouble(1)); m_dates.add(c.getLong(2)); m_miles.add(c.getDouble(3)); c.moveToNext(); } catch (CursorIndexOutOfBoundsException e) { break; } count++; num--; } if (count == 0) { return; } calculatedData.putAll(distanceStats()); calculatedData.putAll(economyStats()); calculatedData.putAll(costStats()); // update the text for (Integer dataId : calculatedData.keySet()) { setText(dataId, calculatedData.get(dataId)); } } private Map<Integer, String> distanceStats() { HashMap<Integer, String> data = new HashMap<Integer, String>(); // total distance tracked double total_distance = 0.0D; // distance between last two fill-ups double running_distance = 0.0D; if (m_miles.size() > 1) { running_distance = Math.abs(m_miles.get(0) - m_miles.get(1)); total_distance = Math.abs(m_miles.get(0) - m_miles.get(m_miles.size() - 1)); } double max_distance = 0.0D; double min_distance = Double.MAX_VALUE; for (int i = 0; i < m_miles.size() - 1; i++) { double diff = m_miles.get(i) - m_miles.get(i + 1); if (diff > max_distance) { max_distance = diff; } if (diff < min_distance) { min_distance = diff; } } double avg_distance = total_distance / (m_miles.size() - 1); data.put(R.id.stats_distance_running, string(running_distance)); data.put(R.id.stats_distance_average, string(avg_distance)); data.put(R.id.stats_distance_maximum, string(max_distance)); data.put(R.id.stats_distance_minimum, string(min_distance)); return data; } private Map<Integer, String> economyStats() { HashMap<Integer, String> data = new HashMap<Integer, String>(); double average_mpg = 0.0D; double minimum_mpg = Double.MAX_VALUE; double maximum_mpg = 0.0D; double total_miles = 0.0D; double total_fuel = 0.0D; double running_mpg = 0.0D; for (int i = 0; i < m_amounts.size() - 1; i++) { total_fuel += m_amounts.get(i); double mile_diff = m_miles.get(i) - m_miles.get(i + 1); total_miles += mile_diff; double mpg = mile_diff / m_amounts.get(i); if (mpg > maximum_mpg) { maximum_mpg = mpg; } if (mpg < minimum_mpg) { minimum_mpg = mpg; } if (i == 0) { running_mpg = mpg; } } // note that you don't sum up ALL of m_amounts because the last one // (which is the oldest in history terms) does not relate to the average // mileage. average_mpg = total_miles / total_fuel; data.put(R.id.stats_economy_average, string(average_mpg)); data.put(R.id.stats_economy_maximum, string(maximum_mpg)); data.put(R.id.stats_economy_minimum, string(minimum_mpg)); data.put(R.id.stats_economy_running, string(running_mpg)); return data; } private Map<Integer, String> costStats() { HashMap<Integer, String> data = new HashMap<Integer, String>(); double total_cost = 0.0D; double lowest_ppg = Double.MAX_VALUE; double highest_ppg = 0.0D; double total_fuel = 0.0D; double highest_amt = 0.0D; double lowest_amt = Double.MAX_VALUE; double lowest_cost = Double.MAX_VALUE; double highest_cost = 0.0D; double total_expense = 0.0D; double thirty_day_cost = 0.0D; long then = System.currentTimeMillis() - (30L * 24L * 60L * 60L * 1000L); Calendar then_c = Calendar.getInstance(); then_c.setTimeInMillis(then); for (int i = 0; i < m_costs.size(); i++) { double cost_ppg = m_costs.get(i); if (cost_ppg > highest_ppg) { highest_ppg = cost_ppg; } if (cost_ppg < lowest_ppg) { lowest_ppg = cost_ppg; } double amount = m_amounts.get(i); if (amount > highest_amt) { highest_amt = amount; } if (amount < lowest_amt) { lowest_amt = amount; } double cost = cost_ppg * amount; if (cost > highest_cost) { highest_cost = cost; } if (cost < lowest_cost) { lowest_cost = cost; } long date = m_dates.get(i); if (date > then) { thirty_day_cost += cost; } total_cost += cost_ppg; total_fuel += amount; total_expense += cost; } data.put(R.id.stats_price_thirty_days, string(thirty_day_cost, "$")); data.put(R.id.stats_price_latest, string(m_costs.get(0), "$")); data.put(R.id.stats_price_average, string(total_cost / m_costs.size(), "$")); data.put(R.id.stats_price_running, string(total_expense, "$")); data.put(R.id.stats_price_minimum, string(lowest_ppg, "$")); data.put(R.id.stats_price_maximum, string(highest_ppg, "$")); data.put(R.id.stats_amount_running, string(total_fuel)); data.put(R.id.stats_amount_average, string(total_fuel / m_amounts.size())); data.put(R.id.stats_amount_average_cost, string(total_expense / m_costs.size(), "$")); data.put(R.id.stats_amount_maximum, string(highest_amt)); data.put(R.id.stats_amount_minimum, string(lowest_amt)); data.put(R.id.stats_amount_maximum_cost, string(highest_cost, "$")); data.put(R.id.stats_amount_minimum_cost, string(lowest_cost, "$")); return data; } private void setText(int id, String text) { TextView tv = m_stats.get(id); if (tv == null) { getTextView(id); tv = m_stats.get(id); if (tv == null) { throw new IllegalArgumentException("Invalid ID: " + String.valueOf(id)); } } tv.setText(text); } private String string(double val) { return string(val, ""); } private String string(double val, String prefix) { DecimalFormat format = new DecimalFormat("##0.00"); String str = prefix + format.format(val); return str; } }