package com.evancharlton.mileage; import com.evancharlton.mileage.dao.CachedValue; import com.evancharlton.mileage.dao.Dao; import com.evancharlton.mileage.dao.Field; import com.evancharlton.mileage.dao.Fillup; import com.evancharlton.mileage.dao.FillupField; import com.evancharlton.mileage.dao.FillupSeries; import com.evancharlton.mileage.dao.Vehicle; import com.evancharlton.mileage.exceptions.InvalidFieldException; import com.evancharlton.mileage.math.Calculator; import com.evancharlton.mileage.provider.FillUpsProvider; import com.evancharlton.mileage.provider.Settings; import com.evancharlton.mileage.provider.Settings.DataFormats; import com.evancharlton.mileage.provider.Statistics; import com.evancharlton.mileage.provider.tables.CacheTable; import com.evancharlton.mileage.provider.tables.FieldsTable; import com.evancharlton.mileage.provider.tables.FillupsTable; import com.evancharlton.mileage.provider.tables.VehiclesTable; import com.evancharlton.mileage.services.RecalculateEconomyService; import com.evancharlton.mileage.views.CursorSpinner; import com.evancharlton.mileage.views.DateButton; import com.evancharlton.mileage.views.DividerView; import com.evancharlton.mileage.views.FieldView; import android.app.Activity; import android.content.ContentUris; import android.content.ContentValues; import android.content.Intent; import android.database.Cursor; import android.location.Location; import android.location.LocationManager; import android.net.Uri; import android.os.Bundle; import android.view.LayoutInflater; import android.view.Menu; import android.view.View; import android.widget.AdapterView; import android.widget.CheckBox; import android.widget.EditText; import android.widget.LinearLayout; import java.util.ArrayList; import java.util.HashMap; public class FillupActivity extends BaseFormActivity { private EditText mOdometer; private EditText mVolume; private EditText mPrice; private DateButton mDate; private CursorSpinner mVehicles; private CheckBox mPartial; private LinearLayout mFieldsContainer; private final ArrayList<FieldView> mFields = new ArrayList<FieldView>(); private Fillup mFillup = new Fillup(new ContentValues()); private Bundle mIcicle; private Vehicle mVehicle; @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState, R.layout.fillup); // save the icicle so that we can restore the meta fields later on. mIcicle = savedInstanceState; } private final Vehicle getVehicle() { if (mVehicle == null) { mVehicle = Vehicle.loadById(this, mVehicles.getSelectedItemId()); } if (mVehicle == null) { throw new IllegalStateException("Unable to load vehicle #" + mVehicles.getSelectedItemId()); } return mVehicle; } @Override protected void onResume() { super.onResume(); Cursor fields = managedQuery(Uri.withAppendedPath(FillUpsProvider.BASE_URI, FieldsTable.URI_PATH), FieldsTable.PROJECTION, null, null, null); LayoutInflater inflater = LayoutInflater.from(this); mFieldsContainer.removeAllViews(); HashMap<Long, FillupField> fieldMap = new HashMap<Long, FillupField>(); if (mFillup.isExistingObject()) { // set the fields ArrayList<FillupField> objectFields = mFillup.getFields(this); for (FillupField field : objectFields) { fieldMap.put(field.getTemplateId(), field); } } if (fields.getCount() > 0) { DividerView divider = (DividerView) inflater.inflate(R.layout.divider, mFieldsContainer, false); divider.setText(R.string.divider_fillup_fields); mFieldsContainer.addView(divider); } while (fields.moveToNext()) { String hint = fields.getString(fields.getColumnIndex(Field.TITLE)); long id = fields.getLong(fields.getColumnIndex(Field._ID)); View container = inflater.inflate(R.layout.fillup_field, null); FieldView field = (FieldView) container.findViewById(R.id.field); field.setFieldId(id); field.setId((int) id); field.setHint(hint); mFieldsContainer.addView(container); mFields.add(field); if (mIcicle != null || fieldMap.size() > 0) { String value = null; if (mIcicle != null) { value = mIcicle.getString(field.getKey()); } if (value != null && value.length() > 0) { field.setText(value); } else { if (mFillup.isExistingObject()) { // set the value from the database, if present FillupField objectField = fieldMap.get(id); if (objectField != null) { field.setText(objectField.getValue()); } } } } } if (fields.getCount() == 0) { mFieldsContainer.setVisibility(View.GONE); } } @Override public boolean onCreateOptionsMenu(Menu menu) { menu.add(Menu.NONE, R.string.edit_fields, Menu.NONE, R.string.edit_fields) .setIntent(new Intent(this, FieldListActivity.class)) .setIcon(R.drawable.ic_menu_edit); return super.onCreateOptionsMenu(menu); } @Override public void onSaveInstanceState(Bundle outState) { super.onSaveInstanceState(outState); for (FieldView fieldView : mFields) { outState.putString(fieldView.getKey(), fieldView.getText().toString()); } } @Override protected boolean postSaveValidation() { try { for (FieldView fieldView : mFields) { FillupField field = new FillupField(new ContentValues()); field.setFillupId(mFillup.getId()); field.setTemplateId(fieldView.getFieldId()); field.setValue(fieldView.getText().toString()); field.save(this); } return true; } catch (InvalidFieldException exception) { handleInvalidField(exception); } return false; } @Override protected void saved() { // invalidate the cache ContentValues values = new ContentValues(); values.put(CachedValue.VALID, "0"); getContentResolver().update(CacheTable.BASE_URI, values, CachedValue.ITEM + " = ?", new String[] { String.valueOf(mVehicles.getSelectedItemId()) }); Activity parent = getParent(); if (parent == null) { finish(); } else if (parent instanceof Mileage) { ((Mileage) parent).switchToHistoryTab(); } mFillup = new Fillup(new ContentValues()); onCreate(null); } @Override protected Dao getDao() { return mFillup; } @Override protected String[] getProjectionArray() { return FillupsTable.PROJECTION; } @Override protected Uri getUri(long id) { return ContentUris.withAppendedId(FillupsTable.BASE_URI, id); } @Override protected void initUI() { mOdometer = (EditText) findViewById(R.id.odometer); mVolume = (EditText) findViewById(R.id.volume); mPrice = (EditText) findViewById(R.id.price); mDate = (DateButton) findViewById(R.id.date); mPartial = (CheckBox) findViewById(R.id.partial); mFieldsContainer = (LinearLayout) findViewById(R.id.container); mVehicles = (CursorSpinner) findViewById(R.id.vehicle); mVehicles.setOnItemSelectedListener(new AdapterView.OnItemSelectedListener() { @Override public void onItemSelected(AdapterView<?> arg0, View arg1, int arg2, long arg3) { mVehicle = null; mVehicle = getVehicle(); } @Override public void onNothingSelected(AdapterView<?> arg0) { } }); setDataFormats(); } @Override protected void populateUI() { double odometer = mFillup.getOdometer(); odometer = Math.round(odometer * 100D) / 100D; mOdometer.setText(String.valueOf(odometer)); mDate.setDate(mFillup.getTimestamp()); mPartial.setChecked(mFillup.isPartial()); setDataFormats(); if (mFillup.isExistingObject()) { setTitle(getString(R.string.title_fillup, mDate.getText())); mVehicles.setSelectedId(mFillup.getVehicleId()); } } private void setDataFormats() { int dataFormat = Integer.parseInt(mPreferences.getString(Settings.DATA_FORMAT, "0")); boolean existing = mFillup.isExistingObject(); switch (dataFormat) { case DataFormats.UNIT_PRICE_VOLUME: mVolume.setHint(Calculator.getVolumeUnits(this, getVehicle())); mPrice.setHint(getString(R.string.price_per_unit, Calculator.getVolumeUnits(this, getVehicle()))); if (existing) { mVolume.setText(String.valueOf(mFillup.getVolume())); mPrice.setText(String.valueOf(mFillup.getUnitPrice())); } break; case DataFormats.TOTAL_COST_VOLUME: mVolume.setHint(Calculator.getVolumeUnits(this, getVehicle())); mPrice.setHint(R.string.total_cost); if (existing) { mVolume.setText(String.valueOf(mFillup.getVolume())); mPrice.setText(String.valueOf(mFillup.getTotalCost())); } break; case DataFormats.TOTAL_COST_UNIT_PRICE: mVolume.setHint(R.string.total_cost); mPrice.setHint(getString(R.string.price_per_unit, Calculator.getVolumeUnits(this, getVehicle()))); if (existing) { mVolume.setText(String.valueOf(mFillup.getTotalCost())); mPrice.setText(String.valueOf(mFillup.getUnitPrice())); } break; } } @Override protected void setFields() throws InvalidFieldException { double unitPrice = 0D; double totalCost = 0D; double volume = 0D; int dataFormat = Integer.parseInt(mPreferences.getString(Settings.DATA_FORMAT, "0")); switch (dataFormat) { case DataFormats.TOTAL_COST_VOLUME: try { volume = Double.parseDouble(mVolume.getText().toString()); mFillup.setVolume(volume); } catch (NumberFormatException e) { throw new InvalidFieldException(mVolume, R.string.error_no_volume_specified); } try { totalCost = Double.parseDouble(mPrice.getText().toString()); mFillup.setTotalCost(totalCost); } catch (NumberFormatException e) { throw new InvalidFieldException(mPrice, R.string.error_no_total_cost_specified); } unitPrice = totalCost / volume; mFillup.setUnitPrice(unitPrice); break; case DataFormats.TOTAL_COST_UNIT_PRICE: try { totalCost = Double.parseDouble(mVolume.getText().toString()); mFillup.setTotalCost(totalCost); } catch (NumberFormatException e) { throw new InvalidFieldException(mVolume, R.string.error_no_total_cost_specified); } try { unitPrice = Double.parseDouble(mPrice.getText().toString()); mFillup.setUnitPrice(unitPrice); } catch (NumberFormatException e) { throw new InvalidFieldException(mPrice, R.string.error_no_price_specified); } volume = totalCost / unitPrice; mFillup.setVolume(volume); break; default: case DataFormats.UNIT_PRICE_VOLUME: try { volume = Double.parseDouble(mVolume.getText().toString()); mFillup.setVolume(volume); } catch (NumberFormatException e) { throw new InvalidFieldException(mVolume, R.string.error_no_volume_specified); } try { unitPrice = Double.parseDouble(mPrice.getText().toString()); mFillup.setUnitPrice(unitPrice); } catch (NumberFormatException e) { throw new InvalidFieldException(mPrice, R.string.error_no_price_specified); } totalCost = unitPrice * volume; mFillup.setTotalCost(totalCost); break; } try { String odometerText = mOdometer.getText().toString(); double odometerValue = 0; if (odometerText.startsWith("+")) { Fillup previous = mFillup.loadPrevious(this); double previousOdometer = 0D; if (previous == null) { Cursor top = getContentResolver().query(FillupsTable.BASE_URI, FillupsTable.PROJECTION, null, null, Fillup.ODOMETER + " DESC"); if (top.getCount() > 0) { previous = new Fillup(top); if (previous != null) { previousOdometer = previous.getOdometer(); } } } odometerValue = previousOdometer + Double.parseDouble(odometerText.substring(1)); } else { odometerValue = Double.parseDouble(odometerText); } mFillup.setOdometer(odometerValue); } catch (NumberFormatException e) { throw new InvalidFieldException(mOdometer, R.string.error_no_odometer_specified); } mFillup.setPartial(mPartial.isChecked()); mFillup.setVehicleId(mVehicles.getSelectedItemId()); mFillup.setTimestamp(mDate.getTimestamp()); if (mFillup.isPartial()) { mFillup.setEconomy(0); } else { // update the economy number Uri vehicleUri = ContentUris.withAppendedId(VehiclesTable.BASE_URI, mVehicles.getSelectedItemId()); Vehicle v = null; Cursor vehicleCursor = managedQuery(vehicleUri, VehiclesTable.PROJECTION, null, null, null); if (vehicleCursor.getCount() == 1) { vehicleCursor.moveToFirst(); v = new Vehicle(vehicleCursor); Fillup previous = null; if (mFillup.isExistingObject()) { previous = mFillup.loadPrevious(this); } else { previous = v.loadLatestFillup(this); } if (previous == null) { mFillup.setEconomy(0D); } else { double economy = Calculator.averageEconomy(v, new FillupSeries(previous, mFillup)); mFillup.setEconomy(economy); } } } if (mPreferences.getBoolean(Settings.STORE_LOCATION, false) && mFillup.isExistingObject() == false) { // Don't want to erase location data LocationManager locationManager = (LocationManager) getSystemService(LOCATION_SERVICE); Location lastLocation = locationManager.getLastKnownLocation(LocationManager.NETWORK_PROVIDER); // Only record if the user has a network location. if (lastLocation != null) { mFillup.setLatitude(lastLocation.getLatitude()); mFillup.setLongitude(lastLocation.getLongitude()); } } if (mFillup.isPartial() || (mFillup.isExistingObject() && !mFillup.isPartial())) { ContentValues values = new ContentValues(); values.put(Fillup.ECONOMY, -1); getContentResolver().update( FillupsTable.BASE_URI, values, Fillup.ODOMETER + " > ? AND " + Fillup.VEHICLE_ID + " = ?", new String[] { String.valueOf(mFillup.getOdometer()), String.valueOf(mFillup.getVehicleId()) }); RecalculateEconomyService.run(this, mVehicle); } } @Override protected void deleted() { getContentResolver().delete(CacheTable.BASE_URI, CachedValue.KEY + " = ?", new String[] { Statistics.AVG_ECONOMY.getKey() }); super.deleted(); } @Override protected int getCreateString() { return R.string.add_fillup; } }