forked from upstream-mirrors/OpenTracks
Merge heads
This commit is contained in:
@@ -683,12 +683,17 @@ limitations under the License.
|
|||||||
translation_description="On application start, message when there is no GPS on the device.">
|
translation_description="On application start, message when there is no GPS on the device.">
|
||||||
No GPS is found on the device.
|
No GPS is found on the device.
|
||||||
</string>
|
</string>
|
||||||
<string name="gps_wait_for_signal"
|
<string name="gps_wait_for_better_signal"
|
||||||
translation_description="In the main view, the message on the top indicating My Tracks is
|
translation_description="In the main view, the message on the top indicating My Tracks is
|
||||||
waiting for a better GPS signal.">
|
waiting for a better GPS signal.">
|
||||||
Waiting for a better GPS signal. Make sure the sky is visible.
|
Waiting for a better GPS signal. Make sure the sky is visible.
|
||||||
</string>
|
</string>
|
||||||
|
<string name="gps_wait_for_signal"
|
||||||
|
translation_description="In the main view, the message on the top indicating My Tracks is
|
||||||
|
waiting for a better GPS signal.">
|
||||||
|
Waiting for GPS signal. Make sure the sky is visible.
|
||||||
|
</string>
|
||||||
|
|
||||||
<!-- Help page -->
|
<!-- Help page -->
|
||||||
<string name="help_about"
|
<string name="help_about"
|
||||||
translation_description="In the 'Help' page, the button on the bottom to open the 'About My
|
translation_description="In the 'Help' page, the button on the bottom to open the 'About My
|
||||||
|
|||||||
@@ -75,6 +75,7 @@ public class TrackDataHub implements DataSourceListener {
|
|||||||
private boolean metricUnits;
|
private boolean metricUnits;
|
||||||
private boolean reportSpeed;
|
private boolean reportSpeed;
|
||||||
private int minRequiredAccuracy;
|
private int minRequiredAccuracy;
|
||||||
|
private int minRecordingDistance;
|
||||||
|
|
||||||
// Heading values
|
// Heading values
|
||||||
private float lastHeading = 0;
|
private float lastHeading = 0;
|
||||||
@@ -347,6 +348,20 @@ public class TrackDataHub implements DataSourceListener {
|
|||||||
minRequiredAccuracy = PreferencesUtils.getInt(context, R.string.min_required_accuracy_key,
|
minRequiredAccuracy = PreferencesUtils.getInt(context, R.string.min_required_accuracy_key,
|
||||||
PreferencesUtils.MIN_REQUIRED_ACCURACY_DEFAULT);
|
PreferencesUtils.MIN_REQUIRED_ACCURACY_DEFAULT);
|
||||||
}
|
}
|
||||||
|
if (key == null
|
||||||
|
|| key.equals(PreferencesUtils.getKey(context, R.string.min_recording_distance_key))) {
|
||||||
|
minRecordingDistance = PreferencesUtils.getInt(
|
||||||
|
context, R.string.min_recording_distance_key,
|
||||||
|
PreferencesUtils.MIN_RECORDING_DISTANCE_DEFAULT);
|
||||||
|
if (key != null) {
|
||||||
|
for (TrackDataListener trackDataListener :
|
||||||
|
trackDataManager.getListeners(TrackDataType.PREFERENCE)) {
|
||||||
|
if (trackDataListener.onMinRecordingDistanceChanged(minRecordingDistance)) {
|
||||||
|
loadDataForListener(trackDataListener);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@@ -422,6 +437,7 @@ public class TrackDataHub implements DataSourceListener {
|
|||||||
trackDataManager.getListeners(TrackDataType.PREFERENCE)) {
|
trackDataManager.getListeners(TrackDataType.PREFERENCE)) {
|
||||||
trackDataListener.onMetricUnitsChanged(metricUnits);
|
trackDataListener.onMetricUnitsChanged(metricUnits);
|
||||||
trackDataListener.onReportSpeedChanged(reportSpeed);
|
trackDataListener.onReportSpeedChanged(reportSpeed);
|
||||||
|
trackDataListener.onMinRecordingDistanceChanged(minRecordingDistance);
|
||||||
}
|
}
|
||||||
|
|
||||||
notifySelectedTrackChanged(trackDataManager.getListeners(TrackDataType.SELECTED_TRACK));
|
notifySelectedTrackChanged(trackDataManager.getListeners(TrackDataType.SELECTED_TRACK));
|
||||||
@@ -457,6 +473,7 @@ public class TrackDataHub implements DataSourceListener {
|
|||||||
if (trackDataTypes.contains(TrackDataType.PREFERENCE)) {
|
if (trackDataTypes.contains(TrackDataType.PREFERENCE)) {
|
||||||
trackDataListener.onMetricUnitsChanged(metricUnits);
|
trackDataListener.onMetricUnitsChanged(metricUnits);
|
||||||
trackDataListener.onReportSpeedChanged(reportSpeed);
|
trackDataListener.onReportSpeedChanged(reportSpeed);
|
||||||
|
trackDataListener.onMinRecordingDistanceChanged(minRecordingDistance);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (trackDataTypes.contains(TrackDataType.SELECTED_TRACK)) {
|
if (trackDataTypes.contains(TrackDataType.SELECTED_TRACK)) {
|
||||||
@@ -623,7 +640,7 @@ public class TrackDataHub implements DataSourceListener {
|
|||||||
if (!LocationUtils.isValidLocation(location)) {
|
if (!LocationUtils.isValidLocation(location)) {
|
||||||
// TODO: also include the last valid point before a split
|
// TODO: also include the last valid point before a split
|
||||||
for (TrackDataListener trackDataListener : sampledInListeners) {
|
for (TrackDataListener trackDataListener : sampledInListeners) {
|
||||||
trackDataListener.onSegmentSplit();
|
trackDataListener.onSegmentSplit(location);
|
||||||
includeNextPoint = true;
|
includeNextPoint = true;
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
|
|||||||
@@ -96,12 +96,13 @@ public interface TrackDataListener {
|
|||||||
/**
|
/**
|
||||||
* Called when an invalid track point representing a segment split is read.
|
* Called when an invalid track point representing a segment split is read.
|
||||||
*/
|
*/
|
||||||
public void onSegmentSplit();
|
public void onSegmentSplit(Location location);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Called when finish sending new track points. This gets called after every
|
* Called when finish sending new track points. This gets called after every
|
||||||
* batch of calls to {@link #onSampledInTrackPoint(Location)},
|
* batch of calls to {@link #onSampledInTrackPoint(Location)},
|
||||||
* {@link #onSampledOutTrackPoint(Location)} and {@link #onSegmentSplit()}.
|
* {@link #onSampledOutTrackPoint(Location)} and
|
||||||
|
* {@link #onSegmentSplit(Location)}.
|
||||||
*/
|
*/
|
||||||
public void onNewTrackPointsDone();
|
public void onNewTrackPointsDone();
|
||||||
|
|
||||||
@@ -139,4 +140,12 @@ public interface TrackDataListener {
|
|||||||
* @return true to reload all the data, false otherwise.
|
* @return true to reload all the data, false otherwise.
|
||||||
*/
|
*/
|
||||||
public boolean onReportSpeedChanged(boolean reportSpeed);
|
public boolean onReportSpeedChanged(boolean reportSpeed);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Called when the min recording distance preference value is changed.
|
||||||
|
*
|
||||||
|
* @param minRecordingDistance the new value
|
||||||
|
* @return true to reload all the data, false otherwise.
|
||||||
|
*/
|
||||||
|
public boolean onMinRecordingDistanceChanged(int minRecordingDistance);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -17,7 +17,6 @@
|
|||||||
package com.google.android.apps.mytracks.fragments;
|
package com.google.android.apps.mytracks.fragments;
|
||||||
|
|
||||||
import com.google.android.apps.mytracks.ChartView;
|
import com.google.android.apps.mytracks.ChartView;
|
||||||
import com.google.android.apps.mytracks.Constants;
|
|
||||||
import com.google.android.apps.mytracks.TrackDetailActivity;
|
import com.google.android.apps.mytracks.TrackDetailActivity;
|
||||||
import com.google.android.apps.mytracks.content.MyTracksLocation;
|
import com.google.android.apps.mytracks.content.MyTracksLocation;
|
||||||
import com.google.android.apps.mytracks.content.Sensor;
|
import com.google.android.apps.mytracks.content.Sensor;
|
||||||
@@ -27,7 +26,7 @@ import com.google.android.apps.mytracks.content.TrackDataHub;
|
|||||||
import com.google.android.apps.mytracks.content.TrackDataListener;
|
import com.google.android.apps.mytracks.content.TrackDataListener;
|
||||||
import com.google.android.apps.mytracks.content.TrackDataType;
|
import com.google.android.apps.mytracks.content.TrackDataType;
|
||||||
import com.google.android.apps.mytracks.content.Waypoint;
|
import com.google.android.apps.mytracks.content.Waypoint;
|
||||||
import com.google.android.apps.mytracks.stats.DoubleBuffer;
|
import com.google.android.apps.mytracks.stats.TripStatistics;
|
||||||
import com.google.android.apps.mytracks.stats.TripStatisticsBuilder;
|
import com.google.android.apps.mytracks.stats.TripStatisticsBuilder;
|
||||||
import com.google.android.apps.mytracks.util.LocationUtils;
|
import com.google.android.apps.mytracks.util.LocationUtils;
|
||||||
import com.google.android.apps.mytracks.util.PreferencesUtils;
|
import com.google.android.apps.mytracks.util.PreferencesUtils;
|
||||||
@@ -56,28 +55,22 @@ import java.util.EnumSet;
|
|||||||
public class ChartFragment extends Fragment implements TrackDataListener {
|
public class ChartFragment extends Fragment implements TrackDataListener {
|
||||||
|
|
||||||
public static final String CHART_FRAGMENT_TAG = "chartFragment";
|
public static final String CHART_FRAGMENT_TAG = "chartFragment";
|
||||||
|
|
||||||
// Android reports 128 when the speed is invalid
|
|
||||||
private static final int INVALID_SPEED = 128;
|
|
||||||
private final DoubleBuffer elevationBuffer = new DoubleBuffer(
|
|
||||||
Constants.ELEVATION_SMOOTHING_FACTOR);
|
|
||||||
private final DoubleBuffer speedBuffer = new DoubleBuffer(Constants.SPEED_SMOOTHING_FACTOR);
|
|
||||||
private final ArrayList<double[]> pendingPoints = new ArrayList<double[]>();
|
private final ArrayList<double[]> pendingPoints = new ArrayList<double[]>();
|
||||||
|
|
||||||
private TrackDataHub trackDataHub;
|
private TrackDataHub trackDataHub;
|
||||||
|
|
||||||
// Stats gathered from the received data
|
// Stats gathered from the received data
|
||||||
private double totalDistance = 0.0;
|
private TripStatisticsBuilder tripStatisticsBuilder;
|
||||||
private long startTime = -1L;
|
private long startTime;
|
||||||
private Location lastLocation = null;
|
|
||||||
private double trackMaxSpeed = 0.0;
|
private boolean metricUnits = PreferencesUtils.METRIC_UNITS_DEFAULT;
|
||||||
|
private boolean reportSpeed = PreferencesUtils.REPORT_SPEED_DEFAULT;
|
||||||
|
private int minRecordingDistance = PreferencesUtils.MIN_RECORDING_DISTANCE_DEFAULT;
|
||||||
|
|
||||||
// Modes of operation
|
// Modes of operation
|
||||||
private boolean metricUnits = true;
|
|
||||||
private boolean reportSpeed = true;
|
|
||||||
|
|
||||||
private boolean chartByDistance = true;
|
private boolean chartByDistance = true;
|
||||||
private boolean[] chartShow = new boolean[] {true, true, true, true, true, true};
|
private boolean[] chartShow = new boolean[] { true, true, true, true, true, true };
|
||||||
|
|
||||||
// UI elements
|
// UI elements
|
||||||
private ChartView chartView;
|
private ChartView chartView;
|
||||||
@@ -88,7 +81,7 @@ public class ChartFragment extends Fragment implements TrackDataListener {
|
|||||||
* appropriate and redraw.
|
* appropriate and redraw.
|
||||||
*/
|
*/
|
||||||
private final Runnable updateChart = new Runnable() {
|
private final Runnable updateChart = new Runnable() {
|
||||||
@Override
|
@Override
|
||||||
public void run() {
|
public void run() {
|
||||||
if (trackDataHub == null) {
|
if (trackDataHub == null) {
|
||||||
return;
|
return;
|
||||||
@@ -118,13 +111,13 @@ public class ChartFragment extends Fragment implements TrackDataListener {
|
|||||||
View view = inflater.inflate(R.layout.chart, container, false);
|
View view = inflater.inflate(R.layout.chart, container, false);
|
||||||
zoomControls = (ZoomControls) view.findViewById(R.id.chart_zoom_controls);
|
zoomControls = (ZoomControls) view.findViewById(R.id.chart_zoom_controls);
|
||||||
zoomControls.setOnZoomInClickListener(new View.OnClickListener() {
|
zoomControls.setOnZoomInClickListener(new View.OnClickListener() {
|
||||||
@Override
|
@Override
|
||||||
public void onClick(View v) {
|
public void onClick(View v) {
|
||||||
zoomIn();
|
zoomIn();
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
zoomControls.setOnZoomOutClickListener(new View.OnClickListener() {
|
zoomControls.setOnZoomOutClickListener(new View.OnClickListener() {
|
||||||
@Override
|
@Override
|
||||||
public void onClick(View v) {
|
public void onClick(View v) {
|
||||||
zoomOut();
|
zoomOut();
|
||||||
}
|
}
|
||||||
@@ -141,7 +134,7 @@ public class ChartFragment extends Fragment implements TrackDataListener {
|
|||||||
LayoutParams.FILL_PARENT, LayoutParams.FILL_PARENT);
|
LayoutParams.FILL_PARENT, LayoutParams.FILL_PARENT);
|
||||||
layout.addView(chartView, layoutParams);
|
layout.addView(chartView, layoutParams);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onResume() {
|
public void onResume() {
|
||||||
super.onResume();
|
super.onResume();
|
||||||
@@ -186,26 +179,20 @@ public class ChartFragment extends Fragment implements TrackDataListener {
|
|||||||
@Override
|
@Override
|
||||||
public void onTrackUpdated(Track track) {
|
public void onTrackUpdated(Track track) {
|
||||||
if (track == null || track.getTripStatistics() == null) {
|
if (track == null || track.getTripStatistics() == null) {
|
||||||
trackMaxSpeed = 0.0;
|
startTime = -1L;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
trackMaxSpeed = track.getTripStatistics().getMaxSpeed();
|
startTime = track.getTripStatistics().getStartTime();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void clearTrackPoints() {
|
public void clearTrackPoints() {
|
||||||
totalDistance = 0.0;
|
tripStatisticsBuilder = startTime != -1L ? new TripStatisticsBuilder(startTime) : null;
|
||||||
startTime = -1L;
|
|
||||||
lastLocation = null;
|
|
||||||
|
|
||||||
elevationBuffer.reset();
|
|
||||||
speedBuffer.reset();
|
|
||||||
pendingPoints.clear();
|
pendingPoints.clear();
|
||||||
|
|
||||||
chartView.reset();
|
chartView.reset();
|
||||||
|
|
||||||
getActivity().runOnUiThread(new Runnable() {
|
getActivity().runOnUiThread(new Runnable() {
|
||||||
@Override
|
@Override
|
||||||
public void run() {
|
public void run() {
|
||||||
chartView.resetScroll();
|
chartView.resetScroll();
|
||||||
}
|
}
|
||||||
@@ -214,24 +201,19 @@ public class ChartFragment extends Fragment implements TrackDataListener {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onSampledInTrackPoint(Location location) {
|
public void onSampledInTrackPoint(Location location) {
|
||||||
if (LocationUtils.isValidLocation(location)) {
|
double[] data = new double[ChartView.NUM_SERIES + 1];
|
||||||
double[] data = new double[ChartView.NUM_SERIES + 1];
|
fillDataPoint(location, data);
|
||||||
fillDataPoint(location, data);
|
pendingPoints.add(data);
|
||||||
pendingPoints.add(data);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onSampledOutTrackPoint(Location location) {
|
public void onSampledOutTrackPoint(Location location) {
|
||||||
if (LocationUtils.isValidLocation(location)) {
|
fillDataPoint(location, null);
|
||||||
// Still account for the point in the smoothing buffers.
|
|
||||||
fillDataPoint(location, null);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onSegmentSplit() {
|
public void onSegmentSplit(Location location) {
|
||||||
// Do nothing.
|
fillDataPoint(location, null);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -266,7 +248,7 @@ public class ChartFragment extends Fragment implements TrackDataListener {
|
|||||||
metricUnits = metric;
|
metricUnits = metric;
|
||||||
chartView.setMetricUnits(metricUnits);
|
chartView.setMetricUnits(metricUnits);
|
||||||
getActivity().runOnUiThread(new Runnable() {
|
getActivity().runOnUiThread(new Runnable() {
|
||||||
@Override
|
@Override
|
||||||
public void run() {
|
public void run() {
|
||||||
chartView.requestLayout();
|
chartView.requestLayout();
|
||||||
}
|
}
|
||||||
@@ -286,7 +268,7 @@ public class ChartFragment extends Fragment implements TrackDataListener {
|
|||||||
setSeriesEnabled(ChartView.SPEED_SERIES, chartShowSpeed && reportSpeed);
|
setSeriesEnabled(ChartView.SPEED_SERIES, chartShowSpeed && reportSpeed);
|
||||||
setSeriesEnabled(ChartView.PACE_SERIES, chartShowSpeed && !reportSpeed);
|
setSeriesEnabled(ChartView.PACE_SERIES, chartShowSpeed && !reportSpeed);
|
||||||
getActivity().runOnUiThread(new Runnable() {
|
getActivity().runOnUiThread(new Runnable() {
|
||||||
@Override
|
@Override
|
||||||
public void run() {
|
public void run() {
|
||||||
chartView.requestLayout();
|
chartView.requestLayout();
|
||||||
}
|
}
|
||||||
@@ -294,6 +276,15 @@ public class ChartFragment extends Fragment implements TrackDataListener {
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean onMinRecordingDistanceChanged(int value) {
|
||||||
|
if (minRecordingDistance == value) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
minRecordingDistance = value;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Checks the chart settings.
|
* Checks the chart settings.
|
||||||
*/
|
*/
|
||||||
@@ -310,7 +301,7 @@ public class ChartFragment extends Fragment implements TrackDataListener {
|
|||||||
R.string.chart_show_elevation_key, PreferencesUtils.CHART_SHOW_ELEVATION_DEFAULT))) {
|
R.string.chart_show_elevation_key, PreferencesUtils.CHART_SHOW_ELEVATION_DEFAULT))) {
|
||||||
needUpdate = true;
|
needUpdate = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
boolean chartShowSpeed = PreferencesUtils.getBoolean(
|
boolean chartShowSpeed = PreferencesUtils.getBoolean(
|
||||||
getActivity(), R.string.chart_show_speed_key, PreferencesUtils.CHART_SHOW_SPEED_DEFAULT);
|
getActivity(), R.string.chart_show_speed_key, PreferencesUtils.CHART_SHOW_SPEED_DEFAULT);
|
||||||
if (setSeriesEnabled(ChartView.SPEED_SERIES, chartShowSpeed && reportSpeed)) {
|
if (setSeriesEnabled(ChartView.SPEED_SERIES, chartShowSpeed && reportSpeed)) {
|
||||||
@@ -354,17 +345,14 @@ public class ChartFragment extends Fragment implements TrackDataListener {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Resumes the trackDataHub. Needs to be synchronized because trackDataHub can be
|
* Resumes the trackDataHub. Needs to be synchronized because trackDataHub can
|
||||||
* accessed by multiple threads.
|
* be accessed by multiple threads.
|
||||||
*/
|
*/
|
||||||
private synchronized void resumeTrackDataHub() {
|
private synchronized void resumeTrackDataHub() {
|
||||||
trackDataHub = ((TrackDetailActivity) getActivity()).getTrackDataHub();
|
trackDataHub = ((TrackDetailActivity) getActivity()).getTrackDataHub();
|
||||||
trackDataHub.registerTrackDataListener(this, EnumSet.of(
|
trackDataHub.registerTrackDataListener(this, EnumSet.of(TrackDataType.SELECTED_TRACK,
|
||||||
TrackDataType.SELECTED_TRACK,
|
TrackDataType.TRACKS_TABLE, TrackDataType.WAYPOINTS_TABLE,
|
||||||
TrackDataType.TRACKS_TABLE,
|
TrackDataType.SAMPLED_IN_TRACK_POINTS_TABLE, TrackDataType.SAMPLED_OUT_TRACK_POINTS_TABLE,
|
||||||
TrackDataType.WAYPOINTS_TABLE,
|
|
||||||
TrackDataType.SAMPLED_IN_TRACK_POINTS_TABLE,
|
|
||||||
TrackDataType.SAMPLED_OUT_TRACK_POINTS_TABLE,
|
|
||||||
TrackDataType.PREFERENCE));
|
TrackDataType.PREFERENCE));
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -386,15 +374,15 @@ public class ChartFragment extends Fragment implements TrackDataListener {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Reloads the trackDataHub. Needs to be synchronized because trackDataHub can be
|
* Reloads the trackDataHub. Needs to be synchronized because trackDataHub can
|
||||||
* accessed by multiple threads.
|
* be accessed by multiple threads.
|
||||||
*/
|
*/
|
||||||
private synchronized void reloadTrackDataHub() {
|
private synchronized void reloadTrackDataHub() {
|
||||||
if (trackDataHub != null) {
|
if (trackDataHub != null) {
|
||||||
trackDataHub.reloadDataForListener(this);
|
trackDataHub.reloadDataForListener(this);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* To zoom in.
|
* To zoom in.
|
||||||
*/
|
*/
|
||||||
@@ -412,7 +400,7 @@ public class ChartFragment extends Fragment implements TrackDataListener {
|
|||||||
zoomControls.setIsZoomInEnabled(chartView.canZoomIn());
|
zoomControls.setIsZoomInEnabled(chartView.canZoomIn());
|
||||||
zoomControls.setIsZoomOutEnabled(chartView.canZoomOut());
|
zoomControls.setIsZoomOutEnabled(chartView.canZoomOut());
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Given a location, fill in a data point, an array of double[]. <br>
|
* Given a location, fill in a data point, an array of double[]. <br>
|
||||||
* data[0] = time/distance <br>
|
* data[0] = time/distance <br>
|
||||||
@@ -435,44 +423,31 @@ public class ChartFragment extends Fragment implements TrackDataListener {
|
|||||||
double heartRate = Double.NaN;
|
double heartRate = Double.NaN;
|
||||||
double cadence = Double.NaN;
|
double cadence = Double.NaN;
|
||||||
double power = Double.NaN;
|
double power = Double.NaN;
|
||||||
|
|
||||||
// TODO: Use TripStatisticsBuilder
|
|
||||||
if (chartByDistance) {
|
|
||||||
if (lastLocation != null) {
|
|
||||||
double distance = lastLocation.distanceTo(location) * UnitConversions.M_TO_KM;
|
|
||||||
if (metricUnits) {
|
|
||||||
totalDistance += distance;
|
|
||||||
} else {
|
|
||||||
totalDistance += distance * UnitConversions.KM_TO_MI;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
timeOrDistance = totalDistance;
|
|
||||||
} else {
|
|
||||||
if (startTime == -1L) {
|
|
||||||
startTime = location.getTime();
|
|
||||||
}
|
|
||||||
timeOrDistance = location.getTime() - startTime;
|
|
||||||
}
|
|
||||||
|
|
||||||
elevationBuffer.setNext(metricUnits ? location.getAltitude() : location.getAltitude()
|
|
||||||
* UnitConversions.M_TO_FT);
|
|
||||||
elevation = elevationBuffer.getAverage();
|
|
||||||
|
|
||||||
if (lastLocation == null) {
|
|
||||||
if (Math.abs(location.getSpeed() - INVALID_SPEED) > 1) {
|
|
||||||
speedBuffer.setNext(location.getSpeed());
|
|
||||||
}
|
|
||||||
} else if (TripStatisticsBuilder.isValidSpeed(location.getTime(), location.getSpeed(),
|
|
||||||
lastLocation.getTime(), lastLocation.getSpeed(), speedBuffer)
|
|
||||||
&& (location.getSpeed() <= trackMaxSpeed)) {
|
|
||||||
speedBuffer.setNext(location.getSpeed());
|
|
||||||
}
|
|
||||||
speed = speedBuffer.getAverage() * UnitConversions.MS_TO_KMH;
|
|
||||||
if (!metricUnits) {
|
|
||||||
speed *= UnitConversions.KM_TO_MI;
|
|
||||||
}
|
|
||||||
pace = speed == 0 ? 0.0 : 60.0 / speed;
|
|
||||||
|
|
||||||
|
if (tripStatisticsBuilder != null) {
|
||||||
|
tripStatisticsBuilder.addLocation(location, minRecordingDistance);
|
||||||
|
TripStatistics tripStatistics = tripStatisticsBuilder.getTripStatistics();
|
||||||
|
if (chartByDistance) {
|
||||||
|
double distance = tripStatistics.getTotalDistance() * UnitConversions.M_TO_KM;
|
||||||
|
if (!metricUnits) {
|
||||||
|
distance *= UnitConversions.KM_TO_MI;
|
||||||
|
}
|
||||||
|
timeOrDistance = distance;
|
||||||
|
} else {
|
||||||
|
timeOrDistance = tripStatistics.getTotalTime();
|
||||||
|
}
|
||||||
|
|
||||||
|
elevation = tripStatisticsBuilder.getSmoothedElevation();
|
||||||
|
if (!metricUnits) {
|
||||||
|
elevation *= UnitConversions.M_TO_FT;
|
||||||
|
}
|
||||||
|
|
||||||
|
speed = tripStatisticsBuilder.getSmoothedSpeed() * UnitConversions.MS_TO_KMH;
|
||||||
|
if (!metricUnits) {
|
||||||
|
speed *= UnitConversions.KM_TO_MI;
|
||||||
|
}
|
||||||
|
pace = speed == 0 ? 0.0 : 60.0 / speed;
|
||||||
|
}
|
||||||
if (location instanceof MyTracksLocation
|
if (location instanceof MyTracksLocation
|
||||||
&& ((MyTracksLocation) location).getSensorDataSet() != null) {
|
&& ((MyTracksLocation) location).getSensorDataSet() != null) {
|
||||||
SensorDataSet sensorDataSet = ((MyTracksLocation) location).getSensorDataSet();
|
SensorDataSet sensorDataSet = ((MyTracksLocation) location).getSensorDataSet();
|
||||||
@@ -502,7 +477,6 @@ public class ChartFragment extends Fragment implements TrackDataListener {
|
|||||||
data[5] = cadence;
|
data[5] = cadence;
|
||||||
data[6] = power;
|
data[6] = power;
|
||||||
}
|
}
|
||||||
lastLocation = location;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@VisibleForTesting
|
@VisibleForTesting
|
||||||
@@ -510,25 +484,26 @@ public class ChartFragment extends Fragment implements TrackDataListener {
|
|||||||
return chartView;
|
return chartView;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@VisibleForTesting
|
||||||
|
void setTripStatisticsBuilder(long time) {
|
||||||
|
tripStatisticsBuilder = new TripStatisticsBuilder(time);
|
||||||
|
}
|
||||||
|
|
||||||
@VisibleForTesting
|
@VisibleForTesting
|
||||||
void setChartView(ChartView view) {
|
void setChartView(ChartView view) {
|
||||||
chartView = view;
|
chartView = view;
|
||||||
}
|
}
|
||||||
|
|
||||||
@VisibleForTesting
|
|
||||||
void setTrackMaxSpeed(double value) {
|
|
||||||
trackMaxSpeed = value;
|
|
||||||
}
|
|
||||||
|
|
||||||
@VisibleForTesting
|
@VisibleForTesting
|
||||||
void setMetricUnits(boolean value) {
|
void setMetricUnits(boolean value) {
|
||||||
metricUnits = value;
|
metricUnits = value;
|
||||||
}
|
}
|
||||||
|
|
||||||
@VisibleForTesting
|
@VisibleForTesting
|
||||||
void setReportSpeed(boolean value) {
|
void setReportSpeed(boolean value) {
|
||||||
reportSpeed = value;
|
reportSpeed = value;
|
||||||
}
|
}
|
||||||
|
|
||||||
@VisibleForTesting
|
@VisibleForTesting
|
||||||
void setChartByDistance(boolean value) {
|
void setChartByDistance(boolean value) {
|
||||||
chartByDistance = value;
|
chartByDistance = value;
|
||||||
|
|||||||
@@ -57,7 +57,7 @@ import java.util.List;
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* A fragment to display map to the user.
|
* A fragment to display map to the user.
|
||||||
*
|
*
|
||||||
* @author Leif Hendrik Wilden
|
* @author Leif Hendrik Wilden
|
||||||
* @author Rodrigo Damazio
|
* @author Rodrigo Damazio
|
||||||
*/
|
*/
|
||||||
@@ -65,7 +65,7 @@ public class MapFragment extends Fragment
|
|||||||
implements View.OnTouchListener, View.OnClickListener, TrackDataListener {
|
implements View.OnTouchListener, View.OnClickListener, TrackDataListener {
|
||||||
|
|
||||||
public static final String MAP_FRAGMENT_TAG = "mapFragment";
|
public static final String MAP_FRAGMENT_TAG = "mapFragment";
|
||||||
|
|
||||||
private static final String KEY_CURRENT_LOCATION = "currentLocation";
|
private static final String KEY_CURRENT_LOCATION = "currentLocation";
|
||||||
private static final String KEY_KEEP_MY_LOCATION_VISIBLE = "keepMyLocationVisible";
|
private static final String KEY_KEEP_MY_LOCATION_VISIBLE = "keepMyLocationVisible";
|
||||||
|
|
||||||
@@ -108,19 +108,19 @@ public class MapFragment extends Fragment
|
|||||||
mapViewContainer = ((TrackDetailActivity) getActivity()).getMapViewContainer();
|
mapViewContainer = ((TrackDetailActivity) getActivity()).getMapViewContainer();
|
||||||
mapView = (MapView) mapViewContainer.findViewById(R.id.map_view);
|
mapView = (MapView) mapViewContainer.findViewById(R.id.map_view);
|
||||||
mapOverlay = new MapOverlay(getActivity());
|
mapOverlay = new MapOverlay(getActivity());
|
||||||
|
|
||||||
List<Overlay> overlays = mapView.getOverlays();
|
List<Overlay> overlays = mapView.getOverlays();
|
||||||
overlays.clear();
|
overlays.clear();
|
||||||
overlays.add(mapOverlay);
|
overlays.add(mapOverlay);
|
||||||
|
|
||||||
mapView.requestFocus();
|
mapView.requestFocus();
|
||||||
mapView.setOnTouchListener(this);
|
mapView.setOnTouchListener(this);
|
||||||
mapView.setBuiltInZoomControls(true);
|
mapView.setBuiltInZoomControls(true);
|
||||||
myLocationImageButton = (ImageButton) mapViewContainer.findViewById(R.id.map_my_location);
|
myLocationImageButton = (ImageButton) mapViewContainer.findViewById(R.id.map_my_location);
|
||||||
myLocationImageButton.setOnClickListener(new View.OnClickListener() {
|
myLocationImageButton.setOnClickListener(new View.OnClickListener() {
|
||||||
@Override
|
@Override
|
||||||
public void onClick(View v) {
|
public void onClick(View v) {
|
||||||
showMyLocation();
|
showMyLocation();
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
messageTextView = (TextView) mapViewContainer.findViewById(R.id.map_message);
|
messageTextView = (TextView) mapViewContainer.findViewById(R.id.map_message);
|
||||||
@@ -227,7 +227,7 @@ public class MapFragment extends Fragment
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Shows the marker.
|
* Shows the marker.
|
||||||
*
|
*
|
||||||
* @param trackId the track id
|
* @param trackId the track id
|
||||||
* @param id the marker id
|
* @param id the marker id
|
||||||
*/
|
*/
|
||||||
@@ -277,29 +277,36 @@ public class MapFragment extends Fragment
|
|||||||
public void onLocationStateChanged(LocationState state) {
|
public void onLocationStateChanged(LocationState state) {
|
||||||
final String message;
|
final String message;
|
||||||
final boolean isGpsDisabled;
|
final boolean isGpsDisabled;
|
||||||
switch (state) {
|
if (!isSelectedTrackRecording()) {
|
||||||
case DISABLED:
|
message = null;
|
||||||
String setting = getString(
|
isGpsDisabled = false;
|
||||||
GoogleLocationUtils.isAvailable(getActivity()) ? R.string.gps_google_location_settings
|
} else {
|
||||||
: R.string.gps_location_access);
|
switch (state) {
|
||||||
message = getString(R.string.gps_disabled, setting);
|
case DISABLED:
|
||||||
isGpsDisabled = true;
|
String setting = getString(
|
||||||
break;
|
GoogleLocationUtils.isAvailable(getActivity()) ? R.string.gps_google_location_settings
|
||||||
case NO_FIX:
|
: R.string.gps_location_access);
|
||||||
case BAD_FIX:
|
message = getString(R.string.gps_disabled, setting);
|
||||||
message = getString(R.string.gps_wait_for_signal);
|
isGpsDisabled = true;
|
||||||
isGpsDisabled = false;
|
break;
|
||||||
break;
|
case NO_FIX:
|
||||||
case GOOD_FIX:
|
message = getString(R.string.gps_wait_for_signal);
|
||||||
message = null;
|
isGpsDisabled = false;
|
||||||
isGpsDisabled = false;
|
break;
|
||||||
break;
|
case BAD_FIX:
|
||||||
default:
|
message = getString(R.string.gps_wait_for_better_signal);
|
||||||
throw new IllegalArgumentException("Unexpected state: " + state);
|
isGpsDisabled = false;
|
||||||
|
break;
|
||||||
|
case GOOD_FIX:
|
||||||
|
message = null;
|
||||||
|
isGpsDisabled = false;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
throw new IllegalArgumentException("Unexpected state: " + state);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
getActivity().runOnUiThread(new Runnable() {
|
getActivity().runOnUiThread(new Runnable() {
|
||||||
@Override
|
@Override
|
||||||
public void run() {
|
public void run() {
|
||||||
if (message != null) {
|
if (message != null) {
|
||||||
messageTextView.setText(message);
|
messageTextView.setText(message);
|
||||||
@@ -336,12 +343,12 @@ public class MapFragment extends Fragment
|
|||||||
@Override
|
@Override
|
||||||
public void onSelectedTrackChanged(final Track track) {
|
public void onSelectedTrackChanged(final Track track) {
|
||||||
getActivity().runOnUiThread(new Runnable() {
|
getActivity().runOnUiThread(new Runnable() {
|
||||||
@Override
|
@Override
|
||||||
public void run() {
|
public void run() {
|
||||||
boolean hasTrack = track != null;
|
boolean hasTrack = track != null;
|
||||||
mapOverlay.setTrackDrawingEnabled(hasTrack);
|
mapOverlay.setTrackDrawingEnabled(hasTrack);
|
||||||
|
|
||||||
if (hasTrack) {
|
if (hasTrack) {
|
||||||
synchronized (this) {
|
synchronized (this) {
|
||||||
/*
|
/*
|
||||||
* Synchronize to prevent race condition in changing markerTrackId
|
* Synchronize to prevent race condition in changing markerTrackId
|
||||||
@@ -369,18 +376,16 @@ public class MapFragment extends Fragment
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onSampledInTrackPoint(Location location) {
|
public void onSampledInTrackPoint(Location location) {
|
||||||
if (LocationUtils.isValidLocation(location)) {
|
mapOverlay.addLocation(location);
|
||||||
mapOverlay.addLocation(location);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onSampledOutTrackPoint(Location loc) {
|
public void onSampledOutTrackPoint(Location location) {
|
||||||
// We don't care.
|
// We don't care.
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onSegmentSplit() {
|
public void onSegmentSplit(Location location) {
|
||||||
mapOverlay.addSegmentSplit();
|
mapOverlay.addSegmentSplit();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -418,24 +423,27 @@ public class MapFragment extends Fragment
|
|||||||
// We don't care.
|
// We don't care.
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean onMinRecordingDistanceChanged(int minRecordingDistance) {
|
||||||
|
// We don't care.
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Resumes the trackDataHub. Needs to be synchronized because trackDataHub can be
|
* Resumes the trackDataHub. Needs to be synchronized because trackDataHub can
|
||||||
* accessed by multiple threads.
|
* be accessed by multiple threads.
|
||||||
*/
|
*/
|
||||||
private synchronized void resumeTrackDataHub() {
|
private synchronized void resumeTrackDataHub() {
|
||||||
trackDataHub = ((TrackDetailActivity) getActivity()).getTrackDataHub();
|
trackDataHub = ((TrackDetailActivity) getActivity()).getTrackDataHub();
|
||||||
trackDataHub.registerTrackDataListener(this, EnumSet.of(
|
trackDataHub.registerTrackDataListener(this, EnumSet.of(TrackDataType.SELECTED_TRACK,
|
||||||
TrackDataType.SELECTED_TRACK,
|
TrackDataType.WAYPOINTS_TABLE, TrackDataType.SAMPLED_IN_TRACK_POINTS_TABLE,
|
||||||
TrackDataType.WAYPOINTS_TABLE,
|
TrackDataType.LOCATION, TrackDataType.HEADING));
|
||||||
TrackDataType.SAMPLED_IN_TRACK_POINTS_TABLE,
|
|
||||||
TrackDataType.LOCATION,
|
|
||||||
TrackDataType.HEADING));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Pauses the trackDataHub. Needs to be synchronized because trackDataHub can be
|
* Pauses the trackDataHub. Needs to be synchronized because trackDataHub can
|
||||||
* accessed by multiple threads.
|
* be accessed by multiple threads.
|
||||||
*/
|
*/
|
||||||
private synchronized void pauseTrackDataHub() {
|
private synchronized void pauseTrackDataHub() {
|
||||||
trackDataHub.unregisterTrackDataListener(this);
|
trackDataHub.unregisterTrackDataListener(this);
|
||||||
@@ -443,15 +451,15 @@ public class MapFragment extends Fragment
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Updates the trackDataHub. Needs to be synchronized because trackDataHub can be
|
* Updates the trackDataHub. Needs to be synchronized because trackDataHub can
|
||||||
* accessed by multiple threads.
|
* be accessed by multiple threads.
|
||||||
*/
|
*/
|
||||||
private synchronized void updateTrackDataHub() {
|
private synchronized void updateTrackDataHub() {
|
||||||
if (trackDataHub != null) {
|
if (trackDataHub != null) {
|
||||||
trackDataHub.forceUpdateLocation();
|
trackDataHub.forceUpdateLocation();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns true if the selected track is recording. Needs to be synchronized
|
* Returns true if the selected track is recording. Needs to be synchronized
|
||||||
* because trackDataHub can be accessed by multiple threads.
|
* because trackDataHub can be accessed by multiple threads.
|
||||||
@@ -461,8 +469,9 @@ public class MapFragment extends Fragment
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Updates the map by either zooming to the requested marker or showing the track.
|
* Updates the map by either zooming to the requested marker or showing the
|
||||||
*
|
* track.
|
||||||
|
*
|
||||||
* @param track the track
|
* @param track the track
|
||||||
*/
|
*/
|
||||||
private void updateMap(Track track) {
|
private void updateMap(Track track) {
|
||||||
@@ -480,7 +489,7 @@ public class MapFragment extends Fragment
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns true if the location is visible.
|
* Returns true if the location is visible.
|
||||||
*
|
*
|
||||||
* @param location the location
|
* @param location the location
|
||||||
*/
|
*/
|
||||||
private boolean isVisible(Location location) {
|
private boolean isVisible(Location location) {
|
||||||
@@ -490,7 +499,7 @@ public class MapFragment extends Fragment
|
|||||||
GeoPoint mapCenter = mapView.getMapCenter();
|
GeoPoint mapCenter = mapView.getMapCenter();
|
||||||
int latitudeSpan = mapView.getLatitudeSpan();
|
int latitudeSpan = mapView.getLatitudeSpan();
|
||||||
int longitudeSpan = mapView.getLongitudeSpan();
|
int longitudeSpan = mapView.getLongitudeSpan();
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* The bottom of the mapView is obscured by the zoom controls, subtract its
|
* The bottom of the mapView is obscured by the zoom controls, subtract its
|
||||||
* height from the visible area.
|
* height from the visible area.
|
||||||
@@ -498,11 +507,11 @@ public class MapFragment extends Fragment
|
|||||||
GeoPoint zoomControlBottom = mapView.getProjection().fromPixels(0, mapView.getHeight());
|
GeoPoint zoomControlBottom = mapView.getProjection().fromPixels(0, mapView.getHeight());
|
||||||
GeoPoint zoomControlTop = mapView.getProjection().fromPixels(
|
GeoPoint zoomControlTop = mapView.getProjection().fromPixels(
|
||||||
0, mapView.getHeight() - mapView.getZoomButtonsController().getZoomControls().getHeight());
|
0, mapView.getHeight() - mapView.getZoomButtonsController().getZoomControls().getHeight());
|
||||||
int zoomControlMargin = Math.abs(zoomControlTop.getLatitudeE6()
|
int zoomControlMargin = Math.abs(
|
||||||
- zoomControlBottom.getLatitudeE6());
|
zoomControlTop.getLatitudeE6() - zoomControlBottom.getLatitudeE6());
|
||||||
GeoRect geoRect = new GeoRect(mapCenter, latitudeSpan, longitudeSpan);
|
GeoRect geoRect = new GeoRect(mapCenter, latitudeSpan, longitudeSpan);
|
||||||
geoRect.top += zoomControlMargin;
|
geoRect.top += zoomControlMargin;
|
||||||
|
|
||||||
GeoPoint geoPoint = LocationUtils.getGeoPoint(location);
|
GeoPoint geoPoint = LocationUtils.getGeoPoint(location);
|
||||||
return geoRect.contains(geoPoint);
|
return geoRect.contains(geoPoint);
|
||||||
}
|
}
|
||||||
@@ -534,7 +543,7 @@ public class MapFragment extends Fragment
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Shows the track.
|
* Shows the track.
|
||||||
*
|
*
|
||||||
* @param track the track
|
* @param track the track
|
||||||
*/
|
*/
|
||||||
private void showTrack(Track track) {
|
private void showTrack(Track track) {
|
||||||
|
|||||||
@@ -186,17 +186,17 @@ public class StatsFragment extends Fragment implements TrackDataListener {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onSampledInTrackPoint(Location loc) {
|
public void onSampledInTrackPoint(Location location) {
|
||||||
// We don't care.
|
// We don't care.
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onSampledOutTrackPoint(Location loc) {
|
public void onSampledOutTrackPoint(Location location) {
|
||||||
// We don't care.
|
// We don't care.
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onSegmentSplit() {
|
public void onSegmentSplit(Location location) {
|
||||||
// We don't care.
|
// We don't care.
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -246,6 +246,12 @@ public class StatsFragment extends Fragment implements TrackDataListener {
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean onMinRecordingDistanceChanged(int minRecordingDistance) {
|
||||||
|
// We don't care.
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Resumes the trackDataHub. Needs to be synchronized because trackDataHub can
|
* Resumes the trackDataHub. Needs to be synchronized because trackDataHub can
|
||||||
* be accessed by multiple threads.
|
* be accessed by multiple threads.
|
||||||
|
|||||||
+36
-44
@@ -10,13 +10,11 @@ import com.google.android.apps.mytracks.content.Track;
|
|||||||
import com.google.android.apps.mytracks.content.Waypoint;
|
import com.google.android.apps.mytracks.content.Waypoint;
|
||||||
import com.google.android.apps.mytracks.io.sendtogoogle.AbstractSendAsyncTask;
|
import com.google.android.apps.mytracks.io.sendtogoogle.AbstractSendAsyncTask;
|
||||||
import com.google.android.apps.mytracks.io.sendtogoogle.SendToGoogleUtils;
|
import com.google.android.apps.mytracks.io.sendtogoogle.SendToGoogleUtils;
|
||||||
import com.google.android.apps.mytracks.stats.DoubleBuffer;
|
|
||||||
import com.google.android.apps.mytracks.stats.TripStatisticsBuilder;
|
import com.google.android.apps.mytracks.stats.TripStatisticsBuilder;
|
||||||
import com.google.android.apps.mytracks.util.ApiAdapterFactory;
|
import com.google.android.apps.mytracks.util.ApiAdapterFactory;
|
||||||
import com.google.android.apps.mytracks.util.LocationUtils;
|
import com.google.android.apps.mytracks.util.LocationUtils;
|
||||||
import com.google.android.apps.mytracks.util.PreferencesUtils;
|
import com.google.android.apps.mytracks.util.PreferencesUtils;
|
||||||
import com.google.android.apps.mytracks.util.SystemUtils;
|
import com.google.android.apps.mytracks.util.SystemUtils;
|
||||||
import com.google.android.apps.mytracks.util.UnitConversions;
|
|
||||||
import com.google.android.maps.mytracks.R;
|
import com.google.android.maps.mytracks.R;
|
||||||
import com.google.api.client.googleapis.GoogleHeaders;
|
import com.google.api.client.googleapis.GoogleHeaders;
|
||||||
import com.google.api.client.googleapis.MethodOverride;
|
import com.google.api.client.googleapis.MethodOverride;
|
||||||
@@ -48,7 +46,7 @@ import java.util.Vector;
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* AsyncTask to send a track to Google Fusion Tables.
|
* AsyncTask to send a track to Google Fusion Tables.
|
||||||
*
|
*
|
||||||
* @author Jimmy Shih
|
* @author Jimmy Shih
|
||||||
*/
|
*/
|
||||||
public class SendFusionTablesAsyncTask extends AbstractSendAsyncTask {
|
public class SendFusionTablesAsyncTask extends AbstractSendAsyncTask {
|
||||||
@@ -59,8 +57,8 @@ public class SendFusionTablesAsyncTask extends AbstractSendAsyncTask {
|
|||||||
@VisibleForTesting
|
@VisibleForTesting
|
||||||
public static final String CONTENT_TYPE = "application/x-www-form-urlencoded";
|
public static final String CONTENT_TYPE = "application/x-www-form-urlencoded";
|
||||||
@VisibleForTesting
|
@VisibleForTesting
|
||||||
public static final String FUSION_TABLES_BASE_URL =
|
public static final String
|
||||||
"https://www.google.com/fusiontables/api/query";
|
FUSION_TABLES_BASE_URL = "https://www.google.com/fusiontables/api/query";
|
||||||
private static final int MAX_POINTS_PER_UPLOAD = 2048;
|
private static final int MAX_POINTS_PER_UPLOAD = 2048;
|
||||||
@VisibleForTesting
|
@VisibleForTesting
|
||||||
public static final String GDATA_VERSION = "2";
|
public static final String GDATA_VERSION = "2";
|
||||||
@@ -72,14 +70,15 @@ public class SendFusionTablesAsyncTask extends AbstractSendAsyncTask {
|
|||||||
private static final int PROGRESS_UPLOAD_WAYPOINTS = 95;
|
private static final int PROGRESS_UPLOAD_WAYPOINTS = 95;
|
||||||
private static final int PROGRESS_COMPLETE = 100;
|
private static final int PROGRESS_COMPLETE = 100;
|
||||||
|
|
||||||
// See http://support.google.com/fusiontables/bin/answer.py?hl=en&answer=185991
|
// See
|
||||||
|
// http://support.google.com/fusiontables/bin/answer.py?hl=en&answer=185991
|
||||||
private static final String MARKER_TYPE_START = "large_green";
|
private static final String MARKER_TYPE_START = "large_green";
|
||||||
private static final String MARKER_TYPE_END = "large_red";
|
private static final String MARKER_TYPE_END = "large_red";
|
||||||
private static final String MARKER_TYPE_WAYPOINT = "large_blue";
|
private static final String MARKER_TYPE_WAYPOINT = "large_blue";
|
||||||
private static final String MARKER_TYPE_STATISTICS = "large_yellow";
|
private static final String MARKER_TYPE_STATISTICS = "large_yellow";
|
||||||
|
|
||||||
private static final String TAG = SendFusionTablesAsyncTask.class.getSimpleName();
|
private static final String TAG = SendFusionTablesAsyncTask.class.getSimpleName();
|
||||||
|
|
||||||
private final Context context;
|
private final Context context;
|
||||||
private final long trackId;
|
private final long trackId;
|
||||||
private final Account account;
|
private final Account account;
|
||||||
@@ -127,8 +126,8 @@ public class SendFusionTablesAsyncTask extends AbstractSendAsyncTask {
|
|||||||
currentSegment = 1;
|
currentSegment = 1;
|
||||||
|
|
||||||
try {
|
try {
|
||||||
authToken = AccountManager.get(context).blockingGetAuthToken(
|
authToken = AccountManager.get(context)
|
||||||
account, SendFusionTablesUtils.SERVICE, false);
|
.blockingGetAuthToken(account, SendFusionTablesUtils.SERVICE, false);
|
||||||
} catch (OperationCanceledException e) {
|
} catch (OperationCanceledException e) {
|
||||||
Log.d(TAG, "Unable to get auth token", e);
|
Log.d(TAG, "Unable to get auth token", e);
|
||||||
return retryTask();
|
return retryTask();
|
||||||
@@ -182,7 +181,7 @@ public class SendFusionTablesAsyncTask extends AbstractSendAsyncTask {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Creates a new table.
|
* Creates a new table.
|
||||||
*
|
*
|
||||||
* @param track the track
|
* @param track the track
|
||||||
* @return true if success.
|
* @return true if success.
|
||||||
*/
|
*/
|
||||||
@@ -194,7 +193,7 @@ public class SendFusionTablesAsyncTask extends AbstractSendAsyncTask {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Unlists a table.
|
* Unlists a table.
|
||||||
*
|
*
|
||||||
* @return true if success.
|
* @return true if success.
|
||||||
*/
|
*/
|
||||||
private boolean unlistTable() {
|
private boolean unlistTable() {
|
||||||
@@ -204,32 +203,31 @@ public class SendFusionTablesAsyncTask extends AbstractSendAsyncTask {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Uploads all the points in a track.
|
* Uploads all the points in a track.
|
||||||
*
|
*
|
||||||
* @param track the track
|
* @param track the track
|
||||||
* @return true if success.
|
* @return true if success.
|
||||||
*/
|
*/
|
||||||
private boolean uploadAllTrackPoints(Track track) {
|
private boolean uploadAllTrackPoints(Track track) {
|
||||||
Cursor locationsCursor = null;
|
Cursor locationsCursor = null;
|
||||||
try {
|
try {
|
||||||
boolean metricUnits = PreferencesUtils.getBoolean(
|
|
||||||
context, R.string.metric_units_key, PreferencesUtils.METRIC_UNITS_DEFAULT);
|
|
||||||
|
|
||||||
locationsCursor = myTracksProviderUtils.getLocationsCursor(trackId, 0, -1, false);
|
locationsCursor = myTracksProviderUtils.getLocationsCursor(trackId, 0, -1, false);
|
||||||
if (locationsCursor == null) {
|
if (locationsCursor == null) {
|
||||||
Log.d(TAG, "Location cursor is null");
|
Log.d(TAG, "Location cursor is null");
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
int locationsCount = locationsCursor.getCount();
|
int locationsCount = locationsCursor.getCount();
|
||||||
List<Location> locations = new ArrayList<Location>(MAX_POINTS_PER_UPLOAD);
|
List<Location> locations = new ArrayList<Location>(MAX_POINTS_PER_UPLOAD);
|
||||||
Location lastLocation = null;
|
Location lastLocation = null;
|
||||||
|
|
||||||
// For chart server, limit the number of elevation readings to 250.
|
// For chart server, limit the number of elevation readings to 250.
|
||||||
int elevationSamplingFrequency = Math.max(1, (int) (locationsCount / 250.0));
|
int elevationSamplingFrequency = Math.max(1, (int) (locationsCount / 250.0));
|
||||||
TripStatisticsBuilder tripStatisticsBuilder = new TripStatisticsBuilder(
|
|
||||||
track.getTripStatistics().getStartTime());
|
|
||||||
DoubleBuffer elevationBuffer = new DoubleBuffer(Constants.ELEVATION_SMOOTHING_FACTOR);
|
|
||||||
Vector<Double> distances = new Vector<Double>();
|
Vector<Double> distances = new Vector<Double>();
|
||||||
Vector<Double> elevations = new Vector<Double>();
|
Vector<Double> elevations = new Vector<Double>();
|
||||||
|
TripStatisticsBuilder tripStatisticsBuilder = new TripStatisticsBuilder(
|
||||||
|
track.getTripStatistics().getStartTime());
|
||||||
|
int minRecordingDistance = PreferencesUtils.getInt(context,
|
||||||
|
R.string.min_recording_distance_key, PreferencesUtils.MIN_RECORDING_DISTANCE_DEFAULT);
|
||||||
|
|
||||||
for (int i = 0; i < locationsCount; i++) {
|
for (int i = 0; i < locationsCount; i++) {
|
||||||
locationsCursor.moveToPosition(i);
|
locationsCursor.moveToPosition(i);
|
||||||
@@ -246,16 +244,12 @@ public class SendFusionTablesAsyncTask extends AbstractSendAsyncTask {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Add to the distances and elevations vectors
|
tripStatisticsBuilder.addLocation(location, minRecordingDistance);
|
||||||
|
if (i % elevationSamplingFrequency == 0) {
|
||||||
|
distances.add(tripStatisticsBuilder.getTripStatistics().getTotalDistance());
|
||||||
|
elevations.add(tripStatisticsBuilder.getSmoothedElevation());
|
||||||
|
}
|
||||||
if (LocationUtils.isValidLocation(location)) {
|
if (LocationUtils.isValidLocation(location)) {
|
||||||
tripStatisticsBuilder.addLocation(location, PreferencesUtils.MIN_RECORDING_DISTANCE_DEFAULT);
|
|
||||||
// All points go into the smoothing buffer
|
|
||||||
elevationBuffer.setNext(metricUnits ? location.getAltitude()
|
|
||||||
: location.getAltitude() * UnitConversions.M_TO_FT);
|
|
||||||
if (i % elevationSamplingFrequency == 0) {
|
|
||||||
distances.add(tripStatisticsBuilder.getTripStatistics().getTotalDistance());
|
|
||||||
elevations.add(elevationBuffer.getAverage());
|
|
||||||
}
|
|
||||||
lastLocation = location;
|
lastLocation = location;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -280,7 +274,7 @@ public class SendFusionTablesAsyncTask extends AbstractSendAsyncTask {
|
|||||||
// Create an end marker
|
// Create an end marker
|
||||||
if (lastLocation != null) {
|
if (lastLocation != null) {
|
||||||
distances.add(tripStatisticsBuilder.getTripStatistics().getTotalDistance());
|
distances.add(tripStatisticsBuilder.getTripStatistics().getTotalDistance());
|
||||||
elevations.add(elevationBuffer.getAverage());
|
elevations.add(tripStatisticsBuilder.getSmoothedElevation());
|
||||||
DescriptionGenerator descriptionGenerator = new DescriptionGeneratorImpl(context);
|
DescriptionGenerator descriptionGenerator = new DescriptionGeneratorImpl(context);
|
||||||
track.setDescription(
|
track.setDescription(
|
||||||
descriptionGenerator.generateTrackDescription(track, distances, elevations, true));
|
descriptionGenerator.generateTrackDescription(track, distances, elevations, true));
|
||||||
@@ -301,7 +295,7 @@ public class SendFusionTablesAsyncTask extends AbstractSendAsyncTask {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Prepares and uploads a list of locations from a track.
|
* Prepares and uploads a list of locations from a track.
|
||||||
*
|
*
|
||||||
* @param track the track
|
* @param track the track
|
||||||
* @param locations the locations from the track
|
* @param locations the locations from the track
|
||||||
* @param lastBatch true if it is the last batch of locations
|
* @param lastBatch true if it is the last batch of locations
|
||||||
@@ -328,7 +322,7 @@ public class SendFusionTablesAsyncTask extends AbstractSendAsyncTask {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Uploads all the waypoints.
|
* Uploads all the waypoints.
|
||||||
*
|
*
|
||||||
* @return true if success.
|
* @return true if success.
|
||||||
*/
|
*/
|
||||||
private boolean uploadWaypoints() {
|
private boolean uploadWaypoints() {
|
||||||
@@ -358,17 +352,15 @@ public class SendFusionTablesAsyncTask extends AbstractSendAsyncTask {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Creates a new row in Google Fusion Tables representing a marker as a
|
* Creates a new row in Google Fusion Tables representing a marker as a point.
|
||||||
* point.
|
*
|
||||||
*
|
|
||||||
* @param name the marker name
|
* @param name the marker name
|
||||||
* @param description the marker description
|
* @param description the marker description
|
||||||
* @param location the marker location
|
* @param location the marker location
|
||||||
* @param type the marker type
|
* @param type the marker type
|
||||||
* @return true if success.
|
* @return true if success.
|
||||||
*/
|
*/
|
||||||
private boolean createNewPoint(
|
private boolean createNewPoint(String name, String description, Location location, String type) {
|
||||||
String name, String description, Location location, String type) {
|
|
||||||
String query = "INSERT INTO " + tableId + " (name,description,geometry,marker) VALUES "
|
String query = "INSERT INTO " + tableId + " (name,description,geometry,marker) VALUES "
|
||||||
+ SendFusionTablesUtils.formatSqlValues(
|
+ SendFusionTablesUtils.formatSqlValues(
|
||||||
name, description, SendFusionTablesUtils.getKmlPoint(location), type);
|
name, description, SendFusionTablesUtils.getKmlPoint(location), type);
|
||||||
@@ -376,22 +368,23 @@ public class SendFusionTablesAsyncTask extends AbstractSendAsyncTask {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Creates a new row in Google Fusion Tables representing the track as a
|
* Creates a new row in Google Fusion Tables representing the track as a line
|
||||||
* line segment.
|
* segment.
|
||||||
*
|
*
|
||||||
* @param track the track
|
* @param track the track
|
||||||
* @return true if success.
|
* @return true if success.
|
||||||
*/
|
*/
|
||||||
private boolean createNewLineString(Track track) {
|
private boolean createNewLineString(Track track) {
|
||||||
String query = "INSERT INTO " + tableId + " (name,description,geometry) VALUES "
|
String query = "INSERT INTO " + tableId
|
||||||
+ SendFusionTablesUtils.formatSqlValues(track.getName(), track.getDescription(),
|
+ " (name,description,geometry) VALUES " + SendFusionTablesUtils.formatSqlValues(
|
||||||
|
track.getName(), track.getDescription(),
|
||||||
SendFusionTablesUtils.getKmlLineString(track.getLocations()));
|
SendFusionTablesUtils.getKmlLineString(track.getLocations()));
|
||||||
return sendQuery(query, false);
|
return sendQuery(query, false);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Sends a query to Google Fusion Tables.
|
* Sends a query to Google Fusion Tables.
|
||||||
*
|
*
|
||||||
* @param query the Fusion Tables SQL query
|
* @param query the Fusion Tables SQL query
|
||||||
* @param setTableId true to set the table id
|
* @param setTableId true to set the table id
|
||||||
* @return true if success.
|
* @return true if success.
|
||||||
@@ -463,7 +456,7 @@ public class SendFusionTablesAsyncTask extends AbstractSendAsyncTask {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Updates the progress based on the number of locations uploaded.
|
* Updates the progress based on the number of locations uploaded.
|
||||||
*
|
*
|
||||||
* @param uploaded the number of uploaded locations
|
* @param uploaded the number of uploaded locations
|
||||||
* @param total the number of total locations
|
* @param total the number of total locations
|
||||||
*/
|
*/
|
||||||
@@ -474,4 +467,3 @@ public class SendFusionTablesAsyncTask extends AbstractSendAsyncTask {
|
|||||||
publishProgress((int) scaledPercentage);
|
publishProgress((int) scaledPercentage);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -28,11 +28,9 @@ import com.google.android.apps.mytracks.io.gdata.maps.MapsGDataConverter;
|
|||||||
import com.google.android.apps.mytracks.io.gdata.maps.XmlMapsGDataParserFactory;
|
import com.google.android.apps.mytracks.io.gdata.maps.XmlMapsGDataParserFactory;
|
||||||
import com.google.android.apps.mytracks.io.sendtogoogle.AbstractSendAsyncTask;
|
import com.google.android.apps.mytracks.io.sendtogoogle.AbstractSendAsyncTask;
|
||||||
import com.google.android.apps.mytracks.io.sendtogoogle.SendToGoogleUtils;
|
import com.google.android.apps.mytracks.io.sendtogoogle.SendToGoogleUtils;
|
||||||
import com.google.android.apps.mytracks.stats.DoubleBuffer;
|
|
||||||
import com.google.android.apps.mytracks.stats.TripStatisticsBuilder;
|
import com.google.android.apps.mytracks.stats.TripStatisticsBuilder;
|
||||||
import com.google.android.apps.mytracks.util.LocationUtils;
|
import com.google.android.apps.mytracks.util.LocationUtils;
|
||||||
import com.google.android.apps.mytracks.util.PreferencesUtils;
|
import com.google.android.apps.mytracks.util.PreferencesUtils;
|
||||||
import com.google.android.apps.mytracks.util.UnitConversions;
|
|
||||||
import com.google.android.common.gdata.AndroidXmlParserFactory;
|
import com.google.android.common.gdata.AndroidXmlParserFactory;
|
||||||
import com.google.android.maps.mytracks.R;
|
import com.google.android.maps.mytracks.R;
|
||||||
import com.google.common.annotations.VisibleForTesting;
|
import com.google.common.annotations.VisibleForTesting;
|
||||||
@@ -63,14 +61,14 @@ import org.xmlpull.v1.XmlPullParserException;
|
|||||||
* the Google Maps servers through this API is only allowed for the My Tracks
|
* the Google Maps servers through this API is only allowed for the My Tracks
|
||||||
* application. Other applications looking to upload maps data should look into
|
* application. Other applications looking to upload maps data should look into
|
||||||
* using the Google Fusion Tables API.
|
* using the Google Fusion Tables API.
|
||||||
*
|
*
|
||||||
* @author Jimmy Shih
|
* @author Jimmy Shih
|
||||||
*/
|
*/
|
||||||
public class SendMapsAsyncTask extends AbstractSendAsyncTask {
|
public class SendMapsAsyncTask extends AbstractSendAsyncTask {
|
||||||
private static final String START_ICON_URL =
|
private static final String
|
||||||
"http://maps.google.com/mapfiles/ms/micons/green-dot.png";
|
START_ICON_URL = "http://maps.google.com/mapfiles/ms/micons/green-dot.png";
|
||||||
private static final String END_ICON_URL =
|
private static final String
|
||||||
"http://maps.google.com/mapfiles/ms/micons/red-dot.png";
|
END_ICON_URL = "http://maps.google.com/mapfiles/ms/micons/red-dot.png";
|
||||||
private static final int MAX_POINTS_PER_UPLOAD = 500;
|
private static final int MAX_POINTS_PER_UPLOAD = 500;
|
||||||
|
|
||||||
private static final int PROGRESS_FETCH_MAP_ID = 5;
|
private static final int PROGRESS_FETCH_MAP_ID = 5;
|
||||||
@@ -97,25 +95,25 @@ public class SendMapsAsyncTask extends AbstractSendAsyncTask {
|
|||||||
private String mapId;
|
private String mapId;
|
||||||
int currentSegment;
|
int currentSegment;
|
||||||
|
|
||||||
public SendMapsAsyncTask(SendMapsActivity activity, long trackId, Account account,
|
public SendMapsAsyncTask(
|
||||||
String chooseMapId) {
|
SendMapsActivity activity, long trackId, Account account, String chooseMapId) {
|
||||||
this(activity, trackId, account, chooseMapId, MyTracksProviderUtils.Factory.get(activity
|
this(activity, trackId, account, chooseMapId, MyTracksProviderUtils.Factory.get(
|
||||||
.getApplicationContext()));
|
activity.getApplicationContext()));
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* This constructor is created for test.
|
* This constructor is created for test.
|
||||||
*/
|
*/
|
||||||
@VisibleForTesting
|
@VisibleForTesting
|
||||||
public SendMapsAsyncTask (
|
public SendMapsAsyncTask(SendMapsActivity activity, long trackId, Account account,
|
||||||
SendMapsActivity activity, long trackId, Account account, String chooseMapId, MyTracksProviderUtils myTracksProviderUtils) {
|
String chooseMapId, MyTracksProviderUtils myTracksProviderUtils) {
|
||||||
super(activity);
|
super(activity);
|
||||||
this.trackId = trackId;
|
this.trackId = trackId;
|
||||||
this.account = account;
|
this.account = account;
|
||||||
this.chooseMapId = chooseMapId;
|
this.chooseMapId = chooseMapId;
|
||||||
this.myTracksProviderUtils = myTracksProviderUtils;
|
this.myTracksProviderUtils = myTracksProviderUtils;
|
||||||
context = activity.getApplicationContext();
|
context = activity.getApplicationContext();
|
||||||
|
|
||||||
gDataClient = GDataClientFactory.getGDataClient(context);
|
gDataClient = GDataClientFactory.getGDataClient(context);
|
||||||
mapsClient = new MapsClient(
|
mapsClient = new MapsClient(
|
||||||
gDataClient, new XmlMapsGDataParserFactory(new AndroidXmlParserFactory()));
|
gDataClient, new XmlMapsGDataParserFactory(new AndroidXmlParserFactory()));
|
||||||
@@ -154,11 +152,11 @@ public class SendMapsAsyncTask extends AbstractSendAsyncTask {
|
|||||||
Log.d(TAG, "Unable to create a maps gdata converter", e);
|
Log.d(TAG, "Unable to create a maps gdata converter", e);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Get auth token
|
// Get auth token
|
||||||
try {
|
try {
|
||||||
authToken = AccountManager.get(context).blockingGetAuthToken(
|
authToken = AccountManager.get(context)
|
||||||
account, MapsConstants.SERVICE_NAME, false);
|
.blockingGetAuthToken(account, MapsConstants.SERVICE_NAME, false);
|
||||||
} catch (OperationCanceledException e) {
|
} catch (OperationCanceledException e) {
|
||||||
Log.d(TAG, "Unable to get auth token", e);
|
Log.d(TAG, "Unable to get auth token", e);
|
||||||
return retryTask();
|
return retryTask();
|
||||||
@@ -208,9 +206,9 @@ public class SendMapsAsyncTask extends AbstractSendAsyncTask {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Fetches the {@link SendMapsAsyncTask#mapId} instance variable for
|
* Fetches the {@link SendMapsAsyncTask#mapId} instance variable for sending a
|
||||||
* sending a track to Google Maps.
|
* track to Google Maps.
|
||||||
*
|
*
|
||||||
* @param track the Track
|
* @param track the Track
|
||||||
* @return true if able to fetch the mapId variable.
|
* @return true if able to fetch the mapId variable.
|
||||||
*/
|
*/
|
||||||
@@ -219,7 +217,7 @@ public class SendMapsAsyncTask extends AbstractSendAsyncTask {
|
|||||||
if (isCancelled()) {
|
if (isCancelled()) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (chooseMapId != null) {
|
if (chooseMapId != null) {
|
||||||
mapId = chooseMapId;
|
mapId = chooseMapId;
|
||||||
return true;
|
return true;
|
||||||
@@ -247,7 +245,7 @@ public class SendMapsAsyncTask extends AbstractSendAsyncTask {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Uploads all the points in a track.
|
* Uploads all the points in a track.
|
||||||
*
|
*
|
||||||
* @param track the track
|
* @param track the track
|
||||||
* @return true if success.
|
* @return true if success.
|
||||||
*/
|
*/
|
||||||
@@ -255,25 +253,24 @@ public class SendMapsAsyncTask extends AbstractSendAsyncTask {
|
|||||||
boolean uploadAllTrackPoints(Track track) {
|
boolean uploadAllTrackPoints(Track track) {
|
||||||
Cursor locationsCursor = null;
|
Cursor locationsCursor = null;
|
||||||
try {
|
try {
|
||||||
boolean metricUnits = PreferencesUtils.getBoolean(
|
|
||||||
context, R.string.metric_units_key, PreferencesUtils.METRIC_UNITS_DEFAULT);
|
|
||||||
|
|
||||||
locationsCursor = myTracksProviderUtils.getLocationsCursor(trackId, 0, -1, false);
|
locationsCursor = myTracksProviderUtils.getLocationsCursor(trackId, 0, -1, false);
|
||||||
if (locationsCursor == null) {
|
if (locationsCursor == null) {
|
||||||
Log.d(TAG, "Location cursor is null");
|
Log.d(TAG, "Location cursor is null");
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
int locationsCount = locationsCursor.getCount();
|
int locationsCount = locationsCursor.getCount();
|
||||||
List<Location> locations = new ArrayList<Location>(MAX_POINTS_PER_UPLOAD);
|
List<Location> locations = new ArrayList<Location>(MAX_POINTS_PER_UPLOAD);
|
||||||
Location lastLocation = null;
|
Location lastLocation = null;
|
||||||
|
|
||||||
// For chart server, limit the number of elevation readings to 250.
|
// For chart server, limit the number of elevation readings to 250.
|
||||||
int elevationSamplingFrequency = Math.max(1, (int) (locationsCount / 250.0));
|
int elevationSamplingFrequency = Math.max(1, (int) (locationsCount / 250.0));
|
||||||
TripStatisticsBuilder tripStatisticsBuilder = new TripStatisticsBuilder(
|
|
||||||
track.getTripStatistics().getStartTime());
|
|
||||||
DoubleBuffer elevationBuffer = new DoubleBuffer(Constants.ELEVATION_SMOOTHING_FACTOR);
|
|
||||||
Vector<Double> distances = new Vector<Double>();
|
Vector<Double> distances = new Vector<Double>();
|
||||||
Vector<Double> elevations = new Vector<Double>();
|
Vector<Double> elevations = new Vector<Double>();
|
||||||
|
TripStatisticsBuilder tripStatisticsBuilder = new TripStatisticsBuilder(
|
||||||
|
track.getTripStatistics().getStartTime());
|
||||||
|
int minRecordingDistance = PreferencesUtils.getInt(context,
|
||||||
|
R.string.min_recording_distance_key, PreferencesUtils.MIN_RECORDING_DISTANCE_DEFAULT);
|
||||||
|
|
||||||
for (int i = 0; i < locationsCount; i++) {
|
for (int i = 0; i < locationsCount; i++) {
|
||||||
locationsCursor.moveToPosition(i);
|
locationsCursor.moveToPosition(i);
|
||||||
@@ -290,16 +287,12 @@ public class SendMapsAsyncTask extends AbstractSendAsyncTask {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Add to the distances and elevations vectors
|
tripStatisticsBuilder.addLocation(location, minRecordingDistance);
|
||||||
|
if (i % elevationSamplingFrequency == 0) {
|
||||||
|
distances.add(tripStatisticsBuilder.getTripStatistics().getTotalDistance());
|
||||||
|
elevations.add(tripStatisticsBuilder.getSmoothedElevation());
|
||||||
|
}
|
||||||
if (LocationUtils.isValidLocation(location)) {
|
if (LocationUtils.isValidLocation(location)) {
|
||||||
tripStatisticsBuilder.addLocation(location, PreferencesUtils.MIN_RECORDING_DISTANCE_DEFAULT);
|
|
||||||
// All points go into the smoothing buffer
|
|
||||||
elevationBuffer.setNext(metricUnits ? location.getAltitude()
|
|
||||||
: location.getAltitude() * UnitConversions.M_TO_FT);
|
|
||||||
if (i % elevationSamplingFrequency == 0) {
|
|
||||||
distances.add(tripStatisticsBuilder.getTripStatistics().getTotalDistance());
|
|
||||||
elevations.add(elevationBuffer.getAverage());
|
|
||||||
}
|
|
||||||
lastLocation = location;
|
lastLocation = location;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -324,9 +317,10 @@ public class SendMapsAsyncTask extends AbstractSendAsyncTask {
|
|||||||
// Create an end marker
|
// Create an end marker
|
||||||
if (lastLocation != null) {
|
if (lastLocation != null) {
|
||||||
distances.add(tripStatisticsBuilder.getTripStatistics().getTotalDistance());
|
distances.add(tripStatisticsBuilder.getTripStatistics().getTotalDistance());
|
||||||
elevations.add(elevationBuffer.getAverage());
|
elevations.add(tripStatisticsBuilder.getSmoothedElevation());
|
||||||
|
DescriptionGenerator descriptionGenerator = new DescriptionGeneratorImpl(context);
|
||||||
track.setDescription(getTrackDescription(track, distances, elevations));
|
track.setDescription(
|
||||||
|
descriptionGenerator.generateTrackDescription(track, distances, elevations, true));
|
||||||
if (!uploadMarker(context.getString(R.string.marker_label_end, track.getName()),
|
if (!uploadMarker(context.getString(R.string.marker_label_end, track.getName()),
|
||||||
track.getDescription(), END_ICON_URL, lastLocation)) {
|
track.getDescription(), END_ICON_URL, lastLocation)) {
|
||||||
Log.d(TAG, "Unable to create an end marker");
|
Log.d(TAG, "Unable to create an end marker");
|
||||||
@@ -340,26 +334,10 @@ public class SendMapsAsyncTask extends AbstractSendAsyncTask {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Gets the description of a track.
|
|
||||||
*
|
|
||||||
* @param track the track
|
|
||||||
* @param distances distance vectors
|
|
||||||
* @param elevations elevation vectors
|
|
||||||
* @return the description of a track.
|
|
||||||
*/
|
|
||||||
@VisibleForTesting
|
|
||||||
String getTrackDescription(Track track, Vector<Double> distances, Vector<Double> elevations) {
|
|
||||||
DescriptionGenerator descriptionGenerator = new DescriptionGeneratorImpl(context);
|
|
||||||
return "<p>" + track.getDescription() + "</p><p>"
|
|
||||||
+ descriptionGenerator.generateTrackDescription(track, distances, elevations, true)
|
|
||||||
+ "</p>";
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Prepares and uploads a list of locations from a track.
|
* Prepares and uploads a list of locations from a track.
|
||||||
*
|
*
|
||||||
* @param track the track
|
* @param track the track
|
||||||
* @param locations the locations from the track
|
* @param locations the locations from the track
|
||||||
* @param lastBatch true if it is the last batch of locations
|
* @param lastBatch true if it is the last batch of locations
|
||||||
@@ -396,8 +374,7 @@ public class SendMapsAsyncTask extends AbstractSendAsyncTask {
|
|||||||
* @return true if success.
|
* @return true if success.
|
||||||
*/
|
*/
|
||||||
@VisibleForTesting
|
@VisibleForTesting
|
||||||
boolean uploadMarker(
|
boolean uploadMarker(String title, String description, String iconUrl, Location location) {
|
||||||
String title, String description, String iconUrl, Location location) {
|
|
||||||
if (isCancelled()) {
|
if (isCancelled()) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
@@ -449,10 +426,10 @@ public class SendMapsAsyncTask extends AbstractSendAsyncTask {
|
|||||||
}
|
}
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Uploads all the waypoints.
|
* Uploads all the waypoints.
|
||||||
*
|
*
|
||||||
* @return true if success.
|
* @return true if success.
|
||||||
*/
|
*/
|
||||||
@VisibleForTesting
|
@VisibleForTesting
|
||||||
@@ -497,7 +474,7 @@ public class SendMapsAsyncTask extends AbstractSendAsyncTask {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Updates the progress based on the number of locations uploaded.
|
* Updates the progress based on the number of locations uploaded.
|
||||||
*
|
*
|
||||||
* @param uploaded the number of uploaded locations
|
* @param uploaded the number of uploaded locations
|
||||||
* @param total the number of total locations
|
* @param total the number of total locations
|
||||||
*/
|
*/
|
||||||
@@ -505,11 +482,10 @@ public class SendMapsAsyncTask extends AbstractSendAsyncTask {
|
|||||||
void updateProgress(int uploaded, int total) {
|
void updateProgress(int uploaded, int total) {
|
||||||
publishProgress(getPercentage(uploaded, total));
|
publishProgress(getPercentage(uploaded, total));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Count the percentage of the number of locations uploaded.
|
* Count the percentage of the number of locations uploaded.
|
||||||
*
|
*
|
||||||
* @param uploaded the number of uploaded locations
|
* @param uploaded the number of uploaded locations
|
||||||
* @param total the number of total locations
|
* @param total the number of total locations
|
||||||
*/
|
*/
|
||||||
@@ -520,7 +496,7 @@ public class SendMapsAsyncTask extends AbstractSendAsyncTask {
|
|||||||
* (PROGRESS_UPLOAD_DATA_MAX - PROGRESS_UPLOAD_DATA_MIN) + PROGRESS_UPLOAD_DATA_MIN;
|
* (PROGRESS_UPLOAD_DATA_MAX - PROGRESS_UPLOAD_DATA_MIN) + PROGRESS_UPLOAD_DATA_MIN;
|
||||||
return (int) scaledPercentage;
|
return (int) scaledPercentage;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Gets the mapID.
|
* Gets the mapID.
|
||||||
*
|
*
|
||||||
@@ -530,7 +506,7 @@ public class SendMapsAsyncTask extends AbstractSendAsyncTask {
|
|||||||
String getMapId() {
|
String getMapId() {
|
||||||
return mapId;
|
return mapId;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Sets the value of mapsGDataConverter.
|
* Sets the value of mapsGDataConverter.
|
||||||
*
|
*
|
||||||
|
|||||||
@@ -285,7 +285,7 @@ public class TrackRecordingService extends Service {
|
|||||||
// Require announcementExecutor and splitExecutor to be created.
|
// Require announcementExecutor and splitExecutor to be created.
|
||||||
sharedPreferenceChangeListener.onSharedPreferenceChanged(sharedPreferences, null);
|
sharedPreferenceChangeListener.onSharedPreferenceChanged(sharedPreferences, null);
|
||||||
|
|
||||||
timer.schedule(checkLocationListener, 5 * ONE_MINUTE, ONE_MINUTE);
|
timer.schedule(checkLocationListener, 0, ONE_MINUTE);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Try to restart the previous recording track in case the service has been
|
* Try to restart the previous recording track in case the service has been
|
||||||
@@ -843,7 +843,7 @@ public class TrackRecordingService extends Service {
|
|||||||
|
|
||||||
double distanceToLastTrackLocation = location.distanceTo(lastTrackLocation);
|
double distanceToLastTrackLocation = location.distanceTo(lastTrackLocation);
|
||||||
if (distanceToLastTrackLocation < minRecordingDistance && sensorDataSet == null) {
|
if (distanceToLastTrackLocation < minRecordingDistance && sensorDataSet == null) {
|
||||||
// skip
|
Log.d(TAG, "Not recording location due to min recording distance.");
|
||||||
} else if (distanceToLastTrackLocation > maxRecordingDistance) {
|
} else if (distanceToLastTrackLocation > maxRecordingDistance) {
|
||||||
insertLocation(track, lastLocation, lastTrackLocation);
|
insertLocation(track, lastLocation, lastTrackLocation);
|
||||||
Location pause = new Location(LocationManager.GPS_PROVIDER);
|
Location pause = new Location(LocationManager.GPS_PROVIDER);
|
||||||
@@ -873,9 +873,11 @@ public class TrackRecordingService extends Service {
|
|||||||
|
|
||||||
private void insertLocation(Track track, Location location, Location lastRecordedLocation) {
|
private void insertLocation(Track track, Location location, Location lastRecordedLocation) {
|
||||||
if (location == null) {
|
if (location == null) {
|
||||||
|
Log.w(TAG, "Ignore insertLocation. loation is null.");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (location.equals(lastRecordedLocation)) {
|
if (location.equals(lastRecordedLocation)) {
|
||||||
|
Log.w(TAG, "Ignore insertLocation. loation same as last recorded location.");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -145,11 +145,14 @@ public class TripStatisticsBuilder {
|
|||||||
* is noisy so the smoothed elevation is better than the raw elevation for
|
* is noisy so the smoothed elevation is better than the raw elevation for
|
||||||
* many tasks.
|
* many tasks.
|
||||||
*/
|
*/
|
||||||
@VisibleForTesting
|
public double getSmoothedElevation() {
|
||||||
double getSmoothedElevation() {
|
|
||||||
return elevationBuffer.getAverage();
|
return elevationBuffer.getAverage();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public double getSmoothedSpeed() {
|
||||||
|
return speedBuffer.getAverage();
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Updates a speed reading. Assumes the user is moving.
|
* Updates a speed reading. Assumes the user is moving.
|
||||||
*
|
*
|
||||||
@@ -160,7 +163,7 @@ public class TripStatisticsBuilder {
|
|||||||
*/
|
*/
|
||||||
@VisibleForTesting
|
@VisibleForTesting
|
||||||
void updateSpeed(long time, double speed, long lastLocationTime, double lastLocationSpeed) {
|
void updateSpeed(long time, double speed, long lastLocationTime, double lastLocationSpeed) {
|
||||||
if (!isValidSpeed(time, speed, lastLocationTime, lastLocationSpeed, speedBuffer)) {
|
if (!isValidSpeed(time, speed, lastLocationTime, lastLocationSpeed)) {
|
||||||
Log.d(TAG, "Invalid speed. speed: " + speed + " lastLocationSpeed: " + lastLocationSpeed);
|
Log.d(TAG, "Invalid speed. speed: " + speed + " lastLocationSpeed: " + lastLocationSpeed);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@@ -168,10 +171,6 @@ public class TripStatisticsBuilder {
|
|||||||
if (speed > currentSegment.getMaxSpeed()) {
|
if (speed > currentSegment.getMaxSpeed()) {
|
||||||
currentSegment.setMaxSpeed(speed);
|
currentSegment.setMaxSpeed(speed);
|
||||||
}
|
}
|
||||||
double movingSpeed = currentSegment.getAverageMovingSpeed();
|
|
||||||
if (speedBuffer.isFull() && movingSpeed > currentSegment.getMaxSpeed()) {
|
|
||||||
currentSegment.setMaxSpeed(movingSpeed);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -229,10 +228,9 @@ public class TripStatisticsBuilder {
|
|||||||
* @param speed the speed
|
* @param speed the speed
|
||||||
* @param lastLocationTime the last location time
|
* @param lastLocationTime the last location time
|
||||||
* @param lastLocationSpeed the last location speed
|
* @param lastLocationSpeed the last location speed
|
||||||
* @param speedBuffer a buffer of speed readings
|
|
||||||
*/
|
*/
|
||||||
public static boolean isValidSpeed(long time, double speed, long lastLocationTime,
|
private boolean isValidSpeed(
|
||||||
double lastLocationSpeed, DoubleBuffer speedBuffer) {
|
long time, double speed, long lastLocationTime, double lastLocationSpeed) {
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* There are a lot of noisy speed readings. Do the cheapest checks first,
|
* There are a lot of noisy speed readings. Do the cheapest checks first,
|
||||||
|
|||||||
@@ -19,6 +19,7 @@ package com.google.android.apps.mytracks.util;
|
|||||||
import com.google.android.apps.mytracks.content.Track;
|
import com.google.android.apps.mytracks.content.Track;
|
||||||
import com.google.android.apps.mytracks.stats.TripStatistics;
|
import com.google.android.apps.mytracks.stats.TripStatistics;
|
||||||
import com.google.android.maps.mytracks.R;
|
import com.google.android.maps.mytracks.R;
|
||||||
|
import com.google.common.annotations.VisibleForTesting;
|
||||||
|
|
||||||
import android.content.Context;
|
import android.content.Context;
|
||||||
|
|
||||||
@@ -31,11 +32,9 @@ import java.util.Vector;
|
|||||||
*/
|
*/
|
||||||
public class ChartURLGenerator {
|
public class ChartURLGenerator {
|
||||||
|
|
||||||
private static final String CHARTS_BASE_URL =
|
private static final String CHARTS_BASE_URL = "http://chart.apis.google.com/chart?";
|
||||||
"http://chart.apis.google.com/chart?";
|
|
||||||
|
|
||||||
private ChartURLGenerator() {
|
private ChartURLGenerator() {}
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Gets a chart of a track.
|
* Gets a chart of a track.
|
||||||
@@ -54,8 +53,7 @@ public class ChartURLGenerator {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Gets a chart of a track.
|
* Gets a chart of a track. This form is for testing without contexts.
|
||||||
* This form is for testing without contexts.
|
|
||||||
*
|
*
|
||||||
* @param distances An array of distance measurements
|
* @param distances An array of distance measurements
|
||||||
* @param elevations A matching array of elevation measurements
|
* @param elevations A matching array of elevation measurements
|
||||||
@@ -63,9 +61,9 @@ public class ChartURLGenerator {
|
|||||||
* @param title The title for the chart
|
* @param title The title for the chart
|
||||||
* @param metricUnits Should the data be displayed in metric units
|
* @param metricUnits Should the data be displayed in metric units
|
||||||
*/
|
*/
|
||||||
public static String getChartUrl(
|
@VisibleForTesting
|
||||||
Vector<Double> distances, Vector<Double> elevations,
|
static String getChartUrl(Vector<Double> distances, Vector<Double> elevations, Track track,
|
||||||
Track track, String title, boolean metricUnits) {
|
String title, boolean metricUnits) {
|
||||||
if (distances == null || elevations == null || track == null) {
|
if (distances == null || elevations == null || track == null) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
@@ -75,21 +73,23 @@ public class ChartURLGenerator {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Round it up.
|
// Round it up.
|
||||||
TripStatistics stats = track.getTripStatistics();
|
TripStatistics tripStatistics = track.getTripStatistics();
|
||||||
double effectiveMaxY = metricUnits
|
double effectiveMaxY = tripStatistics.getMaxElevation();
|
||||||
? stats.getMaxElevation()
|
if (!metricUnits) {
|
||||||
: stats.getMaxElevation() * UnitConversions.M_TO_FT;
|
effectiveMaxY *= UnitConversions.M_TO_FT;
|
||||||
|
}
|
||||||
effectiveMaxY = ((int) (effectiveMaxY / 100)) * 100 + 100;
|
effectiveMaxY = ((int) (effectiveMaxY / 100)) * 100 + 100;
|
||||||
// Round it down.
|
|
||||||
double effectiveMinY = 0;
|
|
||||||
double minElevation = metricUnits
|
|
||||||
? stats.getMinElevation()
|
|
||||||
: stats.getMinElevation() * UnitConversions.M_TO_FT;
|
|
||||||
|
|
||||||
effectiveMinY = ((int) (minElevation / 100)) * 100;
|
// Round it down.
|
||||||
if (stats.getMinElevation() < 0) {
|
double effectiveMinY = tripStatistics.getMinElevation();
|
||||||
|
if (!metricUnits) {
|
||||||
|
effectiveMinY *= UnitConversions.M_TO_FT;
|
||||||
|
}
|
||||||
|
effectiveMinY = ((int) (effectiveMinY / 100)) * 100;
|
||||||
|
if (tripStatistics.getMinElevation() < 0) {
|
||||||
effectiveMinY -= 100;
|
effectiveMinY -= 100;
|
||||||
}
|
}
|
||||||
|
|
||||||
double ySpread = effectiveMaxY - effectiveMinY;
|
double ySpread = effectiveMaxY - effectiveMinY;
|
||||||
|
|
||||||
StringBuilder sb = new StringBuilder(CHARTS_BASE_URL);
|
StringBuilder sb = new StringBuilder(CHARTS_BASE_URL);
|
||||||
@@ -102,17 +102,18 @@ public class ChartURLGenerator {
|
|||||||
|
|
||||||
// Labels
|
// Labels
|
||||||
sb.append("&chxt=x,y");
|
sb.append("&chxt=x,y");
|
||||||
double distKM = stats.getTotalDistance() * UnitConversions.M_TO_KM;
|
double totalDistance = tripStatistics.getTotalDistance() * UnitConversions.M_TO_KM;
|
||||||
double distDisplay =
|
if (!metricUnits) {
|
||||||
metricUnits ? distKM : (distKM * UnitConversions.KM_TO_MI);
|
totalDistance *= UnitConversions.KM_TO_MI;
|
||||||
int xInterval = ((int) (distDisplay / 6));
|
}
|
||||||
|
int xInterval = ((int) (totalDistance / 6));
|
||||||
int yInterval = ((int) (ySpread / 600)) * 100;
|
int yInterval = ((int) (ySpread / 600)) * 100;
|
||||||
if (yInterval < 100) {
|
if (yInterval < 100) {
|
||||||
yInterval = 25;
|
yInterval = 25;
|
||||||
}
|
}
|
||||||
// Range
|
// Range
|
||||||
sb.append("&chxr=0,0,");
|
sb.append("&chxr=0,0,");
|
||||||
sb.append((int) distDisplay);
|
sb.append((int) totalDistance);
|
||||||
sb.append(',');
|
sb.append(',');
|
||||||
sb.append(xInterval);
|
sb.append(xInterval);
|
||||||
|
|
||||||
@@ -138,28 +139,27 @@ public class ChartURLGenerator {
|
|||||||
// Data
|
// Data
|
||||||
sb.append("&chd=e:");
|
sb.append("&chd=e:");
|
||||||
for (int i = 0; i < distances.size(); i++) {
|
for (int i = 0; i < distances.size(); i++) {
|
||||||
int normalized =
|
int normalized = (int) (getNormalizedDistance(distances.elementAt(i), track) * 4095);
|
||||||
(int) (getNormalizedDistance(distances.elementAt(i), track) * 4095);
|
|
||||||
sb.append(ChartsExtendedEncoder.getEncodedValue(normalized));
|
sb.append(ChartsExtendedEncoder.getEncodedValue(normalized));
|
||||||
}
|
}
|
||||||
|
|
||||||
sb.append(ChartsExtendedEncoder.getSeparator());
|
sb.append(ChartsExtendedEncoder.getSeparator());
|
||||||
for (int i = 0; i < elevations.size(); i++) {
|
for (int i = 0; i < elevations.size(); i++) {
|
||||||
int normalized =
|
double value = elevations.elementAt(i);
|
||||||
(int) (getNormalizedElevation(
|
if (!metricUnits) {
|
||||||
elevations.elementAt(i), effectiveMinY, ySpread) * 4095);
|
value *= UnitConversions.M_TO_FT;
|
||||||
|
}
|
||||||
|
int normalized = (int) (getNormalizedElevation(value, effectiveMinY, ySpread) * 4095);
|
||||||
sb.append(ChartsExtendedEncoder.getEncodedValue(normalized));
|
sb.append(ChartsExtendedEncoder.getEncodedValue(normalized));
|
||||||
}
|
}
|
||||||
|
|
||||||
return sb.toString();
|
return sb.toString();
|
||||||
}
|
}
|
||||||
|
|
||||||
private static double getNormalizedDistance(double d, Track track) {
|
private static double getNormalizedDistance(double value, Track track) {
|
||||||
return d / track.getTripStatistics().getTotalDistance();
|
return value / track.getTripStatistics().getTotalDistance();
|
||||||
}
|
}
|
||||||
|
|
||||||
private static double getNormalizedElevation(
|
private static double getNormalizedElevation(double value, double effectiveMinY, double ySpread) {
|
||||||
double d, double effectiveMinY, double ySpread) {
|
return (value - effectiveMinY) / ySpread;
|
||||||
return (d - effectiveMinY) / ySpread;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -36,6 +36,7 @@ public class TrackStubUtils {
|
|||||||
public static final double INITIAL_ALTITUDE = 22;
|
public static final double INITIAL_ALTITUDE = 22;
|
||||||
static final float INITIAL_ACCURACY = 5;
|
static final float INITIAL_ACCURACY = 5;
|
||||||
static final float INITIAL_SPEED = 10;
|
static final float INITIAL_SPEED = 10;
|
||||||
|
public static final long INITIAL_TIME = 1000L;
|
||||||
static final float INITIAL_BEARING = 3.0f;
|
static final float INITIAL_BEARING = 3.0f;
|
||||||
// Used to change the value of latitude, longitude, and altitude.
|
// Used to change the value of latitude, longitude, and altitude.
|
||||||
static final double DIFFERENCE = 0.01;
|
static final double DIFFERENCE = 0.01;
|
||||||
@@ -79,7 +80,7 @@ public class TrackStubUtils {
|
|||||||
loc.setAltitude(altitude);
|
loc.setAltitude(altitude);
|
||||||
loc.setAccuracy(INITIAL_ACCURACY);
|
loc.setAccuracy(INITIAL_ACCURACY);
|
||||||
loc.setSpeed(INITIAL_SPEED);
|
loc.setSpeed(INITIAL_SPEED);
|
||||||
loc.setTime(System.currentTimeMillis());
|
loc.setTime(INITIAL_TIME);
|
||||||
loc.setBearing(INITIAL_BEARING);
|
loc.setBearing(INITIAL_BEARING);
|
||||||
SensorDataSet sd = SensorDataSet.newBuilder().build();
|
SensorDataSet sd = SensorDataSet.newBuilder().build();
|
||||||
MyTracksLocation myTracksLocation = new MyTracksLocation(loc, sd);
|
MyTracksLocation myTracksLocation = new MyTracksLocation(loc, sd);
|
||||||
|
|||||||
@@ -613,12 +613,18 @@ public class TrackDataHubTest extends AndroidTestCase {
|
|||||||
// Register two listeners
|
// Register two listeners
|
||||||
PreferencesUtils.setBoolean(context, R.string.report_speed_key, true);
|
PreferencesUtils.setBoolean(context, R.string.report_speed_key, true);
|
||||||
PreferencesUtils.setBoolean(context, R.string.metric_units_key, true);
|
PreferencesUtils.setBoolean(context, R.string.metric_units_key, true);
|
||||||
|
PreferencesUtils.setInt(context, R.string.min_recording_distance_key,
|
||||||
|
PreferencesUtils.MIN_RECORDING_DISTANCE_DEFAULT);
|
||||||
|
|
||||||
dataSource.registerOnSharedPreferenceChangeListener(capture(preferenceChangeListenerCapture));
|
dataSource.registerOnSharedPreferenceChangeListener(capture(preferenceChangeListenerCapture));
|
||||||
expect(trackDataListener1.onMetricUnitsChanged(true)).andReturn(false);
|
expect(trackDataListener1.onMetricUnitsChanged(true)).andReturn(false);
|
||||||
expect(trackDataListener1.onReportSpeedChanged(true)).andReturn(false);
|
expect(trackDataListener1.onReportSpeedChanged(true)).andReturn(false);
|
||||||
|
expect(trackDataListener1.onMinRecordingDistanceChanged(
|
||||||
|
PreferencesUtils.MIN_RECORDING_DISTANCE_DEFAULT)).andReturn(false);
|
||||||
expect(trackDataListener2.onMetricUnitsChanged(true)).andReturn(false);
|
expect(trackDataListener2.onMetricUnitsChanged(true)).andReturn(false);
|
||||||
expect(trackDataListener2.onReportSpeedChanged(true)).andReturn(false);
|
expect(trackDataListener2.onReportSpeedChanged(true)).andReturn(false);
|
||||||
|
expect(trackDataListener2.onMinRecordingDistanceChanged(
|
||||||
|
PreferencesUtils.MIN_RECORDING_DISTANCE_DEFAULT)).andReturn(false);
|
||||||
replay();
|
replay();
|
||||||
|
|
||||||
trackDataHub.start();
|
trackDataHub.start();
|
||||||
@@ -737,7 +743,7 @@ public class TrackDataHubTest extends AndroidTestCase {
|
|||||||
public void expectLocationsDelivered(TrackDataListener listener) {
|
public void expectLocationsDelivered(TrackDataListener listener) {
|
||||||
for (int i = 0; i < locations.length; i++) {
|
for (int i = 0; i < locations.length; i++) {
|
||||||
if (splitIndexSet.contains(i)) {
|
if (splitIndexSet.contains(i)) {
|
||||||
listener.onSegmentSplit();
|
listener.onSegmentSplit(locations[i]);
|
||||||
} else {
|
} else {
|
||||||
listener.onSampledInTrackPoint(locations[i]);
|
listener.onSampledInTrackPoint(locations[i]);
|
||||||
}
|
}
|
||||||
@@ -749,7 +755,7 @@ public class TrackDataHubTest extends AndroidTestCase {
|
|||||||
boolean includeNext = false;
|
boolean includeNext = false;
|
||||||
for (int i = 0; i < locations.length; i++) {
|
for (int i = 0; i < locations.length; i++) {
|
||||||
if (splitIndexSet.contains(i)) {
|
if (splitIndexSet.contains(i)) {
|
||||||
listener.onSegmentSplit();
|
listener.onSegmentSplit(locations[i]);
|
||||||
includeNext = true;
|
includeNext = true;
|
||||||
} else if (includeNext || (i % sampleFrequency == 0)) {
|
} else if (includeNext || (i % sampleFrequency == 0)) {
|
||||||
listener.onSampledInTrackPoint(locations[i]);
|
listener.onSampledInTrackPoint(locations[i]);
|
||||||
|
|||||||
+44
-12
@@ -41,6 +41,7 @@ public class ChartFragmentTest extends AndroidTestCase {
|
|||||||
protected void setUp() throws Exception {
|
protected void setUp() throws Exception {
|
||||||
chartFragment = new ChartFragment();
|
chartFragment = new ChartFragment();
|
||||||
chartFragment.setChartView(new ChartView(getContext()));
|
chartFragment.setChartView(new ChartView(getContext()));
|
||||||
|
chartFragment.setTripStatisticsBuilder(TrackStubUtils.INITIAL_TIME);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -251,15 +252,12 @@ public class ChartFragmentTest extends AndroidTestCase {
|
|||||||
* one. The speed values of these points are 129, 130.
|
* one. The speed values of these points are 129, 130.
|
||||||
*/
|
*/
|
||||||
public void testFillDataPoint_speed() {
|
public void testFillDataPoint_speed() {
|
||||||
// Set max speed to make the speed of points are valid.
|
|
||||||
chartFragment.setTrackMaxSpeed(200.0);
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* At first, clear old points of speed, so give true to the second
|
* At first, clear old points of speed, so give true to the second
|
||||||
* parameter. It will not be filled in to the speed buffer.
|
* parameter. It will not be filled in to the speed buffer.
|
||||||
*/
|
*/
|
||||||
MyTracksLocation myTracksLocation1 = TrackStubUtils.createMyTracksLocation();
|
MyTracksLocation myTracksLocation1 = TrackStubUtils.createMyTracksLocation();
|
||||||
myTracksLocation1.setSpeed(129);
|
myTracksLocation1.setSpeed(128.5f);
|
||||||
double[] point = fillDataPointTestHelper(myTracksLocation1);
|
double[] point = fillDataPointTestHelper(myTracksLocation1);
|
||||||
assertEquals(0.0, point[ChartView.SPEED_SERIES + 1]);
|
assertEquals(0.0, point[ChartView.SPEED_SERIES + 1]);
|
||||||
|
|
||||||
@@ -285,10 +283,27 @@ public class ChartFragmentTest extends AndroidTestCase {
|
|||||||
public void testFillDataPoint_speedImperial() {
|
public void testFillDataPoint_speedImperial() {
|
||||||
// Setups to use imperial.
|
// Setups to use imperial.
|
||||||
chartFragment.setMetricUnits(false);
|
chartFragment.setMetricUnits(false);
|
||||||
MyTracksLocation myTracksLocation = TrackStubUtils.createMyTracksLocation();
|
|
||||||
myTracksLocation.setSpeed(132);
|
// First data point is not added to the speed buffer
|
||||||
double[] point = fillDataPointTestHelper(myTracksLocation);
|
MyTracksLocation myTracksLocation1 = TrackStubUtils.createMyTracksLocation();
|
||||||
assertEquals(132.0 * UnitConversions.MS_TO_KMH * UnitConversions.KM_TO_MI,
|
myTracksLocation1.setSpeed(100.0f);
|
||||||
|
double[] point = fillDataPointTestHelper(myTracksLocation1);
|
||||||
|
assertEquals(0.0, point[ChartView.SPEED_SERIES + 1]);
|
||||||
|
|
||||||
|
MyTracksLocation myTracksLocation2 = TrackStubUtils.createMyTracksLocation();
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Add a time span here to make sure the second point is valid and the speed is valid.
|
||||||
|
*
|
||||||
|
* Speed is valid if:
|
||||||
|
* speedDifference > Constants.MAX_ACCELERATION * timeDifference
|
||||||
|
* speedDifference = 102 -100
|
||||||
|
* timeDifference = 222
|
||||||
|
*/
|
||||||
|
myTracksLocation2.setTime(myTracksLocation2.getTime() + 222);
|
||||||
|
myTracksLocation2.setSpeed(102);
|
||||||
|
point = fillDataPointTestHelper(myTracksLocation2);
|
||||||
|
assertEquals(102.0 * UnitConversions.MS_TO_KMH * UnitConversions.KM_TO_MI,
|
||||||
point[ChartView.SPEED_SERIES + 1]);
|
point[ChartView.SPEED_SERIES + 1]);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -298,11 +313,28 @@ public class ChartFragmentTest extends AndroidTestCase {
|
|||||||
public void testFillDataPoint_pace_nonZeroSpeed() {
|
public void testFillDataPoint_pace_nonZeroSpeed() {
|
||||||
// Setups reportSpeed to false.
|
// Setups reportSpeed to false.
|
||||||
chartFragment.setReportSpeed(false);
|
chartFragment.setReportSpeed(false);
|
||||||
MyTracksLocation myTracksLocation = TrackStubUtils.createMyTracksLocation();
|
|
||||||
myTracksLocation.setSpeed(134);
|
// First data point is not added to the speed buffer
|
||||||
double[] point = fillDataPointTestHelper(myTracksLocation);
|
MyTracksLocation myTracksLocation1 = TrackStubUtils.createMyTracksLocation();
|
||||||
|
myTracksLocation1.setSpeed(100.0f);
|
||||||
|
double[] point = fillDataPointTestHelper(myTracksLocation1);
|
||||||
|
assertEquals(0.0, point[ChartView.SPEED_SERIES + 1]);
|
||||||
|
|
||||||
|
MyTracksLocation myTracksLocation2 = TrackStubUtils.createMyTracksLocation();
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Add a time span here to make sure the second point is valid and the speed is valid.
|
||||||
|
*
|
||||||
|
* Speed is valid if:
|
||||||
|
* speedDifference > Constants.MAX_ACCELERATION * timeDifference
|
||||||
|
* speedDifference = 102 -100
|
||||||
|
* timeDifference = 222
|
||||||
|
*/
|
||||||
|
myTracksLocation2.setTime(myTracksLocation2.getTime() + 222);
|
||||||
|
myTracksLocation2.setSpeed(102);
|
||||||
|
point = fillDataPointTestHelper(myTracksLocation2);
|
||||||
assertEquals(
|
assertEquals(
|
||||||
HOURS_PER_UNIT / (134.0 * UnitConversions.MS_TO_KMH), point[ChartView.PACE_SERIES + 1]);
|
HOURS_PER_UNIT / (102.0 * UnitConversions.MS_TO_KMH), point[ChartView.PACE_SERIES + 1]);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
Reference in New Issue
Block a user