public class CarSelectionPreference extends DialogPreference { public static final String SENSOR_TYPE = "car"; private static final Logger logger = Logger.getLogger(CarSelectionPreference.class); private static final String DEFAULT_VALUE = "null"; private Car car; private LinearLayout garageProgress; private EditText modelEditText; private EditText manufacturerEditText; private EditText constructionYearEditText; protected String carModel; protected String carManufacturer; protected String carConstructionYear; protected String carFuelType; protected String carEngineDisplacement; private Spinner sensorSpinner; private ProgressBar sensorDlProgress; private Button sensorRetryButton; protected List<Car> sensors; private ScrollView garageForm; private RadioButton gasolineRadioButton; private RadioButton dieselRadioButton; private EditText engineDisplacementEditText; public CarSelectionPreference(Context context, AttributeSet attrs) { super(context, attrs); setDialogLayoutResource(R.layout.my_garage_layout); setPositiveButtonText(android.R.string.ok); setNegativeButtonText(android.R.string.cancel); setDialogIcon(null); } @Override protected void onBindDialogView(View view) { setupUIItems(view); } private void setupUIItems(View view) { // TODO !fancy! search for sensors garageForm = (ScrollView) view.findViewById(R.id.garage_form); garageProgress = (LinearLayout) view.findViewById(R.id.addCarToGarage_status); setupCarCreationItems(view); sensorSpinner = (Spinner) view.findViewById(R.id.dashboard_current_sensor_spinner); sensorSpinner.setOnItemSelectedListener( new OnItemSelectedListener() { private boolean firstSelect = true; @Override public void onItemSelected(AdapterView<?> parent, View view, int pos, long id) { if (!firstSelect) { logger.info(parent.getItemAtPosition(pos) + ""); updateCurrentSensor((Car) parent.getItemAtPosition(pos)); } else { firstSelect = false; } } @Override public void onNothingSelected(AdapterView<?> parent) { logger.info("no change detected"); } }); sensorDlProgress = (ProgressBar) view.findViewById(R.id.sensor_dl_progress); sensorRetryButton = (Button) view.findViewById(R.id.retrybutton); sensorRetryButton.setOnClickListener( new OnClickListener() { @Override public void onClick(View view) { getCarList(); } }); getCarList(); view.findViewById(R.id.mygaragelayout).requestFocus(); view.findViewById(R.id.mygaragelayout).requestFocusFromTouch(); } private void setupCarCreationItems(View view) { modelEditText = (EditText) view.findViewById(R.id.addCarToGarage_car_model); manufacturerEditText = (EditText) view.findViewById(R.id.addCarToGarage_car_manufacturer); constructionYearEditText = (EditText) view.findViewById(R.id.addCarToGarage_car_constructionYear); engineDisplacementEditText = (EditText) view.findViewById(R.id.addCarToGarage_car_engineDisplacement); TextWatcher textWatcher = new TextWatcher() { @Override public void onTextChanged(CharSequence s, int start, int before, int count) { carModel = modelEditText.getText().toString(); carManufacturer = manufacturerEditText.getText().toString(); carConstructionYear = constructionYearEditText.getText().toString(); carEngineDisplacement = engineDisplacementEditText.getText().toString(); } @Override public void afterTextChanged(Editable s) {} @Override public void beforeTextChanged(CharSequence s, int start, int count, int after) {} }; modelEditText.addTextChangedListener(textWatcher); manufacturerEditText.addTextChangedListener(textWatcher); constructionYearEditText.addTextChangedListener(textWatcher); engineDisplacementEditText.addTextChangedListener(textWatcher); OnClickListener listener = new OnClickListener() { @Override public void onClick(View v) { carFuelType = resolveFuelTypeFromCheckbox(v.getId()); logger.info(carFuelType); } }; RadioGroup radioGroup = (RadioGroup) view.findViewById(R.id.radiogroup_fueltype); carFuelType = resolveFuelTypeFromCheckbox(radioGroup.getCheckedRadioButtonId()); gasolineRadioButton = (RadioButton) view.findViewById(R.id.radio_gasoline); gasolineRadioButton.setOnClickListener(listener); dieselRadioButton = (RadioButton) view.findViewById(R.id.radio_diesel); dieselRadioButton.setOnClickListener(listener); view.findViewById(R.id.register_car_button) .setOnClickListener( new View.OnClickListener() { @Override public void onClick(View view) { // if(UserManager.instance().isLoggedIn()){ registerSensorAtServer( SENSOR_TYPE, carManufacturer, carModel, carConstructionYear, carFuelType, carEngineDisplacement); // } // else { // Toast.makeText(getDialog().getContext(), // "Please log in", Toast.LENGTH_SHORT).show(); // } } }); } /** Shows the progress UI and hides the register form. */ @TargetApi(Build.VERSION_CODES.HONEYCOMB_MR2) private void showProgress(final boolean show) { // On Honeycomb MR2 we have the ViewPropertyAnimator APIs, which allow // for very easy animations. If available, use these APIs to fade-in // the progress spinner. if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB_MR2) { int shortAnimTime = getContext().getResources().getInteger(android.R.integer.config_shortAnimTime); garageProgress.setVisibility(View.VISIBLE); garageProgress .animate() .setDuration(shortAnimTime) .alpha(show ? 1 : 0) .setListener( new AnimatorListenerAdapter() { @Override public void onAnimationEnd(Animator animation) { garageProgress.setVisibility(show ? View.VISIBLE : View.GONE); } }); garageForm.setVisibility(View.VISIBLE); garageForm .animate() .setDuration(shortAnimTime) .alpha(show ? 0 : 1) .setListener( new AnimatorListenerAdapter() { @Override public void onAnimationEnd(Animator animation) { garageForm.setVisibility(show ? View.GONE : View.VISIBLE); } }); } else { // The ViewPropertyAnimator APIs are not available, so simply show // and hide the relevant UI components. garageProgress.setVisibility(show ? View.VISIBLE : View.GONE); garageForm.setVisibility(show ? View.GONE : View.VISIBLE); } } /** * Register a new sensor (car) at the server * * @param sensorType * @param carManufacturer Car manufacturer * @param carModel Car model * @param carConstructionYear Construction year of the car * @param carFuelType Fuel type of the car */ private void registerSensorAtServer( final String sensorType, final String carManufacturer, final String carModel, final String carConstructionYear, final String carFuelType, final String carEngineDisplacement) { try { checkEmpty( sensorType, carManufacturer, carModel, carConstructionYear, carConstructionYear, carFuelType, carEngineDisplacement); } catch (Exception e) { // TODO i18n Toast.makeText(getContext(), "Not all values were defined.", Toast.LENGTH_SHORT).show(); return; } String sensorString = String.format( Locale.ENGLISH, "{ \"type\": \"%s\", \"properties\": {\"manufacturer\": \"%s\", \"model\": \"%s\", \"fuelType\": \"%s\", \"constructionYear\": %s, \"engineDisplacement\": %s } }", sensorType, carManufacturer, carModel, carFuelType, carConstructionYear, carEngineDisplacement); User user = UserManager.instance().getUser(); String username = user.getUsername(); String token = user.getToken(); if (((SettingsActivity) getContext()).isConnectedToInternet()) { RestClient.createSensor( sensorString, username, token, new AsyncHttpResponseHandler() { @Override public void onStart() { super.onStart(); showProgress(true); } @Override public void onFailure(Throwable error, String content) { super.onFailure(error, content); if (content != null && content.equals("can't resolve host")) { Toast.makeText( getContext(), getContext().getString(R.string.error_host_not_found), Toast.LENGTH_SHORT) .show(); } else if (content != null && content.contains("Unauthorized")) { logger.info( "Tried to register new car while not logged in. Creating temporary car."); Crouton.makeText( (Activity) getContext(), getContext().getResources().getString(R.string.creating_temp_car), Style.INFO) .show(); createTemporaryCar(); } else { logger.warn( "Received error response: " + content + "; " + error.getMessage(), error); // TODO i18n Toast.makeText(getContext(), "Server Error: " + content, Toast.LENGTH_SHORT).show(); } showProgress(false); } @Override public void onSuccess(int httpStatusCode, Header[] h, String response) { super.onSuccess(httpStatusCode, h, response); String location = ""; for (int i = 0; i < h.length; i++) { if (h[i].getName().equals("Location")) { location += h[i].getValue(); break; } } logger.info(httpStatusCode + " " + location); String sensorId = location.substring(location.lastIndexOf("/") + 1, location.length()); // put the sensor id into shared preferences int engineDisplacement = Integer.parseInt(carEngineDisplacement); int year = Integer.parseInt(carConstructionYear); car = new Car( Car.resolveFuelType(carFuelType), carManufacturer, carModel, sensorId, year, engineDisplacement); persistCar(); } }); } else { createTemporaryCar(); } } private void createTemporaryCar() { String sensorId = Car.TEMPORARY_SENSOR_ID + UUID.randomUUID().toString().substring(0, 5); createNewCar(sensorId); } private void createNewCar(String sensorId) { int year = Integer.parseInt(carConstructionYear); car = new Car( Car.resolveFuelType(carFuelType), carManufacturer, carModel, sensorId, year, Integer.parseInt(carEngineDisplacement)); persistCar(); Toast.makeText( getContext(), getContext().getString(R.string.creating_temp_car), Toast.LENGTH_SHORT) .show(); } private void checkEmpty(String... values) throws Exception { for (String string : values) { if (string == null || string.isEmpty()) { throw new Exception("Empty value!"); } } } /** * Get the fuel type form the checkbox * * @param resid * @return */ private String resolveFuelTypeFromCheckbox(int resid) { switch (resid) { case R.id.radio_diesel: return FuelType.DIESEL.toString(); case R.id.radio_gasoline: return FuelType.GASOLINE.toString(); } return "none"; } protected String downloadSensors() throws Exception { HttpGet getRequest = new HttpGet(ECApplication.BASE_URL + "/sensors"); getRequest.addHeader("Accept-Encoding", "application/json"); try { HttpResponse response = HTTPClient.execute(getRequest); String content = HTTPClient.readResponse(response.getEntity()); JSONObject parentObject = new JSONObject(content); JSONArray res = (JSONArray) parentObject.get("sensors"); addSensorsToList(res); } catch (Exception e) { logger.warn(e.getMessage(), e); throw e; } return ""; } private void addSensorsToList(JSONArray res) { for (int i = 0; i < res.length(); i++) { String typeString; JSONObject properties; String carId; try { typeString = ((JSONObject) res.get(i)).optString("type", "none"); properties = ((JSONObject) res.get(i)).getJSONObject("properties"); carId = properties.getString("id"); } catch (JSONException e) { logger.warn(e.getMessage(), e); continue; } if (typeString.equals(SENSOR_TYPE)) { try { sensors.add(Car.fromJsonWithStrictEngineDisplacement(properties)); } catch (JSONException e) { logger.warn( String.format( "Car '%s' not supported: %s", carId != null ? carId : "null", e.getMessage())); } } } SensorAdapter adapter = new SensorAdapter(); sensorSpinner.setAdapter(adapter); int index = adapter.getInitialSelectedItem(); sensorSpinner.setSelection(index); } public void getCarList() { sensorDlProgress.setVisibility(View.VISIBLE); sensorSpinner.setVisibility(View.GONE); sensorRetryButton.setVisibility(View.GONE); sensors = new ArrayList<Car>(); if (((SettingsActivity) getContext()).isConnectedToInternet()) { try { new SensorDownloadTask().execute().get(); } catch (Exception e) { logger.warn(e.getMessage(), e); Toast.makeText(getContext(), "Could not retrieve cars from server", Toast.LENGTH_SHORT) .show(); } // TODO add possibility to update cache // downloadSensors(true); } else { getCarsFromCache(); } if (sensors.isEmpty()) { logger.warn("Got no cars neither from server nor from cache."); // TODO show warning that no cars were found i18n Toast.makeText( getContext(), "Could not retrieve cars from server or local cache", Toast.LENGTH_SHORT) .show(); } sensorDlProgress.setVisibility(View.GONE); sensorSpinner.setVisibility(View.VISIBLE); } private void getCarsFromCache() { File directory; try { directory = Util.resolveExternalStorageBaseFolder(); File f = new File(directory, CarManager.CAR_CACHE_FILE_NAME); if (f.isFile()) { BufferedReader bufferedReader = new BufferedReader(new FileReader(f)); String content = ""; String line = ""; while ((line = bufferedReader.readLine()) != null) { content = content.concat(line); } bufferedReader.close(); JSONArray cars = new JSONArray(content); addSensorsToList(cars); } } catch (IOException e) { logger.warn(e.getMessage(), e); } catch (JSONException e) { logger.warn(e.getMessage(), e); } } /** * This method updates the attributes of the current sensor (=car) * * @param sensorid the id that is stored on the server * @param carManufacturer the car manufacturer * @param carModel the car model * @param fuelType the fuel type of the car * @param year construction year of the car */ private void updateCurrentSensor(Car car) { this.car = car; } @Override protected void onDialogClosed(boolean positiveResult) { if (positiveResult) { // this fixes issue #166 persistCar(); } } private void persistCar() { persistString(serializeCar(car)); setSummary(car.toString()); CarManager.instance().setCar(car); } @Override protected void onSetInitialValue(boolean restorePersistedValue, Object defaultValue) { if (restorePersistedValue) { car = instantiateCar(this.getPersistedString(DEFAULT_VALUE)); } if (car != null) { setSummary(car.toString()); } else { setSummary(R.string.please_select); } } @Override protected Parcelable onSaveInstanceState() { final Parcelable superState = super.onSaveInstanceState(); // Check whether this Preference is persistent (continually saved) if (isPersistent()) { // No need to save instance state since it's persistent, use superclass state return superState; } // Create instance of custom BaseSavedState final SavedState myState = new SavedState(superState); // Set the state's value with the class member that holds current setting value myState.car = car; return myState; } @Override protected void onRestoreInstanceState(Parcelable state) { // Check whether we saved the state in onSaveInstanceState if (state == null || !state.getClass().equals(SavedState.class)) { // Didn't save the state, so call superclass super.onRestoreInstanceState(state); return; } // Cast state to custom BaseSavedState and pass to superclass SavedState myState = (SavedState) state; super.onRestoreInstanceState(myState.getSuperState()); } public static String serializeCar(Car car) { ObjectOutputStream oos = null; Base64OutputStream b64 = null; try { ByteArrayOutputStream byteArrayOut = new ByteArrayOutputStream(); oos = new ObjectOutputStream(byteArrayOut); oos.writeObject(car); oos.flush(); ByteArrayOutputStream out = new ByteArrayOutputStream(); b64 = new Base64OutputStream(out, Base64.DEFAULT); b64.write(byteArrayOut.toByteArray()); b64.flush(); b64.close(); out.flush(); out.close(); String result = new String(out.toByteArray()); return result; } catch (IOException e) { logger.warn(e.getMessage(), e); } finally { if (oos != null) try { b64.close(); oos.close(); } catch (IOException e) { logger.warn(e.getMessage(), e); } } return null; } public static Car instantiateCar(String object) { if (object == null) return null; ObjectInputStream ois = null; try { Base64InputStream b64 = new Base64InputStream(new ByteArrayInputStream(object.getBytes()), Base64.DEFAULT); ois = new ObjectInputStream(b64); Car car = (Car) ois.readObject(); return car; } catch (StreamCorruptedException e) { logger.warn(e.getMessage(), e); } catch (IOException e) { logger.warn(e.getMessage(), e); } catch (ClassNotFoundException e) { logger.warn(e.getMessage(), e); } finally { if (ois != null) try { ois.close(); } catch (IOException e) { logger.warn(e.getMessage(), e); } } return null; } private class SensorDownloadTask extends AsyncTask<Void, String, String> { @Override protected String doInBackground(Void... params) { try { return downloadSensors(); } catch (Exception e) { logger.warn(e.getMessage(), e); } return ""; } } public static class SavedState extends BaseSavedState { // Member that holds the setting's value // Change this data type to match the type saved by your Preference Car car; public SavedState(Parcelable superState) { super(superState); } public SavedState(Parcel source) { super(source); // Get the current preference's value car = (Car) source.readSerializable(); // Change this to read the appropriate data type } @Override public void writeToParcel(Parcel dest, int flags) { super.writeToParcel(dest, flags); // Write the preference's value dest.writeSerializable(car); // Change this to write the appropriate data type } // Standard creator object using an instance of this class public static final Parcelable.Creator<SavedState> CREATOR = new Parcelable.Creator<SavedState>() { public SavedState createFromParcel(Parcel in) { return new SavedState(in); } public SavedState[] newArray(int size) { return new SavedState[size]; } }; } private class SensorAdapter extends BaseAdapter implements SpinnerAdapter { @Override public int getCount() { return sensors.size() + 1; } public int getInitialSelectedItem() { if (car != null) { int index = 1; for (Car c : sensors) { if (c.equals(car)) { return index; } index++; } } return 0; } @Override public Object getItem(int position) { return sensors.get(position - 1); } @Override public long getItemId(int position) { return position; } @Override public View getView(int position, View view, ViewGroup parent) { TextView text; if (position == 0) { text = new TextView(parent.getContext()); text.setText(getContext().getString(R.string.please_select)); } else { text = new TextView(parent.getContext()); text.setText(((Car) getItem(position)).toString()); } return text; } } }
/** * This is a track. A track is a collection of measurements. All measurements of a track are * accessible via the track. The track stores meta information about the ride (car, fuel, * description...) */ public class Track implements Comparable<Track> { public enum TrackStatus { ONGOING { @Override public String toString() { return "ONGOING"; } }, FINISHED { @Override public String toString() { return "FINISHED"; } } } private static final Logger logger = Logger.getLogger(Track.class); private long id; private String name; private String description; private List<Measurement> measurements = new ArrayList<Measurement>(); private Car car; private AbstractConsumptionAlgorithm consumptionAlgorithm; private String remoteID; private Double consumptionPerHour; private TrackStatus status = TrackStatus.ONGOING; private DbAdapter dbAdapter; private boolean lazyLoadingMeasurements; private Long startTime = null; private Long endTime = null; private TrackMetadata metadata; public static Track createTrackWithId(long id, DbAdapter dbAdapterImpl) { Track track = new Track(id); track.dbAdapter = dbAdapterImpl; return track; } public static Track createNewLocalTrack(DbAdapter dbAdapterImpl) { Track t = new Track(dbAdapterImpl); return t; } public static Track createRemoteTrack(String remoteID, DbAdapter dbAdapter) { Track track = new Track(remoteID, dbAdapter); return track; } private Track(long id) { this.id = id; } private Track(String remoteID, DbAdapter dbAdapter) { this(dbAdapter); this.remoteID = remoteID; this.status = TrackStatus.FINISHED; } /** * Constructor for creating "fresh" new track. Use this for new measurements that were captured * from the OBD-II adapter. */ private Track(DbAdapter dbAdapter) { this.name = ""; this.description = ""; this.measurements = new ArrayList<Measurement>(); this.id = dbAdapter.insertTrack(this); this.dbAdapter = dbAdapter; } /** @return the localTrack */ public boolean isLocalTrack() { return !isRemoteTrack(); } public boolean isRemoteTrack() { return (remoteID != null ? true : false); } /** @return the name */ public String getName() { return name; } /** @param name the name to set */ public void setName(String name) { this.name = name; } /** @return the description */ public String getDescription() { return description; } /** @param description the description to set */ public void setDescription(String description) { this.description = description; } /** @return the measurements */ public List<Measurement> getMeasurements() { if ((measurements == null || measurements.isEmpty()) && dbAdapter != null) { try { this.measurements = dbAdapter.getAllMeasurementsForTrack(this); } catch (TrackWithoutMeasurementsException e) { logger.warn(e.getMessage(), e); } } return measurements; } public Car getCar() { return car; } public void setCar(Car car) { this.car = car; this.consumptionAlgorithm = new BasicConsumptionAlgorithm(car); } /** * get the time where the track started * * @return start time of track as unix long * @throws MeasurementsException */ public Long getStartTime() throws MeasurementsException { if (startTime != null) return startTime; if (this.getMeasurements().size() > 0) return this.getMeasurements().get(0).getTime(); else throw new MeasurementsException("No measurements in the track"); } public void setStartTime(Long time) { this.startTime = time; } /** * get the time where the track ended * * @return end time of track as unix long * @throws MeasurementsException */ public Long getEndTime() throws MeasurementsException { if (endTime != null) return endTime; if (this.getMeasurements().size() > 0) return this.getMeasurements().get(this.getMeasurements().size() - 1).getTime(); else throw new MeasurementsException("No measurements in the track"); } public void setEndTime(Long time) { this.endTime = time; } /** * Sets the measurements with an arraylist of measurements * * @param measurements the measurements of a track */ public void setMeasurementsAsArrayList(List<Measurement> measurements) { setMeasurementsAsArrayList(measurements, false); } public void setMeasurementsAsArrayList(List<Measurement> measurements, boolean storeInDb) { this.measurements = measurements; if (storeInDb) { storeMeasurementsInDbAdapter(); } } private void storeMeasurementsInDbAdapter() { if (this.dbAdapter != null) { for (Measurement measurement : measurements) { try { this.dbAdapter.insertMeasurement(measurement); } catch (MeasurementsException e) { logger.warn(e.getMessage(), e); } } } } /** * Use this method only to insert "fresh" measurements, not to recreate a Track from the database * Use {@code insertMeasurement(ArrayList<Measurement> measurements)} instead Inserts measurments * into the Track and into the database! * * @param measurement * @throws TrackAlreadyFinishedException * @throws MeasurementsException */ public void addMeasurement(Measurement measurement) throws TrackAlreadyFinishedException { measurement.setTrack(Track.this); this.measurements.add(measurement); if (this.dbAdapter != null) { try { this.dbAdapter.insertNewMeasurement(measurement); } catch (MeasurementsException e) { logger.severe("This should never happen", e); return; } } else { logger.warn("DbAdapter was null! Could not insert measurement"); } } /** * Returns the number of measurements of this track * * @return */ public int getNumberOfMeasurements() { return this.measurements.size(); } /** @return the id */ public long getId() { return id; } public String getRemoteID() { return remoteID; } public void setRemoteID(String remoteID) { this.remoteID = remoteID; } /** * Returns the length of a track in kilometers * * @return */ public double getLengthOfTrack() { List<Measurement> measurements = this.getMeasurements(); double distance = 0.0; if (measurements.size() > 1) { for (int i = 0; i < measurements.size() - 1; i++) { distance = distance + Util.getDistance( measurements.get(i).getLatitude(), measurements.get(i).getLongitude(), measurements.get(i + 1).getLatitude(), measurements.get(i + 1).getLongitude()); } } return distance; } /** * Returns the last measurement of this track * * @return the last measurement or null if there are no measurements */ public Measurement getLastMeasurement() { if (this.measurements.size() > 0) { return this.measurements.get(this.measurements.size() - 1); } return null; } /** * Returns the first measurement of this track * * @return Returns the last measurement or null if there are no measurements */ public Measurement getFirstMeasurement() { if (this.measurements.size() > 0) { return this.measurements.get(0); } return null; } /** * Returns the average co2 emission for the track. * * @return */ public double getCO2Average() { double co2Average = 0.0; try { for (Measurement measurement : measurements) { if (measurement.getProperty(CONSUMPTION) != null) { co2Average = co2Average + consumptionAlgorithm.calculateCO2FromConsumption( measurement.getProperty(CONSUMPTION)); } } co2Average = co2Average / measurements.size(); } catch (FuelConsumptionException e) { logger.warn(e.getMessage(), e); } return co2Average; } public double getFuelConsumptionPerHour() throws UnsupportedFuelTypeException { if (consumptionPerHour == null) { consumptionPerHour = 0.0; int consideredCount = 0; for (int i = 0; i < measurements.size(); i++) { try { consumptionPerHour = consumptionPerHour + consumptionAlgorithm.calculateConsumption(measurements.get(i)); consideredCount++; } catch (FuelConsumptionException e) { logger.warn(e.getMessage()); } } consumptionPerHour = consumptionPerHour / consideredCount; } return consumptionPerHour; } @Override public int compareTo(Track t) { try { if (t.getStartTime() == null && t.getEndTime() == null) { /* * we cannot assume any ordering */ return 0; } } catch (MeasurementsException e) { return 0; } try { if (this.getStartTime() == null) { /* * no measurements, this is probably a relatively new track */ return -1; } } catch (MeasurementsException e) { return -1; } try { if (t.getStartTime() == null) { /* * no measurements, that is probably a relatively new track */ return 1; } } catch (MeasurementsException e) { return 1; } try { return (this.getStartTime() < t.getStartTime() ? 1 : -1); } catch (MeasurementsException e) { return 0; } } public double getLiterPerHundredKm() throws MeasurementsException { return consumptionPerHour * getDurationInMillis() / (1000 * 60 * 60) / getLengthOfTrack() * 100; } public long getDurationInMillis() throws MeasurementsException { return getEndTime() - getStartTime(); } public double getGramsPerKm() throws FuelConsumptionException, MeasurementsException { if (this.car.getFuelType().equals(FuelType.GASOLINE)) { return getLiterPerHundredKm() * 23.3; } else if (this.car.getFuelType().equals(FuelType.DIESEL)) { return getLiterPerHundredKm() * 26.4; } else throw new FuelConsumptionException(); } public void setStatus(TrackStatus s) { this.status = s; } public TrackStatus getStatus() { return status; } public void setLazyLoadingMeasurements(boolean b) { this.lazyLoadingMeasurements = b; } public boolean isLazyLoadingMeasurements() { return lazyLoadingMeasurements; } /** * Creates a Track and adds its contents to the DB layer (adapter). * * @param json the input json object * @param adapter the DB layer adapter * @return the Track object * @throws JSONException parsing fails or contains unexpected properties * @throws ParseException if DateTime parsing fails */ public static Track fromJson(JSONObject json, DbAdapter adapter) throws JSONException, ParseException { JSONObject trackProperties = json.getJSONObject("properties"); Track t = Track.createRemoteTrack(trackProperties.getString("id"), adapter); String trackName = "unnamed Track #" + t.getId(); try { trackName = trackProperties.getString("name"); } catch (JSONException e) { logger.warn(e.getMessage(), e); } t.setName(trackName); String description = ""; try { description = trackProperties.getString("description"); } catch (JSONException e) { logger.warn(e.getMessage(), e); } t.setDescription(description); JSONObject sensorProperties = trackProperties.getJSONObject("sensor").getJSONObject("properties"); t.setCar(Car.fromJson(sensorProperties)); // include server properties tracks created, modified? t.dbAdapter.updateTrack(t); // Log.i("track_id",t.getId()+" "+((DbAdapterRemote) // dbAdapter).trackExistsInDatabase(t.getId())+" "+dbAdapter.getNumberOfStoredTracks()); Measurement recycleMeasurement; List<Measurement> measurements = new ArrayList<Measurement>(); JSONArray features = json.getJSONArray("features"); logger.info( "Parsing measurements of track " + t.getRemoteID() + ". Count: " + features.length()); for (int j = 0; j < features.length(); j++) { JSONObject measurementJsonObject = features.getJSONObject(j); recycleMeasurement = Measurement.fromJson(measurementJsonObject); recycleMeasurement.setTrack(t); measurements.add(recycleMeasurement); } t.setMeasurementsAsArrayList(measurements); logger.info("Storing measurements in database"); t.storeMeasurementsInDbAdapter(); return t; } public boolean isFinished() { return status != null && status == TrackStatus.FINISHED; } /** * updates the tracks metadata. if there is already metadata, the properties are merged. the * provided object overrides existing keys. * * @param newMetadata */ public void updateMetadata(TrackMetadata newMetadata) { if (this.metadata != null) { this.metadata.merge(newMetadata); } else { setMetadata(newMetadata); } dbAdapter.updateTrack(this); } public TrackMetadata getMetadata() { return this.metadata; } public void setMetadata(TrackMetadata m) { this.metadata = m; } @Override public String toString() { return "Track / id: " + getId() + " / Name: " + getName(); } }