forked from upstream-mirrors/OpenTracks
Merge heads
This commit is contained in:
@@ -683,11 +683,16 @@ limitations under the License.
|
||||
translation_description="On application start, message when there is no GPS on the device.">
|
||||
No GPS is found on the device.
|
||||
</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
|
||||
waiting for a better GPS signal.">
|
||||
Waiting for a better GPS signal. Make sure the sky is visible.
|
||||
</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 -->
|
||||
<string name="help_about"
|
||||
|
||||
@@ -75,6 +75,7 @@ public class TrackDataHub implements DataSourceListener {
|
||||
private boolean metricUnits;
|
||||
private boolean reportSpeed;
|
||||
private int minRequiredAccuracy;
|
||||
private int minRecordingDistance;
|
||||
|
||||
// Heading values
|
||||
private float lastHeading = 0;
|
||||
@@ -347,6 +348,20 @@ public class TrackDataHub implements DataSourceListener {
|
||||
minRequiredAccuracy = PreferencesUtils.getInt(context, R.string.min_required_accuracy_key,
|
||||
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)) {
|
||||
trackDataListener.onMetricUnitsChanged(metricUnits);
|
||||
trackDataListener.onReportSpeedChanged(reportSpeed);
|
||||
trackDataListener.onMinRecordingDistanceChanged(minRecordingDistance);
|
||||
}
|
||||
|
||||
notifySelectedTrackChanged(trackDataManager.getListeners(TrackDataType.SELECTED_TRACK));
|
||||
@@ -457,6 +473,7 @@ public class TrackDataHub implements DataSourceListener {
|
||||
if (trackDataTypes.contains(TrackDataType.PREFERENCE)) {
|
||||
trackDataListener.onMetricUnitsChanged(metricUnits);
|
||||
trackDataListener.onReportSpeedChanged(reportSpeed);
|
||||
trackDataListener.onMinRecordingDistanceChanged(minRecordingDistance);
|
||||
}
|
||||
|
||||
if (trackDataTypes.contains(TrackDataType.SELECTED_TRACK)) {
|
||||
@@ -623,7 +640,7 @@ public class TrackDataHub implements DataSourceListener {
|
||||
if (!LocationUtils.isValidLocation(location)) {
|
||||
// TODO: also include the last valid point before a split
|
||||
for (TrackDataListener trackDataListener : sampledInListeners) {
|
||||
trackDataListener.onSegmentSplit();
|
||||
trackDataListener.onSegmentSplit(location);
|
||||
includeNextPoint = true;
|
||||
}
|
||||
} else {
|
||||
|
||||
@@ -96,12 +96,13 @@ public interface TrackDataListener {
|
||||
/**
|
||||
* 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
|
||||
* batch of calls to {@link #onSampledInTrackPoint(Location)},
|
||||
* {@link #onSampledOutTrackPoint(Location)} and {@link #onSegmentSplit()}.
|
||||
* {@link #onSampledOutTrackPoint(Location)} and
|
||||
* {@link #onSegmentSplit(Location)}.
|
||||
*/
|
||||
public void onNewTrackPointsDone();
|
||||
|
||||
@@ -139,4 +140,12 @@ public interface TrackDataListener {
|
||||
* @return true to reload all the data, false otherwise.
|
||||
*/
|
||||
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;
|
||||
|
||||
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.content.MyTracksLocation;
|
||||
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.TrackDataType;
|
||||
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.util.LocationUtils;
|
||||
import com.google.android.apps.mytracks.util.PreferencesUtils;
|
||||
@@ -57,27 +56,21 @@ public class ChartFragment extends Fragment implements TrackDataListener {
|
||||
|
||||
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 TrackDataHub trackDataHub;
|
||||
|
||||
// Stats gathered from the received data
|
||||
private double totalDistance = 0.0;
|
||||
private long startTime = -1L;
|
||||
private Location lastLocation = null;
|
||||
private double trackMaxSpeed = 0.0;
|
||||
private TripStatisticsBuilder tripStatisticsBuilder;
|
||||
private long startTime;
|
||||
|
||||
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
|
||||
private boolean metricUnits = true;
|
||||
private boolean reportSpeed = 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
|
||||
private ChartView chartView;
|
||||
@@ -88,7 +81,7 @@ public class ChartFragment extends Fragment implements TrackDataListener {
|
||||
* appropriate and redraw.
|
||||
*/
|
||||
private final Runnable updateChart = new Runnable() {
|
||||
@Override
|
||||
@Override
|
||||
public void run() {
|
||||
if (trackDataHub == null) {
|
||||
return;
|
||||
@@ -118,13 +111,13 @@ public class ChartFragment extends Fragment implements TrackDataListener {
|
||||
View view = inflater.inflate(R.layout.chart, container, false);
|
||||
zoomControls = (ZoomControls) view.findViewById(R.id.chart_zoom_controls);
|
||||
zoomControls.setOnZoomInClickListener(new View.OnClickListener() {
|
||||
@Override
|
||||
@Override
|
||||
public void onClick(View v) {
|
||||
zoomIn();
|
||||
}
|
||||
});
|
||||
zoomControls.setOnZoomOutClickListener(new View.OnClickListener() {
|
||||
@Override
|
||||
@Override
|
||||
public void onClick(View v) {
|
||||
zoomOut();
|
||||
}
|
||||
@@ -186,26 +179,20 @@ public class ChartFragment extends Fragment implements TrackDataListener {
|
||||
@Override
|
||||
public void onTrackUpdated(Track track) {
|
||||
if (track == null || track.getTripStatistics() == null) {
|
||||
trackMaxSpeed = 0.0;
|
||||
startTime = -1L;
|
||||
return;
|
||||
}
|
||||
trackMaxSpeed = track.getTripStatistics().getMaxSpeed();
|
||||
startTime = track.getTripStatistics().getStartTime();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void clearTrackPoints() {
|
||||
totalDistance = 0.0;
|
||||
startTime = -1L;
|
||||
lastLocation = null;
|
||||
|
||||
elevationBuffer.reset();
|
||||
speedBuffer.reset();
|
||||
tripStatisticsBuilder = startTime != -1L ? new TripStatisticsBuilder(startTime) : null;
|
||||
pendingPoints.clear();
|
||||
|
||||
chartView.reset();
|
||||
|
||||
getActivity().runOnUiThread(new Runnable() {
|
||||
@Override
|
||||
@Override
|
||||
public void run() {
|
||||
chartView.resetScroll();
|
||||
}
|
||||
@@ -214,24 +201,19 @@ public class ChartFragment extends Fragment implements TrackDataListener {
|
||||
|
||||
@Override
|
||||
public void onSampledInTrackPoint(Location location) {
|
||||
if (LocationUtils.isValidLocation(location)) {
|
||||
double[] data = new double[ChartView.NUM_SERIES + 1];
|
||||
fillDataPoint(location, data);
|
||||
pendingPoints.add(data);
|
||||
}
|
||||
double[] data = new double[ChartView.NUM_SERIES + 1];
|
||||
fillDataPoint(location, data);
|
||||
pendingPoints.add(data);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onSampledOutTrackPoint(Location location) {
|
||||
if (LocationUtils.isValidLocation(location)) {
|
||||
// Still account for the point in the smoothing buffers.
|
||||
fillDataPoint(location, null);
|
||||
}
|
||||
fillDataPoint(location, null);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onSegmentSplit() {
|
||||
// Do nothing.
|
||||
public void onSegmentSplit(Location location) {
|
||||
fillDataPoint(location, null);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -266,7 +248,7 @@ public class ChartFragment extends Fragment implements TrackDataListener {
|
||||
metricUnits = metric;
|
||||
chartView.setMetricUnits(metricUnits);
|
||||
getActivity().runOnUiThread(new Runnable() {
|
||||
@Override
|
||||
@Override
|
||||
public void run() {
|
||||
chartView.requestLayout();
|
||||
}
|
||||
@@ -286,7 +268,7 @@ public class ChartFragment extends Fragment implements TrackDataListener {
|
||||
setSeriesEnabled(ChartView.SPEED_SERIES, chartShowSpeed && reportSpeed);
|
||||
setSeriesEnabled(ChartView.PACE_SERIES, chartShowSpeed && !reportSpeed);
|
||||
getActivity().runOnUiThread(new Runnable() {
|
||||
@Override
|
||||
@Override
|
||||
public void run() {
|
||||
chartView.requestLayout();
|
||||
}
|
||||
@@ -294,6 +276,15 @@ public class ChartFragment extends Fragment implements TrackDataListener {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onMinRecordingDistanceChanged(int value) {
|
||||
if (minRecordingDistance == value) {
|
||||
return false;
|
||||
}
|
||||
minRecordingDistance = value;
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks the chart settings.
|
||||
*/
|
||||
@@ -354,17 +345,14 @@ public class ChartFragment extends Fragment implements TrackDataListener {
|
||||
}
|
||||
|
||||
/**
|
||||
* Resumes the trackDataHub. Needs to be synchronized because trackDataHub can be
|
||||
* accessed by multiple threads.
|
||||
* Resumes the trackDataHub. Needs to be synchronized because trackDataHub can
|
||||
* be accessed by multiple threads.
|
||||
*/
|
||||
private synchronized void resumeTrackDataHub() {
|
||||
trackDataHub = ((TrackDetailActivity) getActivity()).getTrackDataHub();
|
||||
trackDataHub.registerTrackDataListener(this, EnumSet.of(
|
||||
TrackDataType.SELECTED_TRACK,
|
||||
TrackDataType.TRACKS_TABLE,
|
||||
TrackDataType.WAYPOINTS_TABLE,
|
||||
TrackDataType.SAMPLED_IN_TRACK_POINTS_TABLE,
|
||||
TrackDataType.SAMPLED_OUT_TRACK_POINTS_TABLE,
|
||||
trackDataHub.registerTrackDataListener(this, EnumSet.of(TrackDataType.SELECTED_TRACK,
|
||||
TrackDataType.TRACKS_TABLE, TrackDataType.WAYPOINTS_TABLE,
|
||||
TrackDataType.SAMPLED_IN_TRACK_POINTS_TABLE, TrackDataType.SAMPLED_OUT_TRACK_POINTS_TABLE,
|
||||
TrackDataType.PREFERENCE));
|
||||
}
|
||||
|
||||
@@ -386,8 +374,8 @@ public class ChartFragment extends Fragment implements TrackDataListener {
|
||||
}
|
||||
|
||||
/**
|
||||
* Reloads the trackDataHub. Needs to be synchronized because trackDataHub can be
|
||||
* accessed by multiple threads.
|
||||
* Reloads the trackDataHub. Needs to be synchronized because trackDataHub can
|
||||
* be accessed by multiple threads.
|
||||
*/
|
||||
private synchronized void reloadTrackDataHub() {
|
||||
if (trackDataHub != null) {
|
||||
@@ -436,43 +424,30 @@ public class ChartFragment extends Fragment implements TrackDataListener {
|
||||
double cadence = 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;
|
||||
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();
|
||||
}
|
||||
timeOrDistance = totalDistance;
|
||||
} else {
|
||||
if (startTime == -1L) {
|
||||
startTime = location.getTime();
|
||||
|
||||
elevation = tripStatisticsBuilder.getSmoothedElevation();
|
||||
if (!metricUnits) {
|
||||
elevation *= UnitConversions.M_TO_FT;
|
||||
}
|
||||
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());
|
||||
speed = tripStatisticsBuilder.getSmoothedSpeed() * UnitConversions.MS_TO_KMH;
|
||||
if (!metricUnits) {
|
||||
speed *= UnitConversions.KM_TO_MI;
|
||||
}
|
||||
} else if (TripStatisticsBuilder.isValidSpeed(location.getTime(), location.getSpeed(),
|
||||
lastLocation.getTime(), lastLocation.getSpeed(), speedBuffer)
|
||||
&& (location.getSpeed() <= trackMaxSpeed)) {
|
||||
speedBuffer.setNext(location.getSpeed());
|
||||
pace = speed == 0 ? 0.0 : 60.0 / speed;
|
||||
}
|
||||
speed = speedBuffer.getAverage() * UnitConversions.MS_TO_KMH;
|
||||
if (!metricUnits) {
|
||||
speed *= UnitConversions.KM_TO_MI;
|
||||
}
|
||||
pace = speed == 0 ? 0.0 : 60.0 / speed;
|
||||
|
||||
if (location instanceof MyTracksLocation
|
||||
&& ((MyTracksLocation) location).getSensorDataSet() != null) {
|
||||
SensorDataSet sensorDataSet = ((MyTracksLocation) location).getSensorDataSet();
|
||||
@@ -502,7 +477,6 @@ public class ChartFragment extends Fragment implements TrackDataListener {
|
||||
data[5] = cadence;
|
||||
data[6] = power;
|
||||
}
|
||||
lastLocation = location;
|
||||
}
|
||||
|
||||
@VisibleForTesting
|
||||
@@ -511,13 +485,13 @@ public class ChartFragment extends Fragment implements TrackDataListener {
|
||||
}
|
||||
|
||||
@VisibleForTesting
|
||||
void setChartView(ChartView view) {
|
||||
chartView = view;
|
||||
void setTripStatisticsBuilder(long time) {
|
||||
tripStatisticsBuilder = new TripStatisticsBuilder(time);
|
||||
}
|
||||
|
||||
@VisibleForTesting
|
||||
void setTrackMaxSpeed(double value) {
|
||||
trackMaxSpeed = value;
|
||||
void setChartView(ChartView view) {
|
||||
chartView = view;
|
||||
}
|
||||
|
||||
@VisibleForTesting
|
||||
@@ -529,6 +503,7 @@ public class ChartFragment extends Fragment implements TrackDataListener {
|
||||
void setReportSpeed(boolean value) {
|
||||
reportSpeed = value;
|
||||
}
|
||||
|
||||
@VisibleForTesting
|
||||
void setChartByDistance(boolean value) {
|
||||
chartByDistance = value;
|
||||
|
||||
@@ -118,7 +118,7 @@ public class MapFragment extends Fragment
|
||||
mapView.setBuiltInZoomControls(true);
|
||||
myLocationImageButton = (ImageButton) mapViewContainer.findViewById(R.id.map_my_location);
|
||||
myLocationImageButton.setOnClickListener(new View.OnClickListener() {
|
||||
@Override
|
||||
@Override
|
||||
public void onClick(View v) {
|
||||
showMyLocation();
|
||||
}
|
||||
@@ -277,29 +277,36 @@ public class MapFragment extends Fragment
|
||||
public void onLocationStateChanged(LocationState state) {
|
||||
final String message;
|
||||
final boolean isGpsDisabled;
|
||||
switch (state) {
|
||||
case DISABLED:
|
||||
String setting = getString(
|
||||
GoogleLocationUtils.isAvailable(getActivity()) ? R.string.gps_google_location_settings
|
||||
: R.string.gps_location_access);
|
||||
message = getString(R.string.gps_disabled, setting);
|
||||
isGpsDisabled = true;
|
||||
break;
|
||||
case NO_FIX:
|
||||
case BAD_FIX:
|
||||
message = getString(R.string.gps_wait_for_signal);
|
||||
isGpsDisabled = false;
|
||||
break;
|
||||
case GOOD_FIX:
|
||||
message = null;
|
||||
isGpsDisabled = false;
|
||||
break;
|
||||
default:
|
||||
throw new IllegalArgumentException("Unexpected state: " + state);
|
||||
if (!isSelectedTrackRecording()) {
|
||||
message = null;
|
||||
isGpsDisabled = false;
|
||||
} else {
|
||||
switch (state) {
|
||||
case DISABLED:
|
||||
String setting = getString(
|
||||
GoogleLocationUtils.isAvailable(getActivity()) ? R.string.gps_google_location_settings
|
||||
: R.string.gps_location_access);
|
||||
message = getString(R.string.gps_disabled, setting);
|
||||
isGpsDisabled = true;
|
||||
break;
|
||||
case NO_FIX:
|
||||
message = getString(R.string.gps_wait_for_signal);
|
||||
isGpsDisabled = false;
|
||||
break;
|
||||
case BAD_FIX:
|
||||
message = getString(R.string.gps_wait_for_better_signal);
|
||||
isGpsDisabled = false;
|
||||
break;
|
||||
case GOOD_FIX:
|
||||
message = null;
|
||||
isGpsDisabled = false;
|
||||
break;
|
||||
default:
|
||||
throw new IllegalArgumentException("Unexpected state: " + state);
|
||||
}
|
||||
}
|
||||
|
||||
getActivity().runOnUiThread(new Runnable() {
|
||||
@Override
|
||||
@Override
|
||||
public void run() {
|
||||
if (message != null) {
|
||||
messageTextView.setText(message);
|
||||
@@ -336,7 +343,7 @@ public class MapFragment extends Fragment
|
||||
@Override
|
||||
public void onSelectedTrackChanged(final Track track) {
|
||||
getActivity().runOnUiThread(new Runnable() {
|
||||
@Override
|
||||
@Override
|
||||
public void run() {
|
||||
boolean hasTrack = track != null;
|
||||
mapOverlay.setTrackDrawingEnabled(hasTrack);
|
||||
@@ -369,18 +376,16 @@ public class MapFragment extends Fragment
|
||||
|
||||
@Override
|
||||
public void onSampledInTrackPoint(Location location) {
|
||||
if (LocationUtils.isValidLocation(location)) {
|
||||
mapOverlay.addLocation(location);
|
||||
}
|
||||
mapOverlay.addLocation(location);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onSampledOutTrackPoint(Location loc) {
|
||||
public void onSampledOutTrackPoint(Location location) {
|
||||
// We don't care.
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onSegmentSplit() {
|
||||
public void onSegmentSplit(Location location) {
|
||||
mapOverlay.addSegmentSplit();
|
||||
}
|
||||
|
||||
@@ -419,23 +424,26 @@ public class MapFragment extends Fragment
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Resumes the trackDataHub. Needs to be synchronized because trackDataHub can be
|
||||
* accessed by multiple threads.
|
||||
*/
|
||||
private synchronized void resumeTrackDataHub() {
|
||||
trackDataHub = ((TrackDetailActivity) getActivity()).getTrackDataHub();
|
||||
trackDataHub.registerTrackDataListener(this, EnumSet.of(
|
||||
TrackDataType.SELECTED_TRACK,
|
||||
TrackDataType.WAYPOINTS_TABLE,
|
||||
TrackDataType.SAMPLED_IN_TRACK_POINTS_TABLE,
|
||||
TrackDataType.LOCATION,
|
||||
TrackDataType.HEADING));
|
||||
@Override
|
||||
public boolean onMinRecordingDistanceChanged(int minRecordingDistance) {
|
||||
// We don't care.
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Pauses the trackDataHub. Needs to be synchronized because trackDataHub can be
|
||||
* accessed by multiple threads.
|
||||
* Resumes the trackDataHub. Needs to be synchronized because trackDataHub can
|
||||
* be accessed by multiple threads.
|
||||
*/
|
||||
private synchronized void resumeTrackDataHub() {
|
||||
trackDataHub = ((TrackDetailActivity) getActivity()).getTrackDataHub();
|
||||
trackDataHub.registerTrackDataListener(this, EnumSet.of(TrackDataType.SELECTED_TRACK,
|
||||
TrackDataType.WAYPOINTS_TABLE, TrackDataType.SAMPLED_IN_TRACK_POINTS_TABLE,
|
||||
TrackDataType.LOCATION, TrackDataType.HEADING));
|
||||
}
|
||||
|
||||
/**
|
||||
* Pauses the trackDataHub. Needs to be synchronized because trackDataHub can
|
||||
* be accessed by multiple threads.
|
||||
*/
|
||||
private synchronized void pauseTrackDataHub() {
|
||||
trackDataHub.unregisterTrackDataListener(this);
|
||||
@@ -443,8 +451,8 @@ public class MapFragment extends Fragment
|
||||
}
|
||||
|
||||
/**
|
||||
* Updates the trackDataHub. Needs to be synchronized because trackDataHub can be
|
||||
* accessed by multiple threads.
|
||||
* Updates the trackDataHub. Needs to be synchronized because trackDataHub can
|
||||
* be accessed by multiple threads.
|
||||
*/
|
||||
private synchronized void updateTrackDataHub() {
|
||||
if (trackDataHub != null) {
|
||||
@@ -461,7 +469,8 @@ 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
|
||||
*/
|
||||
@@ -498,8 +507,8 @@ public class MapFragment extends Fragment
|
||||
GeoPoint zoomControlBottom = mapView.getProjection().fromPixels(0, mapView.getHeight());
|
||||
GeoPoint zoomControlTop = mapView.getProjection().fromPixels(
|
||||
0, mapView.getHeight() - mapView.getZoomButtonsController().getZoomControls().getHeight());
|
||||
int zoomControlMargin = Math.abs(zoomControlTop.getLatitudeE6()
|
||||
- zoomControlBottom.getLatitudeE6());
|
||||
int zoomControlMargin = Math.abs(
|
||||
zoomControlTop.getLatitudeE6() - zoomControlBottom.getLatitudeE6());
|
||||
GeoRect geoRect = new GeoRect(mapCenter, latitudeSpan, longitudeSpan);
|
||||
geoRect.top += zoomControlMargin;
|
||||
|
||||
|
||||
@@ -186,17 +186,17 @@ public class StatsFragment extends Fragment implements TrackDataListener {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onSampledInTrackPoint(Location loc) {
|
||||
public void onSampledInTrackPoint(Location location) {
|
||||
// We don't care.
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onSampledOutTrackPoint(Location loc) {
|
||||
public void onSampledOutTrackPoint(Location location) {
|
||||
// We don't care.
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onSegmentSplit() {
|
||||
public void onSegmentSplit(Location location) {
|
||||
// We don't care.
|
||||
}
|
||||
|
||||
@@ -246,6 +246,12 @@ public class StatsFragment extends Fragment implements TrackDataListener {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onMinRecordingDistanceChanged(int minRecordingDistance) {
|
||||
// We don't care.
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Resumes the trackDataHub. Needs to be synchronized because trackDataHub can
|
||||
* be accessed by multiple threads.
|
||||
|
||||
+24
-32
@@ -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.io.sendtogoogle.AbstractSendAsyncTask;
|
||||
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.util.ApiAdapterFactory;
|
||||
import com.google.android.apps.mytracks.util.LocationUtils;
|
||||
import com.google.android.apps.mytracks.util.PreferencesUtils;
|
||||
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.api.client.googleapis.GoogleHeaders;
|
||||
import com.google.api.client.googleapis.MethodOverride;
|
||||
@@ -59,8 +57,8 @@ public class SendFusionTablesAsyncTask extends AbstractSendAsyncTask {
|
||||
@VisibleForTesting
|
||||
public static final String CONTENT_TYPE = "application/x-www-form-urlencoded";
|
||||
@VisibleForTesting
|
||||
public static final String FUSION_TABLES_BASE_URL =
|
||||
"https://www.google.com/fusiontables/api/query";
|
||||
public static final String
|
||||
FUSION_TABLES_BASE_URL = "https://www.google.com/fusiontables/api/query";
|
||||
private static final int MAX_POINTS_PER_UPLOAD = 2048;
|
||||
@VisibleForTesting
|
||||
public static final String GDATA_VERSION = "2";
|
||||
@@ -72,7 +70,8 @@ public class SendFusionTablesAsyncTask extends AbstractSendAsyncTask {
|
||||
private static final int PROGRESS_UPLOAD_WAYPOINTS = 95;
|
||||
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_END = "large_red";
|
||||
private static final String MARKER_TYPE_WAYPOINT = "large_blue";
|
||||
@@ -127,8 +126,8 @@ public class SendFusionTablesAsyncTask extends AbstractSendAsyncTask {
|
||||
currentSegment = 1;
|
||||
|
||||
try {
|
||||
authToken = AccountManager.get(context).blockingGetAuthToken(
|
||||
account, SendFusionTablesUtils.SERVICE, false);
|
||||
authToken = AccountManager.get(context)
|
||||
.blockingGetAuthToken(account, SendFusionTablesUtils.SERVICE, false);
|
||||
} catch (OperationCanceledException e) {
|
||||
Log.d(TAG, "Unable to get auth token", e);
|
||||
return retryTask();
|
||||
@@ -211,25 +210,24 @@ public class SendFusionTablesAsyncTask extends AbstractSendAsyncTask {
|
||||
private boolean uploadAllTrackPoints(Track track) {
|
||||
Cursor locationsCursor = null;
|
||||
try {
|
||||
boolean metricUnits = PreferencesUtils.getBoolean(
|
||||
context, R.string.metric_units_key, PreferencesUtils.METRIC_UNITS_DEFAULT);
|
||||
|
||||
locationsCursor = myTracksProviderUtils.getLocationsCursor(trackId, 0, -1, false);
|
||||
if (locationsCursor == null) {
|
||||
Log.d(TAG, "Location cursor is null");
|
||||
return false;
|
||||
}
|
||||
|
||||
int locationsCount = locationsCursor.getCount();
|
||||
List<Location> locations = new ArrayList<Location>(MAX_POINTS_PER_UPLOAD);
|
||||
Location lastLocation = null;
|
||||
|
||||
// For chart server, limit the number of elevation readings to 250.
|
||||
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> 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++) {
|
||||
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)) {
|
||||
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;
|
||||
}
|
||||
|
||||
@@ -280,7 +274,7 @@ public class SendFusionTablesAsyncTask extends AbstractSendAsyncTask {
|
||||
// Create an end marker
|
||||
if (lastLocation != null) {
|
||||
distances.add(tripStatisticsBuilder.getTripStatistics().getTotalDistance());
|
||||
elevations.add(elevationBuffer.getAverage());
|
||||
elevations.add(tripStatisticsBuilder.getSmoothedElevation());
|
||||
DescriptionGenerator descriptionGenerator = new DescriptionGeneratorImpl(context);
|
||||
track.setDescription(
|
||||
descriptionGenerator.generateTrackDescription(track, distances, elevations, true));
|
||||
@@ -358,8 +352,7 @@ public class SendFusionTablesAsyncTask extends AbstractSendAsyncTask {
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new row in Google Fusion Tables representing a marker as a
|
||||
* point.
|
||||
* Creates a new row in Google Fusion Tables representing a marker as a point.
|
||||
*
|
||||
* @param name the marker name
|
||||
* @param description the marker description
|
||||
@@ -367,8 +360,7 @@ public class SendFusionTablesAsyncTask extends AbstractSendAsyncTask {
|
||||
* @param type the marker type
|
||||
* @return true if success.
|
||||
*/
|
||||
private boolean createNewPoint(
|
||||
String name, String description, Location location, String type) {
|
||||
private boolean createNewPoint(String name, String description, Location location, String type) {
|
||||
String query = "INSERT INTO " + tableId + " (name,description,geometry,marker) VALUES "
|
||||
+ SendFusionTablesUtils.formatSqlValues(
|
||||
name, description, SendFusionTablesUtils.getKmlPoint(location), type);
|
||||
@@ -376,15 +368,16 @@ public class SendFusionTablesAsyncTask extends AbstractSendAsyncTask {
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new row in Google Fusion Tables representing the track as a
|
||||
* line segment.
|
||||
* Creates a new row in Google Fusion Tables representing the track as a line
|
||||
* segment.
|
||||
*
|
||||
* @param track the track
|
||||
* @return true if success.
|
||||
*/
|
||||
private boolean createNewLineString(Track track) {
|
||||
String query = "INSERT INTO " + tableId + " (name,description,geometry) VALUES "
|
||||
+ SendFusionTablesUtils.formatSqlValues(track.getName(), track.getDescription(),
|
||||
String query = "INSERT INTO " + tableId
|
||||
+ " (name,description,geometry) VALUES " + SendFusionTablesUtils.formatSqlValues(
|
||||
track.getName(), track.getDescription(),
|
||||
SendFusionTablesUtils.getKmlLineString(track.getLocations()));
|
||||
return sendQuery(query, false);
|
||||
}
|
||||
@@ -474,4 +467,3 @@ public class SendFusionTablesAsyncTask extends AbstractSendAsyncTask {
|
||||
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.sendtogoogle.AbstractSendAsyncTask;
|
||||
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.util.LocationUtils;
|
||||
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.maps.mytracks.R;
|
||||
import com.google.common.annotations.VisibleForTesting;
|
||||
@@ -67,10 +65,10 @@ import org.xmlpull.v1.XmlPullParserException;
|
||||
* @author Jimmy Shih
|
||||
*/
|
||||
public class SendMapsAsyncTask extends AbstractSendAsyncTask {
|
||||
private static final String START_ICON_URL =
|
||||
"http://maps.google.com/mapfiles/ms/micons/green-dot.png";
|
||||
private static final String END_ICON_URL =
|
||||
"http://maps.google.com/mapfiles/ms/micons/red-dot.png";
|
||||
private static final String
|
||||
START_ICON_URL = "http://maps.google.com/mapfiles/ms/micons/green-dot.png";
|
||||
private static final String
|
||||
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 PROGRESS_FETCH_MAP_ID = 5;
|
||||
@@ -97,18 +95,18 @@ public class SendMapsAsyncTask extends AbstractSendAsyncTask {
|
||||
private String mapId;
|
||||
int currentSegment;
|
||||
|
||||
public SendMapsAsyncTask(SendMapsActivity activity, long trackId, Account account,
|
||||
String chooseMapId) {
|
||||
this(activity, trackId, account, chooseMapId, MyTracksProviderUtils.Factory.get(activity
|
||||
.getApplicationContext()));
|
||||
public SendMapsAsyncTask(
|
||||
SendMapsActivity activity, long trackId, Account account, String chooseMapId) {
|
||||
this(activity, trackId, account, chooseMapId, MyTracksProviderUtils.Factory.get(
|
||||
activity.getApplicationContext()));
|
||||
}
|
||||
|
||||
/**
|
||||
* This constructor is created for test.
|
||||
*/
|
||||
@VisibleForTesting
|
||||
public SendMapsAsyncTask (
|
||||
SendMapsActivity activity, long trackId, Account account, String chooseMapId, MyTracksProviderUtils myTracksProviderUtils) {
|
||||
public SendMapsAsyncTask(SendMapsActivity activity, long trackId, Account account,
|
||||
String chooseMapId, MyTracksProviderUtils myTracksProviderUtils) {
|
||||
super(activity);
|
||||
this.trackId = trackId;
|
||||
this.account = account;
|
||||
@@ -157,8 +155,8 @@ public class SendMapsAsyncTask extends AbstractSendAsyncTask {
|
||||
|
||||
// Get auth token
|
||||
try {
|
||||
authToken = AccountManager.get(context).blockingGetAuthToken(
|
||||
account, MapsConstants.SERVICE_NAME, false);
|
||||
authToken = AccountManager.get(context)
|
||||
.blockingGetAuthToken(account, MapsConstants.SERVICE_NAME, false);
|
||||
} catch (OperationCanceledException e) {
|
||||
Log.d(TAG, "Unable to get auth token", e);
|
||||
return retryTask();
|
||||
@@ -208,8 +206,8 @@ public class SendMapsAsyncTask extends AbstractSendAsyncTask {
|
||||
}
|
||||
|
||||
/**
|
||||
* Fetches the {@link SendMapsAsyncTask#mapId} instance variable for
|
||||
* sending a track to Google Maps.
|
||||
* Fetches the {@link SendMapsAsyncTask#mapId} instance variable for sending a
|
||||
* track to Google Maps.
|
||||
*
|
||||
* @param track the Track
|
||||
* @return true if able to fetch the mapId variable.
|
||||
@@ -255,25 +253,24 @@ public class SendMapsAsyncTask extends AbstractSendAsyncTask {
|
||||
boolean uploadAllTrackPoints(Track track) {
|
||||
Cursor locationsCursor = null;
|
||||
try {
|
||||
boolean metricUnits = PreferencesUtils.getBoolean(
|
||||
context, R.string.metric_units_key, PreferencesUtils.METRIC_UNITS_DEFAULT);
|
||||
|
||||
locationsCursor = myTracksProviderUtils.getLocationsCursor(trackId, 0, -1, false);
|
||||
if (locationsCursor == null) {
|
||||
Log.d(TAG, "Location cursor is null");
|
||||
return false;
|
||||
}
|
||||
|
||||
int locationsCount = locationsCursor.getCount();
|
||||
List<Location> locations = new ArrayList<Location>(MAX_POINTS_PER_UPLOAD);
|
||||
Location lastLocation = null;
|
||||
|
||||
// For chart server, limit the number of elevation readings to 250.
|
||||
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> 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++) {
|
||||
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)) {
|
||||
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;
|
||||
}
|
||||
|
||||
@@ -324,9 +317,10 @@ public class SendMapsAsyncTask extends AbstractSendAsyncTask {
|
||||
// Create an end marker
|
||||
if (lastLocation != null) {
|
||||
distances.add(tripStatisticsBuilder.getTripStatistics().getTotalDistance());
|
||||
elevations.add(elevationBuffer.getAverage());
|
||||
|
||||
track.setDescription(getTrackDescription(track, distances, elevations));
|
||||
elevations.add(tripStatisticsBuilder.getSmoothedElevation());
|
||||
DescriptionGenerator descriptionGenerator = new DescriptionGeneratorImpl(context);
|
||||
track.setDescription(
|
||||
descriptionGenerator.generateTrackDescription(track, distances, elevations, true));
|
||||
if (!uploadMarker(context.getString(R.string.marker_label_end, track.getName()),
|
||||
track.getDescription(), END_ICON_URL, lastLocation)) {
|
||||
Log.d(TAG, "Unable to create an end marker");
|
||||
@@ -341,22 +335,6 @@ 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.
|
||||
*
|
||||
@@ -396,8 +374,7 @@ public class SendMapsAsyncTask extends AbstractSendAsyncTask {
|
||||
* @return true if success.
|
||||
*/
|
||||
@VisibleForTesting
|
||||
boolean uploadMarker(
|
||||
String title, String description, String iconUrl, Location location) {
|
||||
boolean uploadMarker(String title, String description, String iconUrl, Location location) {
|
||||
if (isCancelled()) {
|
||||
return false;
|
||||
}
|
||||
@@ -506,7 +483,6 @@ public class SendMapsAsyncTask extends AbstractSendAsyncTask {
|
||||
publishProgress(getPercentage(uploaded, total));
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Count the percentage of the number of locations uploaded.
|
||||
*
|
||||
|
||||
@@ -285,7 +285,7 @@ public class TrackRecordingService extends Service {
|
||||
// Require announcementExecutor and splitExecutor to be created.
|
||||
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
|
||||
@@ -843,7 +843,7 @@ public class TrackRecordingService extends Service {
|
||||
|
||||
double distanceToLastTrackLocation = location.distanceTo(lastTrackLocation);
|
||||
if (distanceToLastTrackLocation < minRecordingDistance && sensorDataSet == null) {
|
||||
// skip
|
||||
Log.d(TAG, "Not recording location due to min recording distance.");
|
||||
} else if (distanceToLastTrackLocation > maxRecordingDistance) {
|
||||
insertLocation(track, lastLocation, lastTrackLocation);
|
||||
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) {
|
||||
if (location == null) {
|
||||
Log.w(TAG, "Ignore insertLocation. loation is null.");
|
||||
return;
|
||||
}
|
||||
if (location.equals(lastRecordedLocation)) {
|
||||
Log.w(TAG, "Ignore insertLocation. loation same as last recorded location.");
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
@@ -145,11 +145,14 @@ public class TripStatisticsBuilder {
|
||||
* is noisy so the smoothed elevation is better than the raw elevation for
|
||||
* many tasks.
|
||||
*/
|
||||
@VisibleForTesting
|
||||
double getSmoothedElevation() {
|
||||
public double getSmoothedElevation() {
|
||||
return elevationBuffer.getAverage();
|
||||
}
|
||||
|
||||
public double getSmoothedSpeed() {
|
||||
return speedBuffer.getAverage();
|
||||
}
|
||||
|
||||
/**
|
||||
* Updates a speed reading. Assumes the user is moving.
|
||||
*
|
||||
@@ -160,7 +163,7 @@ public class TripStatisticsBuilder {
|
||||
*/
|
||||
@VisibleForTesting
|
||||
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);
|
||||
return;
|
||||
}
|
||||
@@ -168,10 +171,6 @@ public class TripStatisticsBuilder {
|
||||
if (speed > currentSegment.getMaxSpeed()) {
|
||||
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 lastLocationTime the last location time
|
||||
* @param lastLocationSpeed the last location speed
|
||||
* @param speedBuffer a buffer of speed readings
|
||||
*/
|
||||
public static boolean isValidSpeed(long time, double speed, long lastLocationTime,
|
||||
double lastLocationSpeed, DoubleBuffer speedBuffer) {
|
||||
private boolean isValidSpeed(
|
||||
long time, double speed, long lastLocationTime, double lastLocationSpeed) {
|
||||
|
||||
/*
|
||||
* 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.stats.TripStatistics;
|
||||
import com.google.android.maps.mytracks.R;
|
||||
import com.google.common.annotations.VisibleForTesting;
|
||||
|
||||
import android.content.Context;
|
||||
|
||||
@@ -31,11 +32,9 @@ import java.util.Vector;
|
||||
*/
|
||||
public class ChartURLGenerator {
|
||||
|
||||
private static final String CHARTS_BASE_URL =
|
||||
"http://chart.apis.google.com/chart?";
|
||||
private static final String CHARTS_BASE_URL = "http://chart.apis.google.com/chart?";
|
||||
|
||||
private ChartURLGenerator() {
|
||||
}
|
||||
private ChartURLGenerator() {}
|
||||
|
||||
/**
|
||||
* Gets a chart of a track.
|
||||
@@ -54,8 +53,7 @@ public class ChartURLGenerator {
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets a chart of a track.
|
||||
* This form is for testing without contexts.
|
||||
* Gets a chart of a track. This form is for testing without contexts.
|
||||
*
|
||||
* @param distances An array of distance measurements
|
||||
* @param elevations A matching array of elevation measurements
|
||||
@@ -63,9 +61,9 @@ public class ChartURLGenerator {
|
||||
* @param title The title for the chart
|
||||
* @param metricUnits Should the data be displayed in metric units
|
||||
*/
|
||||
public static String getChartUrl(
|
||||
Vector<Double> distances, Vector<Double> elevations,
|
||||
Track track, String title, boolean metricUnits) {
|
||||
@VisibleForTesting
|
||||
static String getChartUrl(Vector<Double> distances, Vector<Double> elevations, Track track,
|
||||
String title, boolean metricUnits) {
|
||||
if (distances == null || elevations == null || track == null) {
|
||||
return null;
|
||||
}
|
||||
@@ -75,21 +73,23 @@ public class ChartURLGenerator {
|
||||
}
|
||||
|
||||
// Round it up.
|
||||
TripStatistics stats = track.getTripStatistics();
|
||||
double effectiveMaxY = metricUnits
|
||||
? stats.getMaxElevation()
|
||||
: stats.getMaxElevation() * UnitConversions.M_TO_FT;
|
||||
TripStatistics tripStatistics = track.getTripStatistics();
|
||||
double effectiveMaxY = tripStatistics.getMaxElevation();
|
||||
if (!metricUnits) {
|
||||
effectiveMaxY *= UnitConversions.M_TO_FT;
|
||||
}
|
||||
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;
|
||||
if (stats.getMinElevation() < 0) {
|
||||
// Round it down.
|
||||
double effectiveMinY = tripStatistics.getMinElevation();
|
||||
if (!metricUnits) {
|
||||
effectiveMinY *= UnitConversions.M_TO_FT;
|
||||
}
|
||||
effectiveMinY = ((int) (effectiveMinY / 100)) * 100;
|
||||
if (tripStatistics.getMinElevation() < 0) {
|
||||
effectiveMinY -= 100;
|
||||
}
|
||||
|
||||
double ySpread = effectiveMaxY - effectiveMinY;
|
||||
|
||||
StringBuilder sb = new StringBuilder(CHARTS_BASE_URL);
|
||||
@@ -102,17 +102,18 @@ public class ChartURLGenerator {
|
||||
|
||||
// Labels
|
||||
sb.append("&chxt=x,y");
|
||||
double distKM = stats.getTotalDistance() * UnitConversions.M_TO_KM;
|
||||
double distDisplay =
|
||||
metricUnits ? distKM : (distKM * UnitConversions.KM_TO_MI);
|
||||
int xInterval = ((int) (distDisplay / 6));
|
||||
double totalDistance = tripStatistics.getTotalDistance() * UnitConversions.M_TO_KM;
|
||||
if (!metricUnits) {
|
||||
totalDistance *= UnitConversions.KM_TO_MI;
|
||||
}
|
||||
int xInterval = ((int) (totalDistance / 6));
|
||||
int yInterval = ((int) (ySpread / 600)) * 100;
|
||||
if (yInterval < 100) {
|
||||
yInterval = 25;
|
||||
}
|
||||
// Range
|
||||
sb.append("&chxr=0,0,");
|
||||
sb.append((int) distDisplay);
|
||||
sb.append((int) totalDistance);
|
||||
sb.append(',');
|
||||
sb.append(xInterval);
|
||||
|
||||
@@ -138,28 +139,27 @@ public class ChartURLGenerator {
|
||||
// Data
|
||||
sb.append("&chd=e:");
|
||||
for (int i = 0; i < distances.size(); i++) {
|
||||
int normalized =
|
||||
(int) (getNormalizedDistance(distances.elementAt(i), track) * 4095);
|
||||
int normalized = (int) (getNormalizedDistance(distances.elementAt(i), track) * 4095);
|
||||
sb.append(ChartsExtendedEncoder.getEncodedValue(normalized));
|
||||
}
|
||||
|
||||
sb.append(ChartsExtendedEncoder.getSeparator());
|
||||
for (int i = 0; i < elevations.size(); i++) {
|
||||
int normalized =
|
||||
(int) (getNormalizedElevation(
|
||||
elevations.elementAt(i), effectiveMinY, ySpread) * 4095);
|
||||
double value = elevations.elementAt(i);
|
||||
if (!metricUnits) {
|
||||
value *= UnitConversions.M_TO_FT;
|
||||
}
|
||||
int normalized = (int) (getNormalizedElevation(value, effectiveMinY, ySpread) * 4095);
|
||||
sb.append(ChartsExtendedEncoder.getEncodedValue(normalized));
|
||||
}
|
||||
|
||||
return sb.toString();
|
||||
}
|
||||
|
||||
private static double getNormalizedDistance(double d, Track track) {
|
||||
return d / track.getTripStatistics().getTotalDistance();
|
||||
private static double getNormalizedDistance(double value, Track track) {
|
||||
return value / track.getTripStatistics().getTotalDistance();
|
||||
}
|
||||
|
||||
private static double getNormalizedElevation(
|
||||
double d, double effectiveMinY, double ySpread) {
|
||||
return (d - effectiveMinY) / ySpread;
|
||||
private static double getNormalizedElevation(double value, double effectiveMinY, double ySpread) {
|
||||
return (value - effectiveMinY) / ySpread;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -36,6 +36,7 @@ public class TrackStubUtils {
|
||||
public static final double INITIAL_ALTITUDE = 22;
|
||||
static final float INITIAL_ACCURACY = 5;
|
||||
static final float INITIAL_SPEED = 10;
|
||||
public static final long INITIAL_TIME = 1000L;
|
||||
static final float INITIAL_BEARING = 3.0f;
|
||||
// Used to change the value of latitude, longitude, and altitude.
|
||||
static final double DIFFERENCE = 0.01;
|
||||
@@ -79,7 +80,7 @@ public class TrackStubUtils {
|
||||
loc.setAltitude(altitude);
|
||||
loc.setAccuracy(INITIAL_ACCURACY);
|
||||
loc.setSpeed(INITIAL_SPEED);
|
||||
loc.setTime(System.currentTimeMillis());
|
||||
loc.setTime(INITIAL_TIME);
|
||||
loc.setBearing(INITIAL_BEARING);
|
||||
SensorDataSet sd = SensorDataSet.newBuilder().build();
|
||||
MyTracksLocation myTracksLocation = new MyTracksLocation(loc, sd);
|
||||
|
||||
@@ -613,12 +613,18 @@ public class TrackDataHubTest extends AndroidTestCase {
|
||||
// Register two listeners
|
||||
PreferencesUtils.setBoolean(context, R.string.report_speed_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));
|
||||
expect(trackDataListener1.onMetricUnitsChanged(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.onReportSpeedChanged(true)).andReturn(false);
|
||||
expect(trackDataListener2.onMinRecordingDistanceChanged(
|
||||
PreferencesUtils.MIN_RECORDING_DISTANCE_DEFAULT)).andReturn(false);
|
||||
replay();
|
||||
|
||||
trackDataHub.start();
|
||||
@@ -737,7 +743,7 @@ public class TrackDataHubTest extends AndroidTestCase {
|
||||
public void expectLocationsDelivered(TrackDataListener listener) {
|
||||
for (int i = 0; i < locations.length; i++) {
|
||||
if (splitIndexSet.contains(i)) {
|
||||
listener.onSegmentSplit();
|
||||
listener.onSegmentSplit(locations[i]);
|
||||
} else {
|
||||
listener.onSampledInTrackPoint(locations[i]);
|
||||
}
|
||||
@@ -749,7 +755,7 @@ public class TrackDataHubTest extends AndroidTestCase {
|
||||
boolean includeNext = false;
|
||||
for (int i = 0; i < locations.length; i++) {
|
||||
if (splitIndexSet.contains(i)) {
|
||||
listener.onSegmentSplit();
|
||||
listener.onSegmentSplit(locations[i]);
|
||||
includeNext = true;
|
||||
} else if (includeNext || (i % sampleFrequency == 0)) {
|
||||
listener.onSampledInTrackPoint(locations[i]);
|
||||
|
||||
+44
-12
@@ -41,6 +41,7 @@ public class ChartFragmentTest extends AndroidTestCase {
|
||||
protected void setUp() throws Exception {
|
||||
chartFragment = new ChartFragment();
|
||||
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.
|
||||
*/
|
||||
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
|
||||
* parameter. It will not be filled in to the speed buffer.
|
||||
*/
|
||||
MyTracksLocation myTracksLocation1 = TrackStubUtils.createMyTracksLocation();
|
||||
myTracksLocation1.setSpeed(129);
|
||||
myTracksLocation1.setSpeed(128.5f);
|
||||
double[] point = fillDataPointTestHelper(myTracksLocation1);
|
||||
assertEquals(0.0, point[ChartView.SPEED_SERIES + 1]);
|
||||
|
||||
@@ -285,10 +283,27 @@ public class ChartFragmentTest extends AndroidTestCase {
|
||||
public void testFillDataPoint_speedImperial() {
|
||||
// Setups to use imperial.
|
||||
chartFragment.setMetricUnits(false);
|
||||
MyTracksLocation myTracksLocation = TrackStubUtils.createMyTracksLocation();
|
||||
myTracksLocation.setSpeed(132);
|
||||
double[] point = fillDataPointTestHelper(myTracksLocation);
|
||||
assertEquals(132.0 * UnitConversions.MS_TO_KMH * UnitConversions.KM_TO_MI,
|
||||
|
||||
// First data point is not added to the speed buffer
|
||||
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(102.0 * UnitConversions.MS_TO_KMH * UnitConversions.KM_TO_MI,
|
||||
point[ChartView.SPEED_SERIES + 1]);
|
||||
}
|
||||
|
||||
@@ -298,11 +313,28 @@ public class ChartFragmentTest extends AndroidTestCase {
|
||||
public void testFillDataPoint_pace_nonZeroSpeed() {
|
||||
// Setups reportSpeed to false.
|
||||
chartFragment.setReportSpeed(false);
|
||||
MyTracksLocation myTracksLocation = TrackStubUtils.createMyTracksLocation();
|
||||
myTracksLocation.setSpeed(134);
|
||||
double[] point = fillDataPointTestHelper(myTracksLocation);
|
||||
|
||||
// First data point is not added to the speed buffer
|
||||
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(
|
||||
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