Clean up ChartValueSeries.

- include metric and imperial title id
- include both speed and pace series
This commit is contained in:
Jimmy Shih
2012-05-24 12:11:32 -07:00
parent ffbbead396
commit 0460425d43
10 changed files with 370 additions and 388 deletions
+4
View File
@@ -33,6 +33,10 @@ limitations under the License.
<color name="speed_border">#FF0066FF</color>
<color name="speed_fill">#40C8E0FF</color>
<!-- Blue and a transparent blue -->
<color name="pace_border">#FF0066FF</color>
<color name="pace_fill">#40C8E0FF</color>
<!-- Red and a transparent red -->
<color name="heartrate_border">#FFCC3300</color>
<color name="heartrate_fill">#40E88A70</color>
+24 -20
View File
@@ -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)
</string>
<string name="description_elevation_imperial"
translation_description="Elevation in imperial units, feet.">
Elevation (ft)
</string>
<string name="description_elevation_gain"
translation_description="Elevation gain description.
@@ -458,6 +462,10 @@ limitations under the License.
%2$d elevation in feet">
Elevation gain: %1$d m (%2$d ft)
</string>
<string name="description_elevation_metric"
translation_description="Elevation in metric units, meters.">
Elevation (m)
</string>
<string name="description_fastest_pace"
translation_description="Fastest pace description.
@@ -532,6 +540,14 @@ limitations under the License.
%1$s time. E.g., '1:08:59'">
Moving time: %1$s
</string>
<string name="description_pace_imperial"
translation_description="Pace in imperial units, hours per mile.">
Pace (hr/mi)
</string>
<string name="description_pace_metric"
translation_description="Pace in metric units, hours per kilometer.">
Pace (hr/km)
</string>
<string name="description_recorded_time"
translation_description="Recorded time description.
@@ -554,6 +570,14 @@ limitations under the License.
translation_description="Label for power with its unit watt (W).">
Power (W)
</string>
<string name="description_speed_imperial"
translation_description="Speed in imperial units, miles per hour.">
Speed (mi/hr)
</string>
<string name="description_speed_metric"
translation_description="Speed in metric units, kilometers per hour.">
Speed (km/hr)
</string>
<string name="description_track_point"
translation_description="Label for a track point">
Point
@@ -1898,26 +1922,6 @@ limitations under the License.
of inclination to the horizontal, also known as slope or incline.">
Grade
</string>
<string name="settings_stats_rate_pace_imperial"
translation_description="In the 'Stats' settings, the option to display pace, with example in
imperial units.">
Pace (hr/mi)
</string>
<string name="settings_stats_rate_pace_metric"
translation_description="In the 'Stats' settings, the option to display pace, with example in
metric units.">
Pace (hr/km)
</string>
<string name="settings_stats_rate_speed_imperial"
translation_description="In the 'Stats' settings, the option to display speed, with example in
imperial units.">
Speed (mi/hr)
</string>
<string name="settings_stats_rate_speed_metric"
translation_description="In the 'Stats' settings, the option to display speed, with example in
metric units.">
Speed (km/hr)
</string>
<string name="settings_stats_rate_title"
translation_description="In the 'Stats' settings, the option to change the preferred rate,
either speed (mi/hr) or pace (hr/mi).">
@@ -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);
}
}
@@ -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);
}
}
@@ -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]. <br>
* Given a location, fill in a data point, an array of double[]. <br>
* data[0] = time/distance <br>
* data[1] = elevation <br>
* data[2] = speed <br>
* data[3] = power <br>
* data[4] = cadence <br>
* data[5] = heart rate <br>
* data[3] = pace <br>
* data[4] = heart rate <br>
* data[5] = cadence <br>
* data[6] = power <br>
*
* @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;
}
@@ -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;
@@ -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));
}
}
@@ -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());
}
}
@@ -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;
}
@@ -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());