/* * Copyright 2012 Google Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not * use this file except in compliance with the License. You may obtain a copy of * the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the * License for the specific language governing permissions and limitations under * the License. */ package de.dennisguse.opentracks.util; import android.app.Activity; import android.content.Context; import android.location.Location; import android.view.View; import android.widget.Spinner; import android.widget.TextView; import de.dennisguse.opentracks.content.sensor.SensorDataSet; import de.dennisguse.opentracks.services.sensors.SensorManager; import de.dennisguse.opentracks.stats.TripStatistics; import de.dennisguse.opentracks.R; import java.util.Locale; /** * Utilities for updating the statistics UI labels and values. * * @author Jimmy Shih */ public class StatsUtils { private static final String GRADE_PERCENTAGE = "%"; private static final String GRADE_FORMAT = "%1$d"; private StatsUtils() { } /** * Sets the location values. * * @param context the context * @param activity the activity for finding views. If null, the view cannot be * null * @param view the containing view for finding views. If null, the activity * cannot be null * @param location the location * @param isRecording true if recording */ public static void setLocationValues(Context context, Activity activity, View view, Location location, boolean isRecording) { boolean metricUnits = PreferencesUtils.isMetricUnits(context); boolean reportSpeed = PreferencesUtils.isReportSpeed(context); // Set speed/pace View speed = getView(activity, view, R.id.stats_speed); speed.setVisibility(isRecording ? View.VISIBLE : View.INVISIBLE); if (isRecording) { double value = isRecording && location != null && location.hasSpeed() ? location.getSpeed() : Double.NaN; setSpeed(context, speed, R.string.stats_speed, R.string.stats_pace, value, metricUnits, reportSpeed); } // Set elevation boolean showGradeElevation = PreferencesUtils.getBoolean(context, R.string.stats_show_grade_elevation_key, PreferencesUtils.STATS_SHOW_GRADE_ELEVATION_DEFAULT) && isRecording; View elevation = getView(activity, view, R.id.stats_elevation); elevation.setVisibility(showGradeElevation ? View.VISIBLE : View.GONE); if (showGradeElevation) { double altitude = location != null && location.hasAltitude() ? location.getAltitude() : Double.NaN; setElevationValue(context, elevation, -1, altitude, metricUnits); } // Set coordinate boolean showCoordinate = isRecording && PreferencesUtils.getBoolean(context, R.string.stats_show_coordinate_key, PreferencesUtils.STATS_SHOW_COORDINATE_DEFAULT); View coordinateSeparator = getView(activity, view, R.id.stats_coordinate_separator); View coordinateContainer = getView(activity, view, R.id.stats_coordinate_container); if (coordinateSeparator != null) { coordinateSeparator.setVisibility(showCoordinate ? View.VISIBLE : View.GONE); } coordinateContainer.setVisibility(showCoordinate ? View.VISIBLE : View.GONE); if (showCoordinate) { double latitude = location != null ? location.getLatitude() : Double.NaN; double longitude = location != null ? location.getLongitude() : Double.NaN; setCoordinateValue(context, getView(activity, view, R.id.stats_latitude), R.string.stats_latitude, latitude); setCoordinateValue(context, getView(activity, view, R.id.stats_longitude), R.string.stats_longitude, longitude); } } public static void setSensorData(Context context, Activity activity, SensorDataSet sensorDataSet, boolean isRecording) { { // SensorDataSet: heart rate int isVisible = View.VISIBLE; if (!isRecording || PreferencesUtils.BLUETOOTH_SENSOR_DEFAULT.equals(PreferencesUtils.getString(context, R.string.bluetooth_sensor_key, PreferencesUtils.BLUETOOTH_SENSOR_DEFAULT))) { isVisible = View.INVISIBLE; } getView(activity, null, R.id.stats_sensor_container).setVisibility(isVisible); View viewValue = getView(activity, null, R.id.stats_sensor_heart_rate_value); View viewSensor = getView(activity, null, R.id.stats_sensor_heart_rate_sensor); if (isRecording) { setHeartRateData(context, viewValue, viewSensor, sensorDataSet); } } } /** * Sets the total time value. * * @param activity the activity * @param totalTime the total time */ public static void setTotalTimeValue(Activity activity, long totalTime) { setTimeValue(activity, activity.findViewById(R.id.stats_total_time), R.string.stats_total_time, totalTime); } /** * Sets the trip statistics values. * * @param context the context * @param activity the activity for finding views. If null, then view cannot * be null * @param view the containing view for finding views. If null, the activity * cannot be null * @param tripStatistics the trip statistics * @param trackIconValue the track icon value or null to hide the track icon * spinner */ public static void setTripStatisticsValues(Context context, Activity activity, View view, TripStatistics tripStatistics, String trackIconValue) { boolean metricUnits = PreferencesUtils.isMetricUnits(context); boolean reportSpeed = PreferencesUtils.isReportSpeed(context); // Set total distance double totalDistance = tripStatistics == null ? Double.NaN : tripStatistics.getTotalDistance(); setDistanceValue(context, getView(activity, view, R.id.stats_distance), totalDistance, metricUnits); // Set activity type Spinner spinner = (Spinner) getView(activity, view, R.id.stats_activity_type_icon); spinner.setVisibility(trackIconValue != null ? View.VISIBLE : View.GONE); if (trackIconValue != null) { TrackIconUtils.setIconSpinner(spinner, trackIconValue); } // Set total time setTimeValue(context, getView(activity, view, R.id.stats_total_time), R.string.stats_total_time, tripStatistics != null ? tripStatistics.getTotalTime() : -1L); // Set moving time setTimeValue(context, getView(activity, view, R.id.stats_moving_time), R.string.stats_moving_time, tripStatistics != null ? tripStatistics.getMovingTime() : -1L); // Set average speed/pace double averageSpeed = tripStatistics != null ? tripStatistics.getAverageSpeed() : Double.NaN; setSpeed(context, getView(activity, view, R.id.stats_average_speed), R.string.stats_average_speed, R.string.stats_average_pace, averageSpeed, metricUnits, reportSpeed); // Set max speed/pace double maxSpeed = tripStatistics == null ? Double.NaN : tripStatistics.getMaxSpeed(); setSpeed(context, getView(activity, view, R.id.stats_max_speed), R.string.stats_max_speed, R.string.stats_fastest_pace, maxSpeed, metricUnits, reportSpeed); // Set average moving speed/pace double averageMovingSpeed = tripStatistics != null ? tripStatistics.getAverageMovingSpeed() : Double.NaN; setSpeed(context, getView(activity, view, R.id.stats_average_moving_speed), R.string.stats_average_moving_speed, R.string.stats_average_moving_pace, averageMovingSpeed, metricUnits, reportSpeed); // Set grade/elevation boolean showGradeElevation = PreferencesUtils.getBoolean(context, R.string.stats_show_grade_elevation_key, PreferencesUtils.STATS_SHOW_GRADE_ELEVATION_DEFAULT); View gradeElevationSeparator = getView(activity, view, R.id.stats_grade_elevation_separator); gradeElevationSeparator.setVisibility(showGradeElevation ? View.VISIBLE : View.GONE); View gradeElevationContainer = getView(activity, view, R.id.stats_grade_elevation_container); gradeElevationContainer.setVisibility(showGradeElevation ? View.VISIBLE : View.GONE); if (showGradeElevation) { // Set grade double minGrade = tripStatistics == null ? Double.NaN : tripStatistics.getMinGrade(); double maxGrade = tripStatistics == null ? Double.NaN : tripStatistics.getMaxGrade(); setGradeValue(context, getView(activity, view, R.id.stats_grade_min), R.string.stats_min, minGrade); setGradeValue(context, getView(activity, view, R.id.stats_grade_max), R.string.stats_max, maxGrade); // Set elevation double elevationGain = tripStatistics == null ? Double.NaN : tripStatistics.getTotalElevationGain(); double minElevation = tripStatistics == null ? Double.NaN : tripStatistics.getMinElevation(); double maxElevation = tripStatistics == null ? Double.NaN : tripStatistics.getMaxElevation(); setElevationValue(context, getView(activity, view, R.id.stats_elevation_gain), R.string.stats_gain, elevationGain, metricUnits); setElevationValue(context, getView(activity, view, R.id.stats_elevation_min), R.string.stats_min, minElevation, metricUnits); setElevationValue(context, getView(activity, view, R.id.stats_elevation_max), R.string.stats_max, maxElevation, metricUnits); } } /** * Sets speed. * * @param context the context * @param view the containing view * @param speedLabelId the speed label id * @param paceLabelId the pace label id * @param speed the speed in meters per second * @param metricUnits true if metric units * @param reportSpeed true if report speed */ private static void setSpeed(Context context, View view, int speedLabelId, int paceLabelId, double speed, boolean metricUnits, boolean reportSpeed) { String[] parts = StringUtils.getSpeedParts(context, speed, metricUnits, reportSpeed); setItem(context, view, reportSpeed ? speedLabelId : paceLabelId, parts[0], parts[1]); } /** * Sets distance value. * * @param context the context * @param view the containing view * @param distance the distance in meters * @param metricUnits true if metric units */ private static void setDistanceValue(Context context, View view, double distance, boolean metricUnits) { String[] parts = StringUtils.getDistanceParts(context, distance, metricUnits); setItem(context, view, R.string.stats_distance, parts[0], parts[1]); } /** * Sets a time value. * * @param context the context * @param view the containing view * @param labelId the label id * @param time the time */ private static void setTimeValue(Context context, View view, int labelId, long time) { String value = time == -1L ? null : StringUtils.formatElapsedTime(time); setItem(context, view, labelId, value, null); } /** * Sets an elevation value. * * @param context the context * @param view the containing view * @param labelId the label id * @param elevation the elevation in meters * @param metricUnits true if metric units */ private static void setElevationValue(Context context, View view, int labelId, double elevation, boolean metricUnits) { String value; String unit; if (Double.isNaN(elevation) || Double.isInfinite(elevation)) { value = null; unit = null; } else { if (metricUnits) { value = StringUtils.formatDecimal(elevation); unit = context.getString(R.string.unit_meter); } else { elevation *= UnitConversions.M_TO_FT; value = StringUtils.formatDecimal(elevation); unit = context.getString(R.string.unit_feet); } } setItem(context, view, labelId, value, unit); } /** * Sets a grade value. * * @param context the context * @param view the containing view * @param labelId the label id * @param grade the grade in fraction between 0 and 1 */ private static void setGradeValue(Context context, View view, int labelId, double grade) { String value = Double.isNaN(grade) || Double.isInfinite(grade) ? null : String.format(Locale.getDefault(), GRADE_FORMAT, Math.round(grade * 100)); setItem(context, view, labelId, value, GRADE_PERCENTAGE); } /** * Sets a coordinate value. * * @param context the context * @param view the containing view * @param labelId the label id * @param coordinate the coordinate in degrees */ private static void setCoordinateValue(Context context, View view, int labelId, double coordinate) { String value = Double.isNaN(coordinate) || Double.isInfinite(coordinate) ? null : StringUtils.formatCoordinate(coordinate); setItem(context, view, labelId, value, null); } /** * Sets a heart rate value (sensor) * * @param context the context * @param viewValue the containing view * @param sensorDataSet the sensorDataSet */ private static void setHeartRateData(Context context, View viewValue, View viewSensor, SensorDataSet sensorDataSet) { String heartRate = context.getString(R.string.value_unknown); String sensor = context.getString(R.string.value_unknown); if (sensorDataSet != null && sensorDataSet.hasHeartRate() && sensorDataSet.isRecent(SensorManager.MAX_SENSOR_DATE_SET_AGE)) { heartRate = StringUtils.formatDecimal(sensorDataSet.getHeartRate(), 0); sensor = sensorDataSet.getSensorName(); } setItem(context, viewValue, R.string.sensor_state_heart_rate, heartRate, context.getString(R.string.sensor_unit_beats_per_minute)); setItem(context, viewSensor, R.string.settings_sensor_bluetooth_sensor, sensor, null); } /** * Sets an item. * * @param context the context * @param view the containing view * @param labelId the label id. -1 to hide the label * @param value the value, can be null * @param unit the unit. Null to hide the unit */ private static void setItem(Context context, View view, int labelId, CharSequence value, CharSequence unit) { TextView labelTextView = view.findViewById(R.id.stats_label); if (labelTextView != null) { if (labelId == -1) { labelTextView.setVisibility(View.GONE); } else { labelTextView.setVisibility(View.VISIBLE); labelTextView.setText(labelId); } } TextView valueTextView = view.findViewById(R.id.stats_value); TextView unitTextView = view.findViewById(R.id.stats_unit); if (valueTextView == null || unitTextView == null) { return; } if (value == null) { value = context.getString(R.string.value_unknown); unitTextView.setVisibility(View.GONE); } else { if (unit == null) { unitTextView.setVisibility(View.GONE); } else { unitTextView.setVisibility(View.VISIBLE); unitTextView.setText(unit); } } valueTextView.setText(value); } /** * Get a view. * * @param activity the activity * @param view the containing view * @param id the id */ //TODO What is the difference between the two cases? Is it (still) necessary? private static View getView(Activity activity, View view, int id) { if (activity != null) { return activity.findViewById(id); } else { return view.findViewById(id); } } }