From 0460425d43a5ccf5d109fe56d9cd034895395334 Mon Sep 17 00:00:00 2001 From: Jimmy Shih Date: Thu, 24 May 2012 12:11:32 -0700 Subject: [PATCH] Clean up ChartValueSeries. - include metric and imperial title id - include both speed and pace series --- MyTracks/res/values/colors.xml | 4 + MyTracks/res/values/strings.xml | 44 ++- .../apps/mytracks/ChartValueSeries.java | 349 ++++++++---------- .../android/apps/mytracks/ChartView.java | 151 ++++---- .../mytracks/fragments/ChartFragment.java | 59 +-- .../io/sendtogoogle/UploadResultActivity.java | 3 +- .../settings/StatsSettingsActivity.java | 10 +- .../apps/mytracks/ChartValueSeriesTest.java | 61 +-- .../mytracks/fragments/ChartFragmentTest.java | 74 ++-- .../UploadResultActivityTest.java | 3 + 10 files changed, 370 insertions(+), 388 deletions(-) diff --git a/MyTracks/res/values/colors.xml b/MyTracks/res/values/colors.xml index 936356e81..672ffeeba 100644 --- a/MyTracks/res/values/colors.xml +++ b/MyTracks/res/values/colors.xml @@ -33,6 +33,10 @@ limitations under the License. #FF0066FF #40C8E0FF + + #FF0066FF + #40C8E0FF + #FFCC3300 #40E88A70 diff --git a/MyTracks/res/values/strings.xml b/MyTracks/res/values/strings.xml index da27b7b8d..243620cee 100644 --- a/MyTracks/res/values/strings.xml +++ b/MyTracks/res/values/strings.xml @@ -451,6 +451,10 @@ limitations under the License. %2$.1f speed in miles per hour"> Average speed: %1$.2f km/h (%2$.1f mi/h) + + Elevation (ft) + Elevation gain: %1$d m (%2$d ft) + + Elevation (m) + Moving time: %1$s + + Pace (hr/mi) + + + Pace (hr/km) + Power (W) + + Speed (mi/hr) + + + Speed (km/hr) + Point @@ -1898,26 +1922,6 @@ limitations under the License. of inclination to the horizontal, also known as slope or incline."> Grade - - Pace (hr/mi) - - - Pace (hr/km) - - - Speed (mi/hr) - - - Speed (km/hr) - diff --git a/MyTracks/src/com/google/android/apps/mytracks/ChartValueSeries.java b/MyTracks/src/com/google/android/apps/mytracks/ChartValueSeries.java index 58650580d..8fcb2cd3a 100644 --- a/MyTracks/src/com/google/android/apps/mytracks/ChartValueSeries.java +++ b/MyTracks/src/com/google/android/apps/mytracks/ChartValueSeries.java @@ -22,195 +22,93 @@ import com.google.common.annotations.VisibleForTesting; import android.content.Context; import android.graphics.Canvas; import android.graphics.Paint; +import android.graphics.Paint.Align; import android.graphics.Paint.Style; import android.graphics.Path; import java.text.NumberFormat; /** - * This class encapsulates the meta data for one series of chart values. - * + * This class encapsulates the meta data for one series of the chart values. + * * @author Sandor Dornbush */ public class ChartValueSeries { private static final float STROKE_WIDTH = 2f; + private final int absoluteMin; + private final int absoluteMax; + private final int[] intervalValues; + private final int metricTitleId; + private final int imperialTitleId; + private final Paint fillPaint; + private final Paint strokePaint; + private final Paint titlePaint; + private final Paint markerPaint; private final ExtremityMonitor extremityMonitor; private final NumberFormat numberFormat; private final Path path; - private final Paint fillPaint; - private final Paint strokePaint; - private final Paint labelPaint; - private final YAxisDimension markingInterval; - private String title; - - private boolean enabled = true; + private int interval = 1; - private int effectiveMin = 0; - private int effectiveMax = 1; + private int minMarkerValue = 0; + private int maxMarkerValue = interval * ChartView.Y_AXIS_INTERVALS; + private boolean enabled = true; /** - * This class to calculates the y axis dimension, interval, effective min, and - * effective max. - */ - public static class YAxisDimension { - private final int numberOfIntervals; - private final int absoluteMin; - private final int absoluteMax; - private final int[] intervalValues; - - /** - * Constructor. - * - * @param numberOfIntervals the number of intervals - * @param absoluteMin the absolute minimum value - * @param absoluteMax the absolute maximum value - * @param intervalValues the list of interval values - */ - public YAxisDimension( - int numberOfIntervals, int absoluteMin, int absoluteMax, int[] intervalValues) { - this.numberOfIntervals = numberOfIntervals; - this.absoluteMin = absoluteMin; - this.absoluteMax = absoluteMax; - this.intervalValues = intervalValues; - } - - /** - * Gets the y axis dimension. Returns an array of int[3], containing interval, effectiveMin, - * and effectiveMax. - * - * @param min the min value - * @param max the max value - */ - public int[] getYAxisDimension(double min, double max) { - min = Math.max(min, absoluteMin); - max = Math.min(max, absoluteMax); - int interval = 0; - int effectiveMin = 0; - for (int i = 0; i < intervalValues.length; i++) { - interval = intervalValues[i]; - effectiveMin = getEffetiveMin(min, interval); - double targetInterval = (max - effectiveMin) / numberOfIntervals; - if (interval >= targetInterval) { - break; - } - } - int effectiveMax = getEffectiveMax(max, interval); - return new int[] { interval, effectiveMin, effectiveMax }; - } - - /** - * Gets the effective min value. - * - * @param min the min value - * @param interval the interval - */ - private int getEffetiveMin(double min, int interval) { - int value = (int) (min / interval) * interval; - // value > min if min is negative - if (value > min) { - return value - interval; - } - return value; - } - - /** - * Gets the effective max value - * - * @param max the max value - * @param interval the interval - */ - private int getEffectiveMax(double max, int interval) { - return ((int) (max / interval)) * interval + interval; - } - } - - /** - * Constructs a new chart value series. - * + * Constructor. + * * @param context the context + * @param absoluteMin the absolute min value + * @param absoluteMax the absolute max value + * @param intervalValues the list of interval values + * @param metricTitleId the metric title id + * @param imperialTitleId the imperial title id * @param fillColor the fill color * @param strokeColor the stroke color - * @param yAxisDimension the marking interval - * @param titleId the title id */ - public ChartValueSeries(Context context, int fillColor, int strokeColor, - YAxisDimension yAxisDimension, int titleId) { - extremityMonitor = new ExtremityMonitor(); - numberFormat = NumberFormat.getIntegerInstance(); - path = new Path(); + public ChartValueSeries(Context context, int absoluteMin, int absoluteMax, int[] intervalValues, + int metricTitleId, int imperialTitleId, int fillColor, int strokeColor) { + this.absoluteMin = absoluteMin; + this.absoluteMax = absoluteMax; + this.intervalValues = intervalValues; + this.metricTitleId = metricTitleId; + this.imperialTitleId = imperialTitleId; + fillPaint = new Paint(); fillPaint.setStyle(Style.FILL); fillPaint.setColor(context.getResources().getColor(fillColor)); fillPaint.setAntiAlias(true); - if (strokeColor != -1) { - strokePaint = new Paint(); - strokePaint.setStyle(Style.STROKE); - strokePaint.setColor(context.getResources().getColor(strokeColor)); - strokePaint.setAntiAlias(true); - // Make a copy of the stroke paint with the default thickness - labelPaint = new Paint(strokePaint); - strokePaint.setStrokeWidth(STROKE_WIDTH); - } else { - strokePaint = null; - labelPaint = fillPaint; - } - this.markingInterval = yAxisDimension; - title = context.getString(titleId); + + strokePaint = new Paint(); + strokePaint.setStyle(Style.STROKE); + strokePaint.setColor(context.getResources().getColor(strokeColor)); + strokePaint.setAntiAlias(true); + + // Make copies of the stroke paint with the default thickness + titlePaint = new Paint(strokePaint); + titlePaint.setTextSize(ChartView.LARGE_TEXT_SIZE); + titlePaint.setTextAlign(Align.CENTER); + + markerPaint = new Paint(strokePaint); + markerPaint.setTextSize(ChartView.MEDIUM_TEXT_SIZE); + markerPaint.setTextAlign(Align.RIGHT); + + // Set stroke paint thickness + strokePaint.setStrokeWidth(STROKE_WIDTH); + + extremityMonitor = new ExtremityMonitor(); + numberFormat = NumberFormat.getIntegerInstance(); + path = new Path(); } /** - * Returns true if the series has data. + * Sets the series enabled value. + * + * @param enabled true to enable */ - public boolean hasData() { - return extremityMonitor.hasData(); - } - - /** - * Updates the series with a new value. - * - * @param value the new value - */ - public void update(double value) { - extremityMonitor.update(value); - } - - /** - * Gets the number format for the series. - */ - public NumberFormat getNumberFormat() { - return numberFormat; - } - - /** - * Gets the path. - */ - public Path getPath() { - return path; - } - - /** - * Gets the label paint. - */ - public Paint getLabelPaint() { - return labelPaint; - } - - /** - * Gets the title. - */ - public String getTitle() { - return title; - } - - /** - * Sets the title. - * - * @param title the title - */ - public void setTitle(String title) { - this.title = title; + public void setEnabled(boolean enabled) { + this.enabled = enabled; } /** @@ -221,65 +119,144 @@ public class ChartValueSeries { } /** - * Sets the enabled value. - * - * @param enabled true to enable + * Returns true if the series has data. */ - public void setEnabled(boolean enabled) { - this.enabled = enabled; + public boolean hasData() { + return extremityMonitor.hasData(); } - + + /** + * Updates the series with a new value. + * + * @param value the new value + */ + public void update(double value) { + extremityMonitor.update(value); + } + + /** + * Gets the path. + */ + public Path getPath() { + return path; + } + /** * Draws the path on canvas. - * + * * @param canvas the canvas */ public void drawPath(Canvas canvas) { canvas.drawPath(path, fillPaint); - if (strokePaint != null) { - canvas.drawPath(path, strokePaint); - } + canvas.drawPath(path, strokePaint); } /** * Updates the y axis dimension. */ public void updateDimension() { - boolean hasData = extremityMonitor.hasData(); - double min = hasData ? extremityMonitor.getMin() : 0.0; - double max = hasData ? extremityMonitor.getMax() : 1.0; - int[] dimension = markingInterval.getYAxisDimension(min, max); - interval = dimension[0]; - effectiveMin = dimension[1]; - effectiveMax = dimension[2]; + double min = hasData() ? extremityMonitor.getMin() : 0.0; + double max = hasData() ? extremityMonitor.getMax() : 1.0; + min = Math.max(min, absoluteMin); + max = Math.min(max, absoluteMax); + interval = getInterval(min, max); + minMarkerValue = getMinMarkerValue(min, interval); + maxMarkerValue = minMarkerValue + interval * ChartView.Y_AXIS_INTERVALS; } /** - * Gets the y axis interval value. + * Gets the interval value. + * + * @param min the min value + * @param max the max value + */ + private int getInterval(double min, double max) { + for (int i = 0; i < intervalValues.length; i++) { + int intervalValue = intervalValues[i]; + int minValue = getMinMarkerValue(min, intervalValue); + double targetInterval = (max - minValue) / ChartView.Y_AXIS_INTERVALS; + if (intervalValue >= targetInterval) { + return intervalValue; + } + } + // Return the largest interval + return intervalValues[intervalValues.length - 1]; + } + + /** + * Gets the min marker value. + * + * @param min the min series value + * @param intervalValue the interval value + */ + private int getMinMarkerValue(double min, int intervalValue) { + // Round down to the nearest intervalValue + int value = ((int) (min / intervalValue)) * intervalValue; + // value > min if min is negative + if (value > min) { + return value - intervalValue; + } + return value; + } + + /** + * Gets the interval value. */ public int getInterval() { return interval; } /** - * Gets the minimum value. + * Gets the minimum marker value. */ - public int getMin() { - return effectiveMin; + public int getMinMarkerValue() { + return minMarkerValue; } /** - * Gets the maximum value. + * Gets the maximum marker value. */ @VisibleForTesting - int getMax() { - return effectiveMax; + int getMaxMarkerValue() { + return maxMarkerValue; } /** - * Gets the maximum label length. + * Gets the title id. */ - public int getMaxLabelLength() { - return Math.max(numberFormat.format(getMin()).length(), numberFormat.format(getMax()).length()); + public int getTitleId(boolean metricUnits) { + return metricUnits ? metricTitleId : imperialTitleId; + } + + /** + * Gets the title paint. + */ + public Paint getTitlePaint() { + return titlePaint; + } + + /** + * Gets the marker paint. + */ + public Paint getMarkerPaint() { + return markerPaint; + } + + /** + * Gets the largest marker. + */ + public String getLargestMarker() { + String minMarker = numberFormat.format(getMinMarkerValue()); + String maxMarker = numberFormat.format(getMaxMarkerValue()); + return minMarker.length() >= maxMarker.length() ? minMarker : maxMarker; + } + + /** + * Formats a marker value. + * + * @param value the value + */ + public String formatMarker(int value) { + return numberFormat.format(value); } } diff --git a/MyTracks/src/com/google/android/apps/mytracks/ChartView.java b/MyTracks/src/com/google/android/apps/mytracks/ChartView.java index 330493387..802bd80f9 100644 --- a/MyTracks/src/com/google/android/apps/mytracks/ChartView.java +++ b/MyTracks/src/com/google/android/apps/mytracks/ChartView.java @@ -15,7 +15,6 @@ */ package com.google.android.apps.mytracks; -import com.google.android.apps.mytracks.ChartValueSeries.YAxisDimension; import com.google.android.apps.mytracks.content.Waypoint; import com.google.android.apps.mytracks.stats.ExtremityMonitor; import com.google.android.apps.mytracks.util.IntentUtils; @@ -30,7 +29,6 @@ import android.graphics.Canvas; import android.graphics.Color; import android.graphics.DashPathEffect; import android.graphics.Paint; -import android.graphics.Paint.Align; import android.graphics.Paint.Style; import android.graphics.Path; import android.graphics.drawable.Drawable; @@ -50,6 +48,21 @@ import java.util.ArrayList; * @author Leif Hendrik Wilden */ public class ChartView extends View { + + public static final float LARGE_TEXT_SIZE = 22f; + public static final float MEDIUM_TEXT_SIZE = 18f; + public static final float SMALL_TEXT_SIZE = 14f; + + public static final int Y_AXIS_INTERVALS = 5; + + public static final int NUM_SERIES = 6; + public static final int ELEVATION_SERIES = 0; + public static final int SPEED_SERIES = 1; + public static final int PACE_SERIES = 2; + public static final int HEART_RATE_SERIES = 3; + public static final int CADENCE_SERIES = 4; + public static final int POWER_SERIES = 5; + private static final int MIN_ZOOM_LEVEL = 1; /* @@ -66,8 +79,6 @@ public class ChartView extends View { private int zoomLevel = 1; private int maxZoomLevel = 10; - private static final int MAX_INTERVALS = 5; - /* * Borders, margins, dimensions (in pixels): */ @@ -109,15 +120,6 @@ public class ChartView extends View { */ private double maxX = 1; - /** - * The various series. - */ - public static final int ELEVATION_SERIES = 0; - public static final int SPEED_SERIES = 1; - public static final int POWER_SERIES = 2; - public static final int CADENCE_SERIES = 3; - public static final int HEART_RATE_SERIES = 4; - public static final int NUM_SERIES = 5; private ChartValueSeries[] series; private final ExtremityMonitor xMonitor = new ExtremityMonitor(); @@ -204,41 +206,54 @@ public class ChartView extends View { series = new ChartValueSeries[NUM_SERIES]; // Create the value series. - series[ELEVATION_SERIES] = - new ChartValueSeries(context, - R.color.elevation_fill, - R.color.elevation_border, - new YAxisDimension(MAX_INTERVALS, Integer.MIN_VALUE, Integer.MAX_VALUE, - new int[] {5, 10, 25, 50, 100, 250, 500, 1000, 2500, 5000}), - R.string.stats_elevation); - - series[SPEED_SERIES] = - new ChartValueSeries(context, - R.color.speed_fill, - R.color.speed_border, - new YAxisDimension(MAX_INTERVALS, 0, Integer.MAX_VALUE, - new int[] {1, 5, 10, 20, 50}), - R.string.stats_speed); - series[POWER_SERIES] = - new ChartValueSeries(context, - R.color.power_fill, - R.color.power_border, - new YAxisDimension(MAX_INTERVALS, 0, 1000, new int[] {5, 50, 100, 200}), - R.string.sensor_state_power); - series[CADENCE_SERIES] = - new ChartValueSeries(context, - R.color.cadence_fill, - R.color.cadence_border, - new YAxisDimension(MAX_INTERVALS, 0, Integer.MAX_VALUE, - new int[] {5, 10, 25, 50}), - R.string.sensor_state_cadence); - series[HEART_RATE_SERIES] = - new ChartValueSeries(context, - R.color.heartrate_fill, - R.color.heartrate_border, - new YAxisDimension(MAX_INTERVALS, 0, Integer.MAX_VALUE, - new int[] {25, 50}), - R.string.sensor_state_heart_rate); + series[ELEVATION_SERIES] = new ChartValueSeries(context, + Integer.MIN_VALUE, + Integer.MAX_VALUE, + new int[] {5, 10, 25, 50, 100, 250, 500, 1000, 2500, 5000}, + R.string.description_elevation_metric, + R.string.description_elevation_imperial, + R.color.elevation_fill, + R.color.elevation_border); + series[SPEED_SERIES] = new ChartValueSeries(context, + 0, + Integer.MAX_VALUE, + new int[] {1, 5, 10, 20, 50}, + R.string.description_speed_metric, + R.string.description_speed_imperial, + R.color.speed_fill, + R.color.speed_border); + series[PACE_SERIES] = new ChartValueSeries(context, + 0, + Integer.MAX_VALUE, + new int[] { 1, 2, 5, 10, 15, 20, 30, 60, 120 }, + R.string.description_pace_metric, + R.string.description_pace_imperial, + R.color.pace_fill, + R.color.pace_border); + series[HEART_RATE_SERIES] = new ChartValueSeries(context, + 0, + Integer.MAX_VALUE, + new int[] {25, 50}, + R.string.description_sensor_heart_rate, + R.string.description_sensor_heart_rate, + R.color.heartrate_fill, + R.color.heartrate_border); + series[CADENCE_SERIES] = new ChartValueSeries(context, + 0, + Integer.MAX_VALUE, + new int[] {5, 10, 25, 50}, + R.string.description_sensor_cadence, + R.string.description_sensor_cadence, + R.color.cadence_fill, + R.color.cadence_border); + series[POWER_SERIES] = new ChartValueSeries(context, + 0, + 1000, + new int[] {5, 50, 100, 200}, + R.string.description_sensor_power, + R.string.description_sensor_power, + R.color.power_fill, + R.color.power_border); } public void clearWaypoints() { @@ -263,12 +278,6 @@ public class ChartView extends View { this.metricUnits = metricUnits; } - public void setReportSpeed(boolean reportSpeed, Context c) { - series[SPEED_SERIES].setTitle(c.getString(reportSpeed - ? R.string.stats_speed - : R.string.stats_pace)); - } - private void addDataPointInternal(double[] theData) { xMonitor.update(theData[0]); int min = Math.min(series.length, theData.length - 1); @@ -622,7 +631,8 @@ public class ChartView extends View { for (ChartValueSeries cvs : series) { if (cvs.isEnabled() && cvs.hasData()) { int x = (int) (w * (double) ++j / sections) + getScrollX(); - c.drawText(cvs.getTitle(), x, topBorder, cvs.getLabelPaint()); + c.drawText( + getContext().getString(cvs.getTitleId(metricUnits)), x, topBorder, cvs.getTitlePaint()); } } } @@ -719,7 +729,7 @@ public class ChartView extends View { int maxLength = 0; for (ChartValueSeries cvs : series) { if (cvs.isEnabled() && cvs.hasData()) { - maxLength += cvs.getMaxLabelLength(); + maxLength += cvs.getLargestMarker().length(); } } float density = getContext().getResources().getDisplayMetrics().density; @@ -758,9 +768,9 @@ public class ChartView extends View { } private int getY(ChartValueSeries cvs, double y) { - int effectiveSpread = cvs.getInterval() * MAX_INTERVALS; + int effectiveSpread = cvs.getInterval() * Y_AXIS_INTERVALS; return topBorder + effectiveHeight - - (int) ((y - cvs.getMin()) * effectiveHeight / effectiveSpread); + - (int) ((y - cvs.getMinMarkerValue()) * effectiveHeight / effectiveSpread); } /** Draws the labels on the X axis into the given canvas. */ @@ -792,8 +802,8 @@ public class ChartView extends View { private float drawYLabels(ChartValueSeries cvs, Canvas c, int x) { int interval = cvs.getInterval(); float maxTextWidth = 0; - for (int i = 0; i < MAX_INTERVALS; ++i) { - maxTextWidth = Math.max(maxTextWidth, drawYLabel(cvs, c, x, i * interval + cvs.getMin())); + for (int i = 0; i < Y_AXIS_INTERVALS; ++i) { + maxTextWidth = Math.max(maxTextWidth, drawYLabel(cvs, c, x, i * interval + cvs.getMinMarkerValue())); } return maxTextWidth; } @@ -814,14 +824,13 @@ public class ChartView extends View { /** Draws a single label on the Y axis. */ private float drawYLabel(ChartValueSeries cvs, Canvas c, int x, int y) { - int desiredY = (int) ((y - cvs.getMin()) * effectiveHeight / - (cvs.getInterval() * MAX_INTERVALS)); + int desiredY = (int) ((y - cvs.getMinMarkerValue()) * effectiveHeight / + (cvs.getInterval() * Y_AXIS_INTERVALS)); desiredY = topBorder + effectiveHeight + FONT_HEIGHT / 2 - desiredY - 1; - Paint p = new Paint(cvs.getLabelPaint()); - p.setTextAlign(Align.RIGHT); - String text = cvs.getNumberFormat().format(y); - c.drawText(text, x, desiredY, p); - return p.measureText(text); + Paint markerPaint = cvs.getMarkerPaint(); + String text = cvs.formatMarker(y); + c.drawText(text, x, desiredY, markerPaint); + return markerPaint.measureText(text); } /** Draws the actual X axis line and its label. */ @@ -844,8 +853,8 @@ public class ChartView extends View { canvas.drawLine(leftBorder, UNIT_BORDER + topBorder, leftBorder, effectiveHeight + topBorder, borderPaint); - for (int i = 1; i < MAX_INTERVALS; ++i) { - int y = i * effectiveHeight / MAX_INTERVALS + topBorder; + for (int i = 1; i < Y_AXIS_INTERVALS; ++i) { + int y = i * effectiveHeight / Y_AXIS_INTERVALS + topBorder; canvas.drawLine(leftBorder - 5, y, leftBorder, y, gridPaint); } @@ -858,8 +867,8 @@ public class ChartView extends View { /** Draws the grid for the graph. */ private void drawGrid(Canvas c) { float rightEdge = getX(maxX); - for (int i = 1; i < MAX_INTERVALS; ++i) { - int y = i * effectiveHeight / MAX_INTERVALS + topBorder; + for (int i = 1; i < Y_AXIS_INTERVALS; ++i) { + int y = i * effectiveHeight / Y_AXIS_INTERVALS + topBorder; c.drawLine(leftBorder, y, rightEdge, y, gridBarPaint); } } diff --git a/MyTracks/src/com/google/android/apps/mytracks/fragments/ChartFragment.java b/MyTracks/src/com/google/android/apps/mytracks/fragments/ChartFragment.java index 9a7f87b8d..679129d3e 100644 --- a/MyTracks/src/com/google/android/apps/mytracks/fragments/ChartFragment.java +++ b/MyTracks/src/com/google/android/apps/mytracks/fragments/ChartFragment.java @@ -77,15 +77,15 @@ public class ChartFragment extends Fragment implements TrackDataListener { private boolean reportSpeed = true; private boolean chartByDistance = true; - private boolean[] chartShow = new boolean[] {true, true, true, true, true}; + private boolean[] chartShow = new boolean[] {true, true, true, true, true, true}; // UI elements private ChartView chartView; private ZoomControls zoomControls; /** - * A runnable that will remove the spinner (if any), enable/disable zoom - * controls and orange pointer as appropriate and redraw. + * A runnable that will enable/disable zoom controls and orange pointer as + * appropriate and redraw. */ private final Runnable updateChart = new Runnable() { @Override @@ -215,7 +215,7 @@ public class ChartFragment extends Fragment implements TrackDataListener { @Override public void onNewTrackPoint(Location location) { if (LocationUtils.isValidLocation(location)) { - double[] data = new double[6]; + double[] data = new double[ChartView.NUM_SERIES + 1]; fillDataPoint(location, data); pendingPoints.add(data); } @@ -280,7 +280,8 @@ public class ChartFragment extends Fragment implements TrackDataListener { return false; } reportSpeed = speed; - chartView.setReportSpeed(speed, getActivity()); + setSeriesEnabled(ChartView.SPEED_SERIES, reportSpeed); + setSeriesEnabled(ChartView.PACE_SERIES, !reportSpeed); getActivity().runOnUiThread(new Runnable() { @Override public void run() { @@ -310,6 +311,10 @@ public class ChartFragment extends Fragment implements TrackDataListener { getActivity(), R.string.chart_show_speed_key, PreferencesUtils.CHART_SHOW_SPEED_DEFAULT))) { needUpdate = true; } + if (setSeriesEnabled(ChartView.PACE_SERIES, !PreferencesUtils.getBoolean( + getActivity(), R.string.chart_show_speed_key, PreferencesUtils.CHART_SHOW_SPEED_DEFAULT))) { + needUpdate = true; + } if (setSeriesEnabled(ChartView.POWER_SERIES, PreferencesUtils.getBoolean( getActivity(), R.string.chart_show_power_key, PreferencesUtils.CHART_SHOW_POWER_DEFAULT))) { needUpdate = true; @@ -405,13 +410,14 @@ public class ChartFragment extends Fragment implements TrackDataListener { } /** - * Given a location, fill in a data point, an array of double[6].
+ * Given a location, fill in a data point, an array of double[].
* data[0] = time/distance
* data[1] = elevation
* data[2] = speed
- * data[3] = power
- * data[4] = cadence
- * data[5] = heart rate
+ * data[3] = pace
+ * data[4] = heart rate
+ * data[5] = cadence
+ * data[6] = power
* * @param location the location * @param data the data point to fill in, can be null @@ -421,9 +427,10 @@ public class ChartFragment extends Fragment implements TrackDataListener { double timeOrDistance = Double.NaN; double elevation = Double.NaN; double speed = Double.NaN; - double power = Double.NaN; + double pace = Double.NaN; + double heartRate = Double.NaN; double cadence = Double.NaN; - double heartRate = Double.NaN; + double power = Double.NaN; // TODO: Use TripStatisticsBuilder if (chartByDistance) { @@ -460,36 +467,36 @@ public class ChartFragment extends Fragment implements TrackDataListener { if (!metricUnits) { speed *= UnitConversions.KM_TO_MI; } - if (!reportSpeed) { - speed = speed == 0 ? 0.0 : 60.0 / speed; - } - + pace = speed == 0 ? 0.0 : 60.0 / speed; + if (location instanceof MyTracksLocation && ((MyTracksLocation) location).getSensorDataSet() != null) { SensorDataSet sensorDataSet = ((MyTracksLocation) location).getSensorDataSet(); - if (sensorDataSet.hasPower() && sensorDataSet.getPower().getState() == Sensor.SensorState.SENDING - && sensorDataSet.getPower().hasValue()) { - power = sensorDataSet.getPower().getValue(); + if (sensorDataSet.hasHeartRate() + && sensorDataSet.getHeartRate().getState() == Sensor.SensorState.SENDING + && sensorDataSet.getHeartRate().hasValue()) { + heartRate = sensorDataSet.getHeartRate().getValue(); } if (sensorDataSet.hasCadence() && sensorDataSet.getCadence().getState() == Sensor.SensorState.SENDING && sensorDataSet.getCadence().hasValue()) { cadence = sensorDataSet.getCadence().getValue(); } - if (sensorDataSet.hasHeartRate() - && sensorDataSet.getHeartRate().getState() == Sensor.SensorState.SENDING - && sensorDataSet.getHeartRate().hasValue()) { - heartRate = sensorDataSet.getHeartRate().getValue(); + if (sensorDataSet.hasPower() + && sensorDataSet.getPower().getState() == Sensor.SensorState.SENDING + && sensorDataSet.getPower().hasValue()) { + power = sensorDataSet.getPower().getValue(); } } - + if (data != null) { data[0] = timeOrDistance; data[1] = elevation; data[2] = speed; - data[3] = power; - data[4] = cadence; - data[5] = heartRate; + data[3] = pace; + data[4] = heartRate; + data[5] = cadence; + data[6] = power; } lastLocation = location; } diff --git a/MyTracks/src/com/google/android/apps/mytracks/io/sendtogoogle/UploadResultActivity.java b/MyTracks/src/com/google/android/apps/mytracks/io/sendtogoogle/UploadResultActivity.java index 01369bb09..354a389f9 100644 --- a/MyTracks/src/com/google/android/apps/mytracks/io/sendtogoogle/UploadResultActivity.java +++ b/MyTracks/src/com/google/android/apps/mytracks/io/sendtogoogle/UploadResultActivity.java @@ -44,7 +44,8 @@ import android.widget.TextView; public class UploadResultActivity extends FragmentActivity { private static final String TAG = UploadResultActivity.class.getSimpleName(); - private static final int DIALOG_RESULT_ID = 0; + @VisibleForTesting + static final int DIALOG_RESULT_ID = 0; private SendRequest sendRequest; private String shareUrl; diff --git a/MyTracks/src/com/google/android/apps/mytracks/settings/StatsSettingsActivity.java b/MyTracks/src/com/google/android/apps/mytracks/settings/StatsSettingsActivity.java index 7b5781756..89e5214f7 100644 --- a/MyTracks/src/com/google/android/apps/mytracks/settings/StatsSettingsActivity.java +++ b/MyTracks/src/com/google/android/apps/mytracks/settings/StatsSettingsActivity.java @@ -71,11 +71,9 @@ public class StatsSettingsActivity extends AbstractSettingsActivity { getString(R.string.report_speed_key)); boolean metric = PreferencesUtils.getBoolean( this, R.string.metric_units_key, PreferencesUtils.METRIC_UNITS_DEFAULT); - reportSpeedCheckBoxPreference.setSummaryOn( - metric ? getString(R.string.settings_stats_rate_speed_metric) - : getString(R.string.settings_stats_rate_speed_imperial)); - reportSpeedCheckBoxPreference.setSummaryOff( - metric ? getString(R.string.settings_stats_rate_pace_metric) - : getString(R.string.settings_stats_rate_pace_imperial)); + reportSpeedCheckBoxPreference.setSummaryOn(metric ? getString(R.string.description_speed_metric) + : getString(R.string.description_speed_imperial)); + reportSpeedCheckBoxPreference.setSummaryOff(metric ? getString(R.string.description_pace_metric) + : getString(R.string.description_pace_imperial)); } } diff --git a/MyTracksTest/src/com/google/android/apps/mytracks/ChartValueSeriesTest.java b/MyTracksTest/src/com/google/android/apps/mytracks/ChartValueSeriesTest.java index 3d0ad000c..39e69f67c 100644 --- a/MyTracksTest/src/com/google/android/apps/mytracks/ChartValueSeriesTest.java +++ b/MyTracksTest/src/com/google/android/apps/mytracks/ChartValueSeriesTest.java @@ -15,7 +15,6 @@ */ package com.google.android.apps.mytracks; -import com.google.android.apps.mytracks.ChartValueSeries.YAxisDimension; import com.google.android.maps.mytracks.R; import android.test.AndroidTestCase; @@ -30,18 +29,20 @@ public class ChartValueSeriesTest extends AndroidTestCase { @Override protected void setUp() throws Exception { - YAxisDimension yAxisDimension = new YAxisDimension( - 5, Integer.MIN_VALUE, Integer.MAX_VALUE, new int[] { 100 }); - series = new ChartValueSeries(getContext(), R.color.elevation_fill, R.color.elevation_border, - yAxisDimension, R.string.stats_elevation); + series = new ChartValueSeries(getContext(), + Integer.MIN_VALUE, + Integer.MAX_VALUE, + new int[] {100, 1000 }, + R.string.description_elevation_metric, + R.string.description_elevation_imperial, + R.color.elevation_fill, + R.color.elevation_border); } public void testInitialConditions() { assertEquals(1, series.getInterval()); - assertEquals(1, series.getMaxLabelLength()); - assertEquals(0, series.getMin()); - assertEquals(1, series.getMax()); - assertEquals(getContext().getString(R.string.stats_elevation), series.getTitle()); + assertEquals(0, series.getMinMarkerValue()); + assertEquals(5, series.getMaxMarkerValue()); assertTrue(series.isEnabled()); } @@ -55,49 +56,25 @@ public class ChartValueSeriesTest extends AndroidTestCase { series.update(10); series.updateDimension(); assertEquals(100, series.getInterval()); - assertEquals(3, series.getMaxLabelLength()); - assertEquals(0, series.getMin()); - assertEquals(100, series.getMax()); + assertEquals(0, series.getMinMarkerValue()); + assertEquals(500, series.getMaxMarkerValue()); } public void testBigUpdates() { series.update(0); series.update(901); series.updateDimension(); - assertEquals(100, series.getInterval()); - assertEquals(5, series.getMaxLabelLength()); - assertEquals(0, series.getMin()); - assertEquals(1000, series.getMax()); + assertEquals(1000, series.getInterval()); + assertEquals(0, series.getMinMarkerValue()); + assertEquals(5000, series.getMaxMarkerValue()); } public void testNotZeroBasedUpdates() { - series.update(500); - series.update(1401); + series.update(220); + series.update(250); series.updateDimension(); assertEquals(100, series.getInterval()); - assertEquals(5, series.getMaxLabelLength()); - assertEquals(500, series.getMin()); - assertEquals(1500, series.getMax()); - } - - public void testYAxisDimension_minAligned() { - YAxisDimension yAxisDimension = new YAxisDimension( - 5, Integer.MIN_VALUE, Integer.MAX_VALUE, new int[] { 10, 50, 100 }); - assertEquals(10, yAxisDimension.getYAxisDimension(0, 15)[0]); - assertEquals(10, yAxisDimension.getYAxisDimension(0, 50)[0]); - assertEquals(50, yAxisDimension.getYAxisDimension(0, 111)[0]); - assertEquals(50, yAxisDimension.getYAxisDimension(0, 250)[0]); - assertEquals(100, yAxisDimension.getYAxisDimension(0, 251)[0]); - assertEquals(100, yAxisDimension.getYAxisDimension(0, 10000)[0]); - } - - public void testYAxisDimension_minNotAligned() { - YAxisDimension settings = new YAxisDimension( - 5, Integer.MIN_VALUE, Integer.MAX_VALUE, new int[] { 10, 50, 100 }); - assertEquals(50, settings.getYAxisDimension(5, 55)[0]); - assertEquals(10, settings.getYAxisDimension(10, 60)[0]); - assertEquals(50, settings.getYAxisDimension(7, 250)[0]); - assertEquals(100, settings.getYAxisDimension(7, 257)[0]); - assertEquals(100, settings.getYAxisDimension(11, 10000)[0]); + assertEquals(200, series.getMinMarkerValue()); + assertEquals(700, series.getMaxMarkerValue()); } } diff --git a/MyTracksTest/src/com/google/android/apps/mytracks/fragments/ChartFragmentTest.java b/MyTracksTest/src/com/google/android/apps/mytracks/fragments/ChartFragmentTest.java index f171510a6..8ba25ceac 100644 --- a/MyTracksTest/src/com/google/android/apps/mytracks/fragments/ChartFragmentTest.java +++ b/MyTracksTest/src/com/google/android/apps/mytracks/fragments/ChartFragmentTest.java @@ -52,31 +52,32 @@ public class ChartFragmentTest extends AndroidTestCase { // No input. double[] point = fillDataPointTestHelper(myTracksLocation); - assertEquals(Double.NaN, point[3]); - assertEquals(Double.NaN, point[4]); - assertEquals(Double.NaN, point[5]); + assertEquals(Double.NaN, point[ChartView.HEART_RATE_SERIES + 1]); + assertEquals(Double.NaN, point[ChartView.CADENCE_SERIES + 1]); + assertEquals(Double.NaN, point[ChartView.POWER_SERIES + 1]); // Input incorrect state. // Creates SensorData. - Sensor.SensorData.Builder powerData = Sensor.SensorData.newBuilder() - .setValue(20).setState(Sensor.SensorState.NONE); - Sensor.SensorData.Builder cadenceData = Sensor.SensorData.newBuilder() - .setValue(20).setState(Sensor.SensorState.NONE); Sensor.SensorData.Builder heartRateData = Sensor.SensorData.newBuilder() - .setValue(20).setState(Sensor.SensorState.NONE); + .setValue(100).setState(Sensor.SensorState.NONE); + Sensor.SensorData.Builder cadenceData = Sensor.SensorData.newBuilder() + .setValue(101).setState(Sensor.SensorState.NONE); + Sensor.SensorData.Builder powerData = Sensor.SensorData.newBuilder() + .setValue(102).setState(Sensor.SensorState.NONE); + // Creates SensorDataSet. SensorDataSet sensorDataSet = myTracksLocation.getSensorDataSet(); sensorDataSet = sensorDataSet.toBuilder() - .setPower(powerData) - .setCadence(cadenceData) .setHeartRate(heartRateData) + .setCadence(cadenceData) + .setPower(powerData) .build(); myTracksLocation.setSensorData(sensorDataSet); // Test. point = fillDataPointTestHelper(myTracksLocation); - assertEquals(Double.NaN, point[3]); - assertEquals(Double.NaN, point[4]); - assertEquals(Double.NaN, point[5]); + assertEquals(Double.NaN, point[ChartView.HEART_RATE_SERIES + 1]); + assertEquals(Double.NaN, point[ChartView.CADENCE_SERIES + 1]); + assertEquals(Double.NaN, point[ChartView.POWER_SERIES + 1]); } /** @@ -87,31 +88,31 @@ public class ChartFragmentTest extends AndroidTestCase { MyTracksLocation myTracksLocation = TrackStubUtils.createMyTracksLocation(); // No input. double[] point = fillDataPointTestHelper(myTracksLocation); - assertEquals(Double.NaN, point[3]); - assertEquals(Double.NaN, point[4]); - assertEquals(Double.NaN, point[5]); + assertEquals(Double.NaN, point[ChartView.HEART_RATE_SERIES + 1]); + assertEquals(Double.NaN, point[ChartView.CADENCE_SERIES + 1]); + assertEquals(Double.NaN, point[ChartView.POWER_SERIES + 1]); // Creates SensorData. - Sensor.SensorData.Builder powerData = Sensor.SensorData.newBuilder() - .setValue(20).setState(Sensor.SensorState.SENDING); - Sensor.SensorData.Builder cadenceData = Sensor.SensorData.newBuilder() - .setValue(20).setState(Sensor.SensorState.SENDING); Sensor.SensorData.Builder heartRateData = Sensor.SensorData.newBuilder() - .setValue(20).setState(Sensor.SensorState.SENDING); + .setValue(100).setState(Sensor.SensorState.SENDING); + Sensor.SensorData.Builder cadenceData = Sensor.SensorData.newBuilder() + .setValue(101).setState(Sensor.SensorState.SENDING); + Sensor.SensorData.Builder powerData = Sensor.SensorData.newBuilder() + .setValue(102).setState(Sensor.SensorState.SENDING); // Creates SensorDataSet. SensorDataSet sensorDataSet = myTracksLocation.getSensorDataSet(); sensorDataSet = sensorDataSet.toBuilder() - .setPower(powerData) - .setCadence(cadenceData) .setHeartRate(heartRateData) + .setCadence(cadenceData) + .setPower(powerData) .build(); myTracksLocation.setSensorData(sensorDataSet); // Test. point = fillDataPointTestHelper(myTracksLocation); - assertEquals(20.0, point[3]); - assertEquals(20.0, point[4]); - assertEquals(20.0, point[5]); + assertEquals(100.0, point[ChartView.HEART_RATE_SERIES + 1]); + assertEquals(101.0, point[ChartView.CADENCE_SERIES + 1]); + assertEquals(102.0, point[ChartView.POWER_SERIES + 1]); } /** @@ -230,7 +231,7 @@ public class ChartFragmentTest extends AndroidTestCase { * parameter. Then only one value INITIALLONGTITUDE in buffer. */ double[] point = fillDataPointTestHelper(myTracksLocation1); - assertEquals(TrackStubUtils.INITIAL_ALTITUDE, point[1]); + assertEquals(TrackStubUtils.INITIAL_ALTITUDE, point[ChartView.ELEVATION_SERIES + 1]); /* * Send another value to buffer, now there are two values, INITIALALTITUDE @@ -239,8 +240,8 @@ public class ChartFragmentTest extends AndroidTestCase { MyTracksLocation myTracksLocation2 = TrackStubUtils.createMyTracksLocation(); myTracksLocation2.setAltitude(TrackStubUtils.INITIAL_ALTITUDE * 2); point = fillDataPointTestHelper(myTracksLocation2); - assertEquals( - (TrackStubUtils.INITIAL_ALTITUDE + TrackStubUtils.INITIAL_ALTITUDE * 2) / 2.0, point[1]); + assertEquals((TrackStubUtils.INITIAL_ALTITUDE + TrackStubUtils.INITIAL_ALTITUDE * 2) / 2.0, + point[ChartView.ELEVATION_SERIES + 1]); } /** @@ -260,7 +261,7 @@ public class ChartFragmentTest extends AndroidTestCase { MyTracksLocation myTracksLocation1 = TrackStubUtils.createMyTracksLocation(); myTracksLocation1.setSpeed(129); double[] point = fillDataPointTestHelper(myTracksLocation1); - assertEquals(0.0, point[2]); + assertEquals(0.0, point[ChartView.SPEED_SERIES + 1]); /* * Tests the logic when both metricUnits and reportSpeed are true.This @@ -275,7 +276,7 @@ public class ChartFragmentTest extends AndroidTestCase { myTracksLocation2.setTime(myTracksLocation1.getTime() + 222); myTracksLocation2.setSpeed(130); point = fillDataPointTestHelper(myTracksLocation2); - assertEquals(130.0 * UnitConversions.MS_TO_KMH, point[2]); + assertEquals(130.0 * UnitConversions.MS_TO_KMH, point[ChartView.SPEED_SERIES + 1]); } /** @@ -287,7 +288,8 @@ public class ChartFragmentTest extends AndroidTestCase { MyTracksLocation myTracksLocation = TrackStubUtils.createMyTracksLocation(); myTracksLocation.setSpeed(132); double[] point = fillDataPointTestHelper(myTracksLocation); - assertEquals(132.0 * UnitConversions.MS_TO_KMH * UnitConversions.KM_TO_MI, point[2]); + assertEquals(132.0 * UnitConversions.MS_TO_KMH * UnitConversions.KM_TO_MI, + point[ChartView.SPEED_SERIES + 1]); } /** @@ -299,8 +301,8 @@ public class ChartFragmentTest extends AndroidTestCase { MyTracksLocation myTracksLocation = TrackStubUtils.createMyTracksLocation(); myTracksLocation.setSpeed(134); double[] point = fillDataPointTestHelper(myTracksLocation); - assertEquals(HOURS_PER_UNIT / (134.0 * UnitConversions.MS_TO_KMH), point[2]); - + assertEquals( + HOURS_PER_UNIT / (134.0 * UnitConversions.MS_TO_KMH), point[ChartView.PACE_SERIES + 1]); } /** @@ -313,7 +315,7 @@ public class ChartFragmentTest extends AndroidTestCase { MyTracksLocation myTracksLocation = TrackStubUtils.createMyTracksLocation(); myTracksLocation.setSpeed(0); double[] point = fillDataPointTestHelper(myTracksLocation); - assertEquals(0.0, point[2]); + assertEquals(0.0, point[ChartView.PACE_SERIES + 1]); } /** @@ -323,7 +325,7 @@ public class ChartFragmentTest extends AndroidTestCase { * @return data of this location */ private double[] fillDataPointTestHelper(Location location) { - double[] point = new double[6]; + double[] point = new double[ChartView.NUM_SERIES + 1]; chartFragment.fillDataPoint(location, point); return point; } diff --git a/MyTracksTest/src/com/google/android/apps/mytracks/io/sendtogoogle/UploadResultActivityTest.java b/MyTracksTest/src/com/google/android/apps/mytracks/io/sendtogoogle/UploadResultActivityTest.java index f56de3b42..6ec62b942 100644 --- a/MyTracksTest/src/com/google/android/apps/mytracks/io/sendtogoogle/UploadResultActivityTest.java +++ b/MyTracksTest/src/com/google/android/apps/mytracks/io/sendtogoogle/UploadResultActivityTest.java @@ -46,6 +46,7 @@ public class UploadResultActivityTest */ public void testAllSuccess() { initialActivity(true, true, true, true, true, true); + uploadResultActivity.showDialog(UploadResultActivity.DIALOG_RESULT_ID); Dialog dialog = uploadResultActivity.getDialog(); TextView textView = (TextView) dialog.findViewById(R.id.upload_result_success_footer); assertTrue(textView.isShown()); @@ -57,6 +58,7 @@ public class UploadResultActivityTest public void testAllFailed() { // Send all kinds but all failed. initialActivity(true, true, true, false, false, false); + uploadResultActivity.showDialog(UploadResultActivity.DIALOG_RESULT_ID); Dialog dialog = uploadResultActivity.getDialog(); TextView textView = (TextView) dialog.findViewById(R.id.upload_result_error_footer); assertTrue(textView.isShown()); @@ -72,6 +74,7 @@ public class UploadResultActivityTest */ public void testPartialSuccess() { initialActivity(true, false, true, true, false, false); + uploadResultActivity.showDialog(UploadResultActivity.DIALOG_RESULT_ID); Dialog dialog = uploadResultActivity.getDialog(); TextView textView = (TextView) dialog.findViewById(R.id.upload_result_error_footer); assertTrue(textView.isShown());