diff --git a/MyTracks/src/com/google/android/apps/mytracks/TrackDetailActivity.java b/MyTracks/src/com/google/android/apps/mytracks/TrackDetailActivity.java
index 5359717cc..418c78d95 100644
--- a/MyTracks/src/com/google/android/apps/mytracks/TrackDetailActivity.java
+++ b/MyTracks/src/com/google/android/apps/mytracks/TrackDetailActivity.java
@@ -241,7 +241,8 @@ public class TrackDetailActivity extends AbstractMyTracksActivity implements Del
splitFrequencyMenuItem = menu.findItem(R.id.track_detail_split_frequency);
sendGoogleMenuItem = menu.findItem(R.id.track_detail_send_google);
saveMenuItem = menu.findItem(R.id.track_detail_save);
-
+
+ updateMenuItems(trackId == recordingTrackId);
return true;
}
diff --git a/MyTracks/src/com/google/android/apps/mytracks/io/file/GpxImporter.java b/MyTracks/src/com/google/android/apps/mytracks/io/file/GpxImporter.java
index 2bfa6c1a2..8059b1057 100644
--- a/MyTracks/src/com/google/android/apps/mytracks/io/file/GpxImporter.java
+++ b/MyTracks/src/com/google/android/apps/mytracks/io/file/GpxImporter.java
@@ -20,6 +20,7 @@ import com.google.android.apps.mytracks.content.MyTracksProviderUtils;
import com.google.android.apps.mytracks.content.Track;
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.StringUtils;
import android.location.Location;
@@ -300,11 +301,11 @@ public class GpxImporter extends DefaultHandler {
flushPoints();
// Calculate statistics for the imported track
- tripStatisticsBuilder.pauseAt(lastLocation.getTime());
+ tripStatisticsBuilder.updateTime(lastLocation.getTime());
track.setStopId(getLastPointId());
} else {
- tripStatisticsBuilder = new TripStatisticsBuilder(0);
- tripStatisticsBuilder.pauseAt(0);
+ tripStatisticsBuilder = new TripStatisticsBuilder(0L);
+ tripStatisticsBuilder.updateTime(0L);
}
track.setTripStatistics(tripStatisticsBuilder.getTripStatistics());
track.setNumberOfPoints(numberOfLocations);
@@ -380,7 +381,7 @@ public class GpxImporter extends DefaultHandler {
// first point did not have a time, start stats builder without it
tripStatisticsBuilder = new TripStatisticsBuilder(0);
}
- tripStatisticsBuilder.addLocation(location, location.getTime());
+ tripStatisticsBuilder.addLocation(location, PreferencesUtils.MIN_RECORDING_DISTANCE_DEFAULT);
// insert in db
insertPoint(location);
diff --git a/MyTracks/src/com/google/android/apps/mytracks/io/fusiontables/SendFusionTablesAsyncTask.java b/MyTracks/src/com/google/android/apps/mytracks/io/fusiontables/SendFusionTablesAsyncTask.java
index 31370e64a..27373ae72 100644
--- a/MyTracks/src/com/google/android/apps/mytracks/io/fusiontables/SendFusionTablesAsyncTask.java
+++ b/MyTracks/src/com/google/android/apps/mytracks/io/fusiontables/SendFusionTablesAsyncTask.java
@@ -248,7 +248,7 @@ public class SendFusionTablesAsyncTask extends AbstractSendAsyncTask {
// Add to the distances and elevations vectors
if (LocationUtils.isValidLocation(location)) {
- tripStatisticsBuilder.addLocation(location, location.getTime());
+ 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);
diff --git a/MyTracks/src/com/google/android/apps/mytracks/io/maps/SendMapsAsyncTask.java b/MyTracks/src/com/google/android/apps/mytracks/io/maps/SendMapsAsyncTask.java
index e46720b8c..65048d74f 100644
--- a/MyTracks/src/com/google/android/apps/mytracks/io/maps/SendMapsAsyncTask.java
+++ b/MyTracks/src/com/google/android/apps/mytracks/io/maps/SendMapsAsyncTask.java
@@ -292,7 +292,7 @@ public class SendMapsAsyncTask extends AbstractSendAsyncTask {
// Add to the distances and elevations vectors
if (LocationUtils.isValidLocation(location)) {
- tripStatisticsBuilder.addLocation(location, location.getTime());
+ 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);
diff --git a/MyTracks/src/com/google/android/apps/mytracks/services/MyTracksLocationManager.java b/MyTracks/src/com/google/android/apps/mytracks/services/MyTracksLocationManager.java
index 42a5d4b4e..b78a1b761 100644
--- a/MyTracks/src/com/google/android/apps/mytracks/services/MyTracksLocationManager.java
+++ b/MyTracks/src/com/google/android/apps/mytracks/services/MyTracksLocationManager.java
@@ -17,6 +17,7 @@
package com.google.android.apps.mytracks.services;
import com.google.android.apps.mytracks.util.GoogleLocationUtils;
+import com.google.common.annotations.VisibleForTesting;
import android.content.ContentResolver;
import android.content.Context;
@@ -44,7 +45,8 @@ public class MyTracksLocationManager {
private static final String USE_LOCATION_FOR_SERVICES = "use_location_for_services";
// User has agreed to use location for Google services.
- private static final String USE_LOCATION_FOR_SERVICES_ON = "1";
+ @VisibleForTesting
+ static final String USE_LOCATION_FOR_SERVICES_ON = "1";
private static final String NAME = "name";
private static final String VALUE = "value";
diff --git a/MyTracks/src/com/google/android/apps/mytracks/services/TrackRecordingService.java b/MyTracks/src/com/google/android/apps/mytracks/services/TrackRecordingService.java
index 5ddd8884e..6570f8b3d 100644
--- a/MyTracks/src/com/google/android/apps/mytracks/services/TrackRecordingService.java
+++ b/MyTracks/src/com/google/android/apps/mytracks/services/TrackRecordingService.java
@@ -82,6 +82,9 @@ import java.util.concurrent.Executors;
public class TrackRecordingService extends Service {
private static final String TAG = TrackRecordingService.class.getSimpleName();
+ public static final int PAUSE_LATITUDE = 100;
+ private static final int RESUME_LATITUDE = 200;
+
// One second in milliseconds
private static final long ONE_SECOND = 1000;
// One minute in milliseconds
@@ -105,25 +108,14 @@ public class TrackRecordingService extends Service {
private int minRequiredAccuracy;
private int autoResumeTrackTimeout;
private long currentRecordingInterval;
- private Track recordingTrack;
// The following variables are set when recording:
-
- /*
- * Track length. Calculated from the recorded points to overlay waypoints
- * precisely on the elevation chart.
- */
- private double length;
-
- // Used for length calculation
- private Location lastLengthLocation;
-
private TripStatisticsBuilder trackTripStatisticsBuilder;
private TripStatisticsBuilder markerTripStatisticsBuilder;
private WakeLock wakeLock;
- private Location lastLocation;
- private boolean isMoving;
private SensorManager sensorManager;
+ private Location lastLocation;
+ private boolean currentSegmentHasLocation;
// Timer to periodically invoke checkLocationListener
private final Timer timer = new Timer();
@@ -202,10 +194,6 @@ public class TrackRecordingService extends Service {
minRecordingDistance = PreferencesUtils.getInt(context,
R.string.min_recording_distance_key,
PreferencesUtils.MIN_RECORDING_DISTANCE_DEFAULT);
- if (trackTripStatisticsBuilder != null && markerTripStatisticsBuilder != null) {
- trackTripStatisticsBuilder.setMinRecordingDistance(minRecordingDistance);
- markerTripStatisticsBuilder.setMinRecordingDistance(minRecordingDistance);
- }
}
if (key == null || key.equals(
PreferencesUtils.getKey(context, R.string.max_recording_distance_key))) {
@@ -262,10 +250,9 @@ public class TrackRecordingService extends Service {
private TimerTask checkLocationListener = new TimerTask() {
@Override
public void run() {
- if (isRecording()) {
+ if (isRecording() && !isPaused()) {
handler.post(new Runnable() {
public void run() {
- unregisterLocationListener();
registerLocationListener();
}
});
@@ -298,21 +285,18 @@ public class TrackRecordingService extends Service {
// Require announcementExecutor and splitExecutor to be created.
sharedPreferenceChangeListener.onSharedPreferenceChanged(sharedPreferences, null);
- // Require locationManager and all preferences set.
- registerLocationListener();
-
timer.schedule(checkLocationListener, 5 * ONE_MINUTE, ONE_MINUTE);
/*
* Try to restart the previous recording track in case the service has been
* restarted by the system, which can sometimes happen.
*/
- recordingTrack = myTracksProviderUtils.getTrack(recordingTrackId);
- if (recordingTrack != null) {
- restartTrack(recordingTrack);
+ Track track = myTracksProviderUtils.getTrack(recordingTrackId);
+ if (track != null) {
+ restartTrack(track);
} else {
- if (recordingTrackId != PreferencesUtils.RECORDING_TRACK_ID_DEFAULT) {
- Log.w(TAG, "recordingTrackId not -1L, but recordingTrack is null. " + recordingTrackId);
+ if (isRecording()) {
+ Log.w(TAG, "track is null, but recordingTrackId not -1L. " + recordingTrackId);
updateRecordingState(PreferencesUtils.RECORDING_TRACK_ID_DEFAULT, true);
}
showNotification();
@@ -426,11 +410,11 @@ public class TrackRecordingService extends Service {
* @return the waypoint id
*/
public long insertWaypoint(WaypointCreationRequest waypointCreationRequest) {
- if (!isRecording()) {
- throw new IllegalStateException("Cannot insert marker when not recording!");
+ if (!isRecording() || isPaused()) {
+ return -1L;
}
- boolean isStatistics = waypointCreationRequest.getType() == WaypointType.STATISTICS;
+ boolean isStatistics = waypointCreationRequest.getType() == WaypointType.STATISTICS;
String name;
if (waypointCreationRequest.getName() != null) {
name = waypointCreationRequest.getName();
@@ -449,7 +433,7 @@ public class TrackRecordingService extends Service {
String description;
if (isStatistics) {
long now = System.currentTimeMillis();
- markerTripStatisticsBuilder.pauseAt(now);
+ markerTripStatisticsBuilder.updateTime(now);
tripStatistics = markerTripStatisticsBuilder.getTripStatistics();
markerTripStatisticsBuilder = new TripStatisticsBuilder(now);
description = new DescriptionGeneratorImpl(this).generateWaypointDescription(tripStatistics);
@@ -466,24 +450,27 @@ public class TrackRecordingService extends Service {
String icon = getString(
isStatistics ? R.string.marker_statistics_icon_url : R.string.marker_waypoint_icon_url);
int type = isStatistics ? Waypoint.TYPE_STATISTICS : Waypoint.TYPE_WAYPOINT;
-
long duration;
- Location location = myTracksProviderUtils.getLastTrackLocation(recordingTrackId);
- if (location != null && trackTripStatisticsBuilder != null
- && trackTripStatisticsBuilder.getTripStatistics() != null) {
- duration = location.getTime() - trackTripStatisticsBuilder.getTripStatistics().getStartTime();
+ double length;
+ Location lastTrackLocation = myTracksProviderUtils.getLastTrackLocation(recordingTrackId);
+ if (currentSegmentHasLocation && LocationUtils.isValidLocation(lastTrackLocation)
+ && trackTripStatisticsBuilder != null) {
+ TripStatistics stats = trackTripStatisticsBuilder.getTripStatistics();
+ length = stats.getTotalDistance();
+ duration = stats.getTotalTime();
} else {
if (!waypointCreationRequest.isTrackStatistics()) {
return -1L;
}
// For track statistics, make it an impossible location
- location = new Location("");
- location.setLatitude(100);
- location.setLongitude(180);
+ lastTrackLocation = new Location("");
+ lastTrackLocation.setLatitude(100);
+ lastTrackLocation.setLongitude(180);
+ length = 0;
duration = 0;
}
Waypoint waypoint = new Waypoint(name, description, category, icon, recordingTrackId, type,
- length, duration, -1L, -1L, location, tripStatistics);
+ length, duration, -1L, -1L, lastTrackLocation, tripStatistics);
Uri uri = myTracksProviderUtils.insertWaypoint(waypoint);
return Long.parseLong(uri.getLastPathSegment());
}
@@ -515,7 +502,7 @@ public class TrackRecordingService extends Service {
private void handleStartCommand(Intent intent, int startId) {
// Check if the service is called to resume track (from phone reboot)
if (intent != null && intent.getBooleanExtra(RESUME_TRACK_EXTRA_NAME, false)) {
- if (!shouldResumeTrack(recordingTrack)) {
+ if (!shouldResumeTrack()) {
Log.i(TAG, "Stop resume track.");
updateRecordingState(PreferencesUtils.RECORDING_TRACK_ID_DEFAULT, true);
stopSelfResult(startId);
@@ -526,10 +513,10 @@ public class TrackRecordingService extends Service {
/**
* Returns true if should resume.
- *
- * @param track the track
*/
- private boolean shouldResumeTrack(Track track) {
+ private boolean shouldResumeTrack() {
+ Track track = myTracksProviderUtils.getTrack(recordingTrackId);
+
if (track == null) {
Log.d(TAG, "Not resuming. Track is null.");
return false;
@@ -570,32 +557,28 @@ public class TrackRecordingService extends Service {
return -1L;
}
long now = System.currentTimeMillis();
- length = 0;
- lastLengthLocation = null;
trackTripStatisticsBuilder = new TripStatisticsBuilder(now);
- trackTripStatisticsBuilder.setMinRecordingDistance(minRecordingDistance);
markerTripStatisticsBuilder = new TripStatisticsBuilder(now);
- markerTripStatisticsBuilder.setMinRecordingDistance(minRecordingDistance);
+ // Insert a track
Track track = new Track();
- track.setStartId(-1L);
- TripStatistics tripStatistics = track.getTripStatistics();
- tripStatistics.setStartTime(now);
Uri uri = myTracksProviderUtils.insertTrack(track);
+ long trackId = Long.parseLong(uri.getLastPathSegment());
- updateRecordingState(Long.parseLong(uri.getLastPathSegment()), false);
-
+ // Update shared preferences
+ updateRecordingState(trackId, false);
+ PreferencesUtils.setInt(this, R.string.auto_resume_track_current_retry_key, 0);
+
+ // Update database
track.setId(recordingTrackId);
track.setName(TrackNameUtils.getTrackName(this, recordingTrackId, now, null));
track.setCategory(PreferencesUtils.getString(
this, R.string.default_activity_key, PreferencesUtils.DEFAULT_ACTIVITY_DEFAULT));
+ track.setTripStatistics(trackTripStatisticsBuilder.getTripStatistics());
myTracksProviderUtils.updateTrack(track);
insertWaypoint(WaypointCreationRequest.DEFAULT_START_TRACK);
-
- PreferencesUtils.setInt(this, R.string.auto_resume_track_current_retry_key, 0);
- registerLocationListener();
- startRecording();
+ startRecording(true);
return recordingTrackId;
}
@@ -607,11 +590,8 @@ public class TrackRecordingService extends Service {
private void restartTrack(Track track) {
Log.d(TAG, "Restarting track: " + track.getId());
- length = 0;
- lastLengthLocation = null;
TripStatistics tripStatistics = track.getTripStatistics();
trackTripStatisticsBuilder = new TripStatisticsBuilder(tripStatistics.getStartTime());
- trackTripStatisticsBuilder.setMinRecordingDistance(minRecordingDistance);
long markerStartTime;
Waypoint waypoint = myTracksProviderUtils.getLastStatisticsWaypoint(recordingTrackId);
@@ -621,10 +601,10 @@ public class TrackRecordingService extends Service {
markerStartTime = tripStatistics.getStartTime();
}
markerTripStatisticsBuilder = new TripStatisticsBuilder(markerStartTime);
- markerTripStatisticsBuilder.setMinRecordingDistance(minRecordingDistance);
Cursor cursor = null;
try {
+ // TODO: how to handle very long track.
cursor = myTracksProviderUtils.getLocationsCursor(
recordingTrackId, -1, Constants.MAX_LOADED_TRACK_POINTS, true);
if (cursor == null) {
@@ -633,20 +613,12 @@ public class TrackRecordingService extends Service {
if (cursor.moveToLast()) {
do {
Location location = myTracksProviderUtils.createLocation(cursor);
- if (LocationUtils.isValidLocation(location)) {
- trackTripStatisticsBuilder.addLocation(location, location.getTime());
- if (location.getTime() > markerStartTime) {
- markerTripStatisticsBuilder.addLocation(location, location.getTime());
- }
- if (lastLengthLocation != null) {
- length += location.distanceTo(lastLengthLocation);
- }
- lastLengthLocation = location;
+ trackTripStatisticsBuilder.addLocation(location, minRecordingDistance);
+ if (location.getTime() > markerStartTime) {
+ markerTripStatisticsBuilder.addLocation(location, minRecordingDistance);
}
} while (cursor.moveToPrevious());
}
- trackTripStatisticsBuilder.pauseAt(tripStatistics.getStopTime());
- trackTripStatisticsBuilder.resumeAt(System.currentTimeMillis());
}
} catch (RuntimeException e) {
Log.e(TAG, "RuntimeException", e);
@@ -655,34 +627,59 @@ public class TrackRecordingService extends Service {
cursor.close();
}
}
- // TODO: update recordingTrackPaused variable based on Track Points table state
- startRecording();
+ startRecording(true);
}
/**
- * Common code for starting a recording, new track or restart track.
+ * Resumes current track.
*/
- private void startRecording() {
+ private void resumeCurrentTrack() {
+ if (!isRecording() || !isPaused()) {
+ Log.d(TAG, "Ignore resumeCurrentTrack. Not recording or not paused.");
+ return;
+ }
+
+ // Update shared preferences
+ recordingTrackPaused = false;
+ PreferencesUtils.setBoolean(this, R.string.recording_track_paused_key, false);
+
+ // Update database
+ Location resume = new Location(LocationManager.GPS_PROVIDER);
+ resume.setLongitude(0);
+ resume.setLatitude(RESUME_LATITUDE);
+ resume.setTime(System.currentTimeMillis());
+ insertLocation(resume, null, null);
+
+ startRecording(false);
+ }
+
+ /**
+ * Common code for starting a new track, resuming a track, or restarting after
+ * phone reboot.
+ *
+ * @param trackStarted true if track is started, false if track is resumed
+ */
+ private void startRecording(boolean trackStarted) {
acquireWakeLock();
- lastLocation = null;
- isMoving = true;
+ // Update instance variables
sensorManager = SensorManagerFactory.getSystemSensorManager(this);
+ lastLocation = null;
+ currentSegmentHasLocation = false;
+ // Register notifications
+ registerLocationListener();
+
+ // Send notifications
showNotification();
- sendTrackBroadcast(R.string.track_started_broadcast_action, recordingTrackId);
+ sendTrackBroadcast(trackStarted ? R.string.track_started_broadcast_action
+ : R.string.track_resumed_broadcast_action, recordingTrackId);
+
+ // Restore periodic tasks
announcementExecutor.restore();
splitExecutor.restore();
}
- private void pauseCurrentTrack() {
- PreferencesUtils.setBoolean(this, R.string.recording_track_paused_key, true);
- }
-
- private void resumeCurrentTrack() {
- PreferencesUtils.setBoolean(this, R.string.recording_track_paused_key, false);
- }
-
/**
* Ends the current track.
*/
@@ -691,30 +688,81 @@ public class TrackRecordingService extends Service {
Log.d(TAG, "Ignore endCurrentTrack. Not recording.");
return;
}
- announcementExecutor.shutdown();
- splitExecutor.shutdown();
- Track track = myTracksProviderUtils.getTrack(recordingTrackId);
- if (track != null) {
- long lastLocationId = myTracksProviderUtils.getLastTrackLocationId(recordingTrackId);
- if (lastLocationId >= 0 && track.getStopId() >= 0) {
- track.setStopId(lastLocationId);
- }
- updateTripStatisticsToTime(track.getTripStatistics(), System.currentTimeMillis());
- myTracksProviderUtils.updateTrack(track);
- }
+
// Need to remember the recordingTrackId before setting it to -1L
long trackId = recordingTrackId;
+
+ // Update shared preferences
updateRecordingState(PreferencesUtils.RECORDING_TRACK_ID_DEFAULT, true);
+ // Update database
+ insertLocation(lastLocation, getSensorDataSet(),
+ myTracksProviderUtils.getLastTrackLocation(recordingTrackId));
+
+ Track track = myTracksProviderUtils.getTrack(recordingTrackId);
+ if (track != null) {
+ updateRecordingTrack(
+ track, myTracksProviderUtils.getLastTrackLocationId(recordingTrackId), false);
+ }
+
+ endRecording(true, trackId);
+ stopSelf();
+ }
+
+ /**
+ * Pauses the current track.
+ */
+ private void pauseCurrentTrack() {
+ if (!isRecording() || isPaused()) {
+ Log.d(TAG, "Ignore pauseCurrentTrack. Not recording or paused.");
+ return;
+ }
+
+ // Update shared preferences
+ recordingTrackPaused = true;
+ PreferencesUtils.setBoolean(this, R.string.recording_track_paused_key, true);
+
+ // Update database
+ insertLocation(lastLocation, getSensorDataSet(),
+ myTracksProviderUtils.getLastTrackLocation(recordingTrackId));
+
+ Location pause = new Location(LocationManager.GPS_PROVIDER);
+ pause.setLongitude(0);
+ pause.setLatitude(PAUSE_LATITUDE);
+ pause.setTime(System.currentTimeMillis());
+ insertLocation(pause, null, null);
+
+ endRecording(false, recordingTrackId);
+ }
+
+ /**
+ * Common code for ending a track or pausing a track.
+ *
+ * @param trackStopped true if track is stopped, false if track is paused
+ * @param trackId
+ */
+ private void endRecording(boolean trackStopped, long trackId) {
+
+ // Shutdown periodic tasks
+ announcementExecutor.shutdown();
+ splitExecutor.shutdown();
+
+ // Update instance variables
if (sensorManager != null) {
SensorManagerFactory.releaseSystemSensorManager();
sensorManager = null;
}
+ lastLocation = null;
+
+ // Unregister notifications
+ unregisterLocationListener();
+
+ // Send notifications
+ showNotification();
+ sendTrackBroadcast(trackStopped ? R.string.track_stopped_broadcast_action
+ : R.string.track_paused_broadcast_action, trackId);
releaseWakeLock();
- showNotification();
- sendTrackBroadcast(R.string.track_stopped_broadcast_action, trackId);
- stopSelf();
}
/**
@@ -730,17 +778,6 @@ public class TrackRecordingService extends Service {
PreferencesUtils.setBoolean(this, R.string.recording_track_paused_key, recordingTrackPaused);
}
- /**
- * Updates a {@link TripStatistics} to a time.
- *
- * @param tripStatistics the trip statistics
- * @param time the time
- */
- private void updateTripStatisticsToTime(TripStatistics tripStatistics, long time) {
- tripStatistics.setStopTime(time);
- tripStatistics.setTotalTime(time - tripStatistics.getStartTime());
- }
-
/**
* Called when location changed.
*
@@ -748,13 +785,13 @@ public class TrackRecordingService extends Service {
*/
private void onLocationChangedAsync(Location location) {
try {
- if (location == null) {
- Log.w(TAG, "Ignore onLocationChangedAsync. location is null.");
+ if (!isRecording() || isPaused()) {
+ Log.w(TAG, "Ignore onLocationChangedAsync. Not recording or paused.");
return;
}
- if (!isRecording()) {
- Log.w(TAG, "Ignore onLocationChangedAsync. Not recording.");
+ if (!LocationUtils.isValidLocation(location)) {
+ Log.w(TAG, "Ignore onLocationChangedAsync. location is invalid.");
return;
}
@@ -763,74 +800,56 @@ public class TrackRecordingService extends Service {
return;
}
- recordingTrack = myTracksProviderUtils.getTrack(recordingTrackId);
- if (recordingTrack == null) {
- Log.d(TAG, "Ignore onLocationChangedAsync. recodingTrack is null.");
- return;
- }
-
- locationListenerPolicy.updateIdleTime(trackTripStatisticsBuilder.getIdleTime());
-
- if (LocationUtils.isValidLocation(location)) {
- long now = System.currentTimeMillis();
- trackTripStatisticsBuilder.addLocation(location, now);
- markerTripStatisticsBuilder.addLocation(location, now);
- }
+ Location lastTrackLocation = myTracksProviderUtils.getLastTrackLocation(recordingTrackId);
+ SensorDataSet sensorDataSet = getSensorDataSet();
+ long idleTime = currentSegmentHasLocation && LocationUtils.isValidLocation(lastTrackLocation) ?
+ location.getTime() - lastTrackLocation.getTime()
+ : 0;
+ locationListenerPolicy.updateIdleTime(idleTime);
if (currentRecordingInterval != locationListenerPolicy.getDesiredPollingInterval()) {
registerLocationListener();
}
- Location lastRecordedLocation = myTracksProviderUtils.getLastLocation();
- double distanceToLastRecordedLocation = lastRecordedLocation != null ? location.distanceTo(
- lastRecordedLocation)
- : Double.POSITIVE_INFINITY;
- double distanceToLastLocation = lastLocation != null ? location.distanceTo(lastLocation)
- : Double.POSITIVE_INFINITY;
- boolean hasSensorData = sensorManager != null && sensorManager.isEnabled()
- && sensorManager.getSensorDataSet() != null && sensorManager.isSensorDataSetValid();
-
- // If stationary for two locations, make sure the first one is recorded
- if (distanceToLastLocation == 0 && !hasSensorData) {
- if (isMoving) {
- isMoving = false;
- if (lastLocation != null && !lastLocation.equals(lastRecordedLocation)) {
- if (!insertLocation(lastLocation, lastRecordedLocation, recordingTrackId)) {
- return;
- }
- }
- }
+ // Always insert the first segment location
+ if (!currentSegmentHasLocation) {
+ insertLocation(location, sensorDataSet, lastTrackLocation);
+ currentSegmentHasLocation = true;
lastLocation = location;
- } else if (distanceToLastRecordedLocation >= minRecordingDistance || hasSensorData) {
- if (!isMoving) {
- isMoving = true;
- // Make sure the lastLocation is added.
- if (lastLocation != null && !lastLocation.equals(lastRecordedLocation)) {
- if (!insertLocation(lastLocation, lastRecordedLocation, recordingTrackId)) {
- return;
- }
- }
- }
-
- boolean startNewSegment = LocationUtils.isValidLocation(lastRecordedLocation)
- && distanceToLastRecordedLocation > maxRecordingDistance
- && recordingTrack.getStartId() >= 0;
- if (startNewSegment) {
- Log.d(TAG, "Inserting a separator track point.");
- Location separator = new Location(LocationManager.GPS_PROVIDER);
- separator.setLongitude(0);
- separator.setLatitude(100);
- separator.setTime(lastRecordedLocation.getTime());
- myTracksProviderUtils.insertTrackPoint(separator, recordingTrackId);
- }
-
- if (!insertLocation(location, lastRecordedLocation, recordingTrackId)) {
- return;
- }
- lastLocation = location;
- } else {
- Log.d(TAG, "Ignore location. distanceToLastRecordedLocation < minRecordingDistance.");
+ return;
}
+
+ if (!LocationUtils.isValidLocation(lastTrackLocation)) {
+ /*
+ * Should not happen. The current segment should have a location. Just
+ * insert the current location.
+ */
+ insertLocation(location, sensorDataSet, lastTrackLocation);
+ lastLocation = location;
+ return;
+ }
+
+ double distanceToLastTrackLocation = location.distanceTo(lastTrackLocation);
+ if (distanceToLastTrackLocation < minRecordingDistance && sensorDataSet == null) {
+ // skip
+ } else if (distanceToLastTrackLocation > maxRecordingDistance) {
+ insertLocation(lastLocation, sensorDataSet, lastTrackLocation);
+ Location pause = new Location(LocationManager.GPS_PROVIDER);
+ pause.setLongitude(0);
+ pause.setLatitude(PAUSE_LATITUDE);
+ pause.setTime(lastLocation.getTime());
+ insertLocation(pause, null, null);
+
+ insertLocation(location, sensorDataSet, lastTrackLocation);
+ } else {
+ /*
+ * (distanceToLastTrackLocation >= minRecordingDistance ||
+ * hasSensorData) && distanceToLastTrackLocation <= maxRecordingDistance
+ */
+ insertLocation(lastLocation, sensorDataSet, lastTrackLocation);
+ insertLocation(location, sensorDataSet, lastTrackLocation);
+ }
+ lastLocation = location;
} catch (Error e) {
Log.e(TAG, "Error in onLocationChangedAsync", e);
throw e;
@@ -840,62 +859,28 @@ public class TrackRecordingService extends Service {
}
}
- /**
- * Inserts a new location in the track points db and updates the corresponding
- * track in the track db.
- *
- * @param location the location to be inserted
- * @param lastRecordedLocation the last recorded location before this one (or
- * null if none)
- * @param trackId the id of the track
- * @return true if successful. False if SQLite3 threw an exception.
- */
- private boolean insertLocation(Location location, Location lastRecordedLocation, long trackId) {
-
- // Keep track of length along recorded track
- if (LocationUtils.isValidLocation(location)) {
- if (lastLengthLocation != null) {
- length += location.distanceTo(lastLengthLocation);
- }
- lastLengthLocation = location;
+ private void insertLocation(
+ Location location, SensorDataSet sensorDataSet, Location lastRecordedLocation) {
+ Track track = myTracksProviderUtils.getTrack(recordingTrackId);
+ if (track == null) {
+ return;
+ }
+ if (location == null) {
+ return;
+ }
+ if (location.equals(lastRecordedLocation)) {
+ return;
}
try {
- Location locationToInsert = location;
- if (sensorManager != null && sensorManager.isEnabled()) {
- SensorDataSet sensorDataSet = sensorManager.getSensorDataSet();
- if (sensorDataSet != null && sensorManager.isSensorDataSetValid()) {
- locationToInsert = new MyTracksLocation(location, sensorDataSet);
- }
- }
- Uri uri = myTracksProviderUtils.insertTrackPoint(locationToInsert, trackId);
- int pointId = Integer.parseInt(uri.getLastPathSegment());
-
- // Update the current track
- if (lastRecordedLocation != null && lastRecordedLocation.getLatitude() <= 90) {
- if (recordingTrack.getStartId() < 0) {
- recordingTrack.setStartId(pointId);
- }
- recordingTrack.setStopId(pointId);
- recordingTrack.setNumberOfPoints(recordingTrack.getNumberOfPoints() + 1);
-
- long now = System.currentTimeMillis();
- TripStatistics tripStatistics = trackTripStatisticsBuilder.getTripStatistics();
- updateTripStatisticsToTime(tripStatistics, now);
- recordingTrack.setTripStatistics(tripStatistics);
-
- myTracksProviderUtils.updateTrack(recordingTrack);
-
- // Update the first waypoint
- Waypoint waypoint = myTracksProviderUtils.getFirstWaypoint(recordingTrackId);
- if (waypoint != null) {
- TripStatistics stats = trackTripStatisticsBuilder.getTripStatistics();
- waypoint.setLength(length);
- waypoint.setDuration(now - stats.getStartTime());
- waypoint.setTripStatistics(stats);
- myTracksProviderUtils.updateWaypoint(waypoint);
- }
+ if (sensorDataSet != null) {
+ location = new MyTracksLocation(location, sensorDataSet);
}
+ Uri uri = myTracksProviderUtils.insertTrackPoint(location, recordingTrackId);
+ long trackPointId = Long.parseLong(uri.getLastPathSegment());
+ trackTripStatisticsBuilder.addLocation(location, minRecordingDistance);
+ markerTripStatisticsBuilder.addLocation(location, minRecordingDistance);
+ updateRecordingTrack(track, trackPointId, LocationUtils.isValidLocation(location));
} catch (SQLiteException e) {
/*
* Insert failed, most likely because of SqlLite error code 5
@@ -903,17 +888,42 @@ public class TrackRecordingService extends Service {
* listener gets invoked twice at about the same time).
*/
Log.w(TAG, "SQLiteException", e);
- return false;
}
announcementExecutor.update();
splitExecutor.update();
- return true;
+ }
+
+ private void updateRecordingTrack(
+ Track track, long trackPointId, boolean isTrackPointNewAndValid) {
+ if (trackPointId >= 0) {
+ if (track.getStartId() < 0) {
+ track.setStartId(trackPointId);
+ }
+ track.setStopId(trackPointId);
+ }
+ if (isTrackPointNewAndValid) {
+ track.setNumberOfPoints(track.getNumberOfPoints() + 1);
+ }
+
+ trackTripStatisticsBuilder.updateTime(System.currentTimeMillis());
+ track.setTripStatistics(trackTripStatisticsBuilder.getTripStatistics());
+ myTracksProviderUtils.updateTrack(track);
+ }
+
+ private SensorDataSet getSensorDataSet() {
+ if (sensorManager == null || !sensorManager.isEnabled()
+ || !sensorManager.isSensorDataSetValid()) {
+ return null;
+ }
+ return sensorManager.getSensorDataSet();
}
/**
* Registers the location listener.
*/
private void registerLocationListener() {
+ unregisterLocationListener();
+
if (myTracksLocationManager == null) {
Log.e(TAG, "locationManager is null.");
return;
@@ -981,7 +991,7 @@ public class TrackRecordingService extends Service {
* Shows the notification.
*/
private void showNotification() {
- if (isRecording()) {
+ if (isRecording() && !isPaused()) {
Intent intent = IntentUtils.newIntent(this, TrackDetailActivity.class)
.putExtra(TrackDetailActivity.EXTRA_TRACK_ID, recordingTrackId);
TaskStackBuilder taskStackBuilder = TaskStackBuilder.from(this);
@@ -1070,7 +1080,7 @@ public class TrackRecordingService extends Service {
}
return trackRecordingService.isPaused();
}
-
+
@Override
public long getRecordingTrackId() {
if (!canAccess()) {
@@ -1102,7 +1112,7 @@ public class TrackRecordingService extends Service {
}
trackRecordingService.pauseCurrentTrack();
}
-
+
@Override
public void resumeCurrentTrack() {
if (!canAccess()) {
diff --git a/MyTracks/src/com/google/android/apps/mytracks/services/tasks/PeriodicTaskExecutor.java b/MyTracks/src/com/google/android/apps/mytracks/services/tasks/PeriodicTaskExecutor.java
index 14a92c684..112ea4db2 100644
--- a/MyTracks/src/com/google/android/apps/mytracks/services/tasks/PeriodicTaskExecutor.java
+++ b/MyTracks/src/com/google/android/apps/mytracks/services/tasks/PeriodicTaskExecutor.java
@@ -62,8 +62,8 @@ public class PeriodicTaskExecutor {
* Restores the executor.
*/
public void restore() {
- if (!trackRecordingService.isRecording()) {
- Log.d(TAG, "Not recording.");
+ if (!trackRecordingService.isRecording() || trackRecordingService.isPaused()) {
+ Log.d(TAG, "Not recording or paused.");
return;
}
@@ -159,7 +159,7 @@ public class PeriodicTaskExecutor {
* Calculates the next distance for the distance periodic task.
*/
private void calculateNextTaskDistance() {
- if (!trackRecordingService.isRecording() || periodicTask == null) {
+ if (!trackRecordingService.isRecording() || trackRecordingService.isPaused() || periodicTask == null) {
return;
}
diff --git a/MyTracks/src/com/google/android/apps/mytracks/services/tasks/TimerTaskExecutor.java b/MyTracks/src/com/google/android/apps/mytracks/services/tasks/TimerTaskExecutor.java
index 4d9364de7..8335116fc 100644
--- a/MyTracks/src/com/google/android/apps/mytracks/services/tasks/TimerTaskExecutor.java
+++ b/MyTracks/src/com/google/android/apps/mytracks/services/tasks/TimerTaskExecutor.java
@@ -50,7 +50,7 @@ public class TimerTaskExecutor {
* @param interval the interval in milliseconds
*/
public void scheduleTask(long interval) {
- if (!trackRecordingService.isRecording()) {
+ if (!trackRecordingService.isRecording() || trackRecordingService.isPaused()) {
return;
}
diff --git a/MyTracks/src/com/google/android/apps/mytracks/stats/TripStatisticsBuilder.java b/MyTracks/src/com/google/android/apps/mytracks/stats/TripStatisticsBuilder.java
index 18f7103b9..1220c66e3 100644
--- a/MyTracks/src/com/google/android/apps/mytracks/stats/TripStatisticsBuilder.java
+++ b/MyTracks/src/com/google/android/apps/mytracks/stats/TripStatisticsBuilder.java
@@ -19,132 +19,125 @@ package com.google.android.apps.mytracks.stats;
import static com.google.android.apps.mytracks.Constants.TAG;
import com.google.android.apps.mytracks.Constants;
-import com.google.android.apps.mytracks.util.PreferencesUtils;
+import com.google.android.apps.mytracks.services.TrackRecordingService;
+import com.google.android.apps.mytracks.util.LocationUtils;
import com.google.common.annotations.VisibleForTesting;
import android.location.Location;
import android.util.Log;
/**
- * Builder for {@link TripStatistics}. For keeping statistics as a track is
- * paused/resumed and new locations are added.
+ * Builder for {@link TripStatistics}. For keeping track statistics as new
+ * locations are added. Note that some of the locations represent pause/resume
+ * separator.
*
* @author Sandor Dornbush
* @author Rodrigo Damazio
*/
public class TripStatisticsBuilder {
- // The trip statistics.
+ // The track's trip statistics
private final TripStatistics tripStatistics;
- // The minimum recording distance.
- private int minRecordingDistance = PreferencesUtils.MIN_RECORDING_DISTANCE_DEFAULT;
+ // The current segment's trip statistics
+ private TripStatistics currentSegment;
- // True if the trip is paused. All trips start as paused.
- private boolean paused = true;
-
- // The last location as reported by GPS.
+ // Current segment's last location.
private Location lastLocation;
- // The last moving location that contributed to the moving statistics.
+ // Current segment's last moving location
private Location lastMovingLocation;
- // A buffer of the recent speed readings (m/s) for calculating max speed.
+ // A buffer of the recent speed readings (m/s) for calculating max speed
private final DoubleBuffer speedBuffer = new DoubleBuffer(Constants.SPEED_SMOOTHING_FACTOR);
- // A buffer of the recent elevation readings (m).
+ // A buffer of the recent elevation readings (m)
private final DoubleBuffer elevationBuffer = new DoubleBuffer(
Constants.ELEVATION_SMOOTHING_FACTOR);
- // A buffer of the recent distance readings for calculating grade.
+ // A buffer of the recent distance readings (m) for calculating grade
private final DoubleBuffer distanceBuffer = new DoubleBuffer(Constants.DISTANCE_SMOOTHING_FACTOR);
- // A buffer of the recent grade calculations
+ // A buffer of the recent grade calculations (%)
private final DoubleBuffer gradeBuffer = new DoubleBuffer(Constants.GRADE_SMOOTHING_FACTOR);
/**
- * Creates a new trip starting at a start time.
+ * Creates a new trip statistics builder.
*
* @param startTime the start time
*/
public TripStatisticsBuilder(long startTime) {
- tripStatistics = new TripStatistics();
- resumeAt(startTime);
+ tripStatistics = init(startTime);
+ currentSegment = init(startTime);
+ }
+
+ public void updateTime(long time) {
+ currentSegment.setStopTime(time);
+ currentSegment.setTotalTime(time - currentSegment.getStartTime());
}
/**
- * Creates a new trip, starting with an existing {@link TripStatistics}.
- *
- * @param other the existing {@link TripStatistics}
- */
- public TripStatisticsBuilder(TripStatistics other) {
- tripStatistics = new TripStatistics(other);
- if (tripStatistics.getStartTime() > 0) {
- resumeAt(tripStatistics.getStartTime());
- }
- }
-
- /**
- * Sets the min recording distance.
- *
- * @param minRecordingDistance the min recording distance
- */
- public void setMinRecordingDistance(int minRecordingDistance) {
- this.minRecordingDistance = minRecordingDistance;
- }
-
- /**
- * Resumes the current track at a given time.
- *
- * @param time the time
- */
- public void resumeAt(long time) {
- if (!paused) {
- return;
- }
-
- tripStatistics.setStartTime(time);
- tripStatistics.setStopTime(-1L);
- paused = false;
- lastLocation = null;
- lastMovingLocation = null;
- speedBuffer.reset();
- elevationBuffer.reset();
- distanceBuffer.reset();
- gradeBuffer.reset();
- }
-
- /**
- * Pauses the track at a given time.
- *
- * @param time the time to pause at
- */
- public void pauseAt(long time) {
- if (paused) {
- return;
- }
- tripStatistics.setStopTime(time);
- // TODO: total time needs to take into account pauses
- tripStatistics.setTotalTime(time - tripStatistics.getStartTime());
- paused = true;
- }
-
- /**
- * Gets the trip statistics.
+ * Gets the track's trip statistics.
*/
public TripStatistics getTripStatistics() {
- // Take a snapshot - we don't want anyone messing with our internals
- return new TripStatistics(tripStatistics);
+ // Take a snapshot - we don't want anyone messing with our tripStatistics
+ TripStatistics stats = new TripStatistics(tripStatistics);
+ stats.merge(currentSegment);
+ return stats;
}
/**
- * Returns the amount of time the user has been idle or 0 if he is moving.
+ * Adds a location. TODO: This assume location has a valid time.
+ *
+ * @param location the location
+ * @param minRecordingDistance the min recording distance
*/
- public long getIdleTime() {
- if (lastLocation == null || lastMovingLocation == null) {
- return 0;
+ public void addLocation(Location location, int minRecordingDistance) {
+ if (!LocationUtils.isValidLocation(location)) {
+ updateTime(location.getTime());
+ if (location.getLatitude() == TrackRecordingService.PAUSE_LATITUDE) {
+ tripStatistics.merge(currentSegment);
+ }
+ currentSegment = init(location.getTime());
+ lastLocation = null;
+ lastMovingLocation = null;
+ speedBuffer.reset();
+ elevationBuffer.reset();
+ distanceBuffer.reset();
+ gradeBuffer.reset();
+ return;
}
- return lastLocation.getTime() - lastMovingLocation.getTime();
+ double elevationDifference = updateElevation(location.getAltitude());
+ currentSegment.updateLatitudeExtremities(location.getLatitude());
+ currentSegment.updateLongitudeExtremities(location.getLongitude());
+
+ if (lastLocation == null || lastMovingLocation == null) {
+ updateTime(location.getTime());
+ lastLocation = location;
+ lastMovingLocation = location;
+ return;
+ }
+ double movingDistance = lastMovingLocation.distanceTo(location);
+ if (movingDistance < minRecordingDistance
+ && location.getSpeed() < Constants.MAX_NO_MOVEMENT_SPEED) {
+ updateTime(location.getTime());
+ lastLocation = location;
+ return;
+ }
+ long movingTime = location.getTime() - lastLocation.getTime();
+ if (movingTime < 0) {
+ updateTime(location.getTime());
+ lastLocation = location;
+ return;
+ }
+ currentSegment.addTotalDistance(movingDistance);
+ currentSegment.addMovingTime(movingTime);
+ updateSpeed(
+ location.getTime(), location.getSpeed(), lastLocation.getTime(), lastLocation.getSpeed());
+ updateGrade(lastLocation.distanceTo(location), elevationDifference);
+ updateTime(location.getTime());
+ lastLocation = location;
+ lastMovingLocation = location;
}
/**
@@ -152,62 +145,11 @@ public class TripStatisticsBuilder {
* is noisy so the smoothed elevation is better than the raw elevation for
* many tasks.
*/
- public double getSmoothedElevation() {
+ @VisibleForTesting
+ double getSmoothedElevation() {
return elevationBuffer.getAverage();
}
- /**
- * Adds a location. This will update all of the internal variables with this
- * new location.
- *
- * @param location the location
- * @param systemTime the system time for calculating totalTime. This should be
- * the phone's system time (not GPS time)
- * @return true if the person is moving
- */
- public boolean addLocation(Location location, long systemTime) {
- if (paused) {
- Log.w(TAG, "Track is paused. Ignore addLocation.");
- return false;
- }
-
- tripStatistics.setTotalTime(systemTime - tripStatistics.getStartTime());
-
- double elevationDifference = updateElevation(location.getAltitude());
- tripStatistics.updateLatitudeExtremities(location.getLatitude());
- tripStatistics.updateLongitudeExtremities(location.getLongitude());
-
- // If this is the first location, remember it and return.
- if (lastLocation == null || lastMovingLocation == null) {
- lastLocation = location;
- lastMovingLocation = location;
- return false;
- }
-
- // Don't do anything more if we didn't move since the last location.
- double distance = lastLocation.distanceTo(location);
- if (distance < minRecordingDistance && location.getSpeed() < Constants.MAX_NO_MOVEMENT_SPEED) {
- lastLocation = location;
- return false;
- }
-
- long timeDifference = location.getTime() - lastLocation.getTime();
- if (timeDifference < 0) {
- Log.e(TAG, "Negative time difference: " + timeDifference);
- lastLocation = location;
- return false;
- }
-
- tripStatistics.addTotalDistance(lastMovingLocation.distanceTo(location));
- tripStatistics.addMovingTime(timeDifference);
- updateSpeed(
- location.getTime(), location.getSpeed(), lastLocation.getTime(), lastLocation.getSpeed());
- updateGrade(distance, elevationDifference);
- lastLocation = location;
- lastMovingLocation = location;
- return true;
- }
-
/**
* Updates a speed reading. Assumes the user is moving.
*
@@ -223,12 +165,12 @@ public class TripStatisticsBuilder {
return;
}
speedBuffer.setNext(speed);
- if (speed > tripStatistics.getMaxSpeed()) {
- tripStatistics.setMaxSpeed(speed);
+ if (speed > currentSegment.getMaxSpeed()) {
+ currentSegment.setMaxSpeed(speed);
}
- double movingSpeed = tripStatistics.getAverageMovingSpeed();
- if (speedBuffer.isFull() && movingSpeed > tripStatistics.getMaxSpeed()) {
- tripStatistics.setMaxSpeed(movingSpeed);
+ double movingSpeed = currentSegment.getAverageMovingSpeed();
+ if (speedBuffer.isFull() && movingSpeed > currentSegment.getMaxSpeed()) {
+ currentSegment.setMaxSpeed(movingSpeed);
}
}
@@ -242,10 +184,10 @@ public class TripStatisticsBuilder {
double oldAverage = elevationBuffer.getAverage();
elevationBuffer.setNext(elevation);
double newAverage = elevationBuffer.getAverage();
- tripStatistics.updateElevationExtremities(newAverage);
+ currentSegment.updateElevationExtremities(newAverage);
double elevationDifference = elevationBuffer.isFull() ? newAverage - oldAverage : 0.0;
if (elevationDifference > 0) {
- tripStatistics.addTotalElevationGain(elevationDifference);
+ currentSegment.addTotalElevationGain(elevationDifference);
}
return elevationDifference;
}
@@ -270,7 +212,14 @@ public class TripStatisticsBuilder {
return;
}
gradeBuffer.setNext(elevationDifference / smoothedDistance);
- tripStatistics.updateGradeExtremities(gradeBuffer.getAverage());
+ currentSegment.updateGradeExtremities(gradeBuffer.getAverage());
+ }
+
+ private TripStatistics init(long time) {
+ TripStatistics stats = new TripStatistics();
+ stats.setStartTime(time);
+ stats.setStopTime(time);
+ return stats;
}
/**
diff --git a/MyTracksLib/res/values/third_party.xml b/MyTracksLib/res/values/third_party.xml
index ef628f352..a216c7a9a 100644
--- a/MyTracksLib/res/values/third_party.xml
+++ b/MyTracksLib/res/values/third_party.xml
@@ -23,6 +23,8 @@ limitations under the License.
com.google.android.apps.mytracks.TRACK_STARTED
+ com.google.android.apps.mytracks.TRACK_PAUSED
+ com.google.android.apps.mytracks.TRACK_RESUMED
com.google.android.apps.mytracks.TRACK_STOPPED
com.google.android.apps.mytracks.TRACK_ID
diff --git a/MyTracksLib/src/com/google/android/apps/mytracks/stats/TripStatistics.java b/MyTracksLib/src/com/google/android/apps/mytracks/stats/TripStatistics.java
index 892c0d590..cee446305 100644
--- a/MyTracksLib/src/com/google/android/apps/mytracks/stats/TripStatistics.java
+++ b/MyTracksLib/src/com/google/android/apps/mytracks/stats/TripStatistics.java
@@ -101,16 +101,24 @@ public class TripStatistics implements Parcelable {
totalDistance += other.totalDistance;
totalTime += other.totalTime;
movingTime += other.movingTime;
- latitudeExtremities.update(other.latitudeExtremities.getMin());
- latitudeExtremities.update(other.latitudeExtremities.getMax());
- longitudeExtremities.update(other.longitudeExtremities.getMin());
- longitudeExtremities.update(other.longitudeExtremities.getMax());
+ if (other.latitudeExtremities.hasData()) {
+ latitudeExtremities.update(other.latitudeExtremities.getMin());
+ latitudeExtremities.update(other.latitudeExtremities.getMax());
+ }
+ if (other.longitudeExtremities.hasData()) {
+ longitudeExtremities.update(other.longitudeExtremities.getMin());
+ longitudeExtremities.update(other.longitudeExtremities.getMax());
+ }
maxSpeed = Math.max(maxSpeed, other.maxSpeed);
- elevationExtremities.update(other.elevationExtremities.getMin());
- elevationExtremities.update(other.elevationExtremities.getMax());
+ if (other.elevationExtremities.hasData()) {
+ elevationExtremities.update(other.elevationExtremities.getMin());
+ elevationExtremities.update(other.elevationExtremities.getMax());
+ }
totalElevationGain += other.totalElevationGain;
- gradeExtremities.update(other.gradeExtremities.getMin());
- gradeExtremities.update(other.gradeExtremities.getMax());
+ if (other.gradeExtremities.hasData()) {
+ gradeExtremities.update(other.gradeExtremities.getMin());
+ gradeExtremities.update(other.gradeExtremities.getMax());
+ }
}
/**
diff --git a/MyTracksTest/src/com/google/android/apps/mytracks/services/TrackRecordingServiceTest.java b/MyTracksTest/src/com/google/android/apps/mytracks/services/TrackRecordingServiceTest.java
index d64cd82bd..93e11a48e 100644
--- a/MyTracksTest/src/com/google/android/apps/mytracks/services/TrackRecordingServiceTest.java
+++ b/MyTracksTest/src/com/google/android/apps/mytracks/services/TrackRecordingServiceTest.java
@@ -46,6 +46,7 @@ import android.test.RenamingDelegatingContext;
import android.test.ServiceTestCase;
import android.test.mock.MockContentProvider;
import android.test.mock.MockContentResolver;
+import android.test.mock.MockCursor;
import android.test.suitebuilder.annotation.MediumTest;
import android.test.suitebuilder.annotation.SmallTest;
import android.util.Log;
@@ -143,6 +144,7 @@ public class TrackRecordingServiceTest extends ServiceTestCase Constants.SPEED_SMOOTHING_FACTOR) {
- assertEquals(11.1f, tripStatistics.getMaxSpeed(), 0.1);
+ if (i >= Constants.SPEED_SMOOTHING_FACTOR) {
+ assertEquals(speed, tripStatistics.getMaxSpeed(), 0.1);
}
- if (i > Constants.DISTANCE_SMOOTHING_FACTOR && i > Constants.ELEVATION_SMOOTHING_FACTOR) {
+ if (i >= Constants.DISTANCE_SMOOTHING_FACTOR && i >= Constants.ELEVATION_SMOOTHING_FACTOR) {
// 1 m / 111 m = .009
assertEquals(0.009, tripStatistics.getMinGrade(), 0.0001);
assertEquals(0.009, tripStatistics.getMaxGrade(), 0.0001);
}
- assertEquals(111.0 * i, tripStatistics.getTotalDistance(), 100);
+ assertEquals(i * 111.0, tripStatistics.getTotalDistance(), 100);
}
}
/**
- * Test that elevation works if the user is stable.
+ * Tests {@link TripStatisticsBuilder#updateElevation(double)} with constant
+ * elevations.
*/
public void testElevationSimple() throws Exception {
for (double elevation = 0; elevation < 1000; elevation += 10) {
- builder = new TripStatisticsBuilder(System.currentTimeMillis());
- for (int j = 0; j < 100; j++) {
- assertEquals(0.0, builder.updateElevation(elevation));
- assertEquals(elevation, builder.getSmoothedElevation());
- TripStatistics data = builder.getTripStatistics();
- assertEquals(elevation, data.getMinElevation());
- assertEquals(elevation, data.getMaxElevation());
- assertEquals(0.0, data.getTotalElevationGain());
+ tripStatisticsBuilder = new TripStatisticsBuilder(System.currentTimeMillis());
+ for (int i = 0; i < 100; i++) {
+ assertEquals(0.0, tripStatisticsBuilder.updateElevation(elevation));
+ assertEquals(elevation, tripStatisticsBuilder.getSmoothedElevation());
+
+ TripStatistics tripStatistics = tripStatisticsBuilder.getTripStatistics();
+ assertEquals(elevation, tripStatistics.getMinElevation());
+ assertEquals(elevation, tripStatistics.getMaxElevation());
+ assertEquals(0.0, tripStatistics.getTotalElevationGain());
}
}
}
+ /**
+ * Tests {@link TripStatisticsBuilder#updateGrade(double, double)} with
+ * elevation gain.
+ */
public void testElevationGain() throws Exception {
for (double i = 0; i < 1000; i++) {
double expectedGain;
- if (i < (Constants.ELEVATION_SMOOTHING_FACTOR - 1)) {
+ if (i < Constants.ELEVATION_SMOOTHING_FACTOR - 1) {
expectedGain = 0;
} else if (i < Constants.ELEVATION_SMOOTHING_FACTOR) {
expectedGain = 0.5;
} else {
expectedGain = 1.0;
}
- assertEquals(expectedGain, builder.updateElevation(i));
- assertEquals(i, builder.getSmoothedElevation(), 20);
- TripStatistics data = builder.getTripStatistics();
- assertEquals(0.0, data.getMinElevation(), 0.0);
- assertEquals(i, data.getMaxElevation(), Constants.ELEVATION_SMOOTHING_FACTOR);
+ assertEquals(expectedGain, tripStatisticsBuilder.updateElevation(i));
+ assertEquals(i, tripStatisticsBuilder.getSmoothedElevation(),
+ Constants.ELEVATION_SMOOTHING_FACTOR / 2);
+
+ TripStatistics data = tripStatisticsBuilder.getTripStatistics();
+ assertEquals(0.0, data.getMinElevation());
+ assertEquals(i, data.getMaxElevation(), Constants.ELEVATION_SMOOTHING_FACTOR / 2);
assertEquals(i, data.getTotalElevationGain(), Constants.ELEVATION_SMOOTHING_FACTOR);
}
}
+ /**
+ * Tests {@link TripStatisticsBuilder#updateGrade(double, double)} with grade
+ * of 1 and -1.
+ */
public void testGradeSimple() throws Exception {
for (double i = 0; i < 1000; i++) {
- // The value of the elevation does not matter. This is just to fill the
- // buffer.
- builder.updateElevation(i);
- builder.updateGrade(100, 100);
- if ((i > Constants.GRADE_SMOOTHING_FACTOR) && (i > Constants.ELEVATION_SMOOTHING_FACTOR)) {
- assertEquals(1.0, builder.getTripStatistics().getMaxGrade());
- assertEquals(1.0, builder.getTripStatistics().getMinGrade());
+ /*
+ * The value of the elevation does not matter. This is just to fill the
+ * elevation buffer.
+ */
+ tripStatisticsBuilder.updateElevation(i);
+ tripStatisticsBuilder.updateGrade(100, 100);
+ if (i >= Constants.GRADE_SMOOTHING_FACTOR && i >= Constants.ELEVATION_SMOOTHING_FACTOR) {
+ assertEquals(1.0, tripStatisticsBuilder.getTripStatistics().getMaxGrade());
+ assertEquals(1.0, tripStatisticsBuilder.getTripStatistics().getMinGrade());
}
}
for (double i = 0; i < 1000; i++) {
- // The value of the elevation does not matter. This is just to fill the
- // buffer.
- builder.updateElevation(i);
- builder.updateGrade(100, -100);
- if ((i > Constants.GRADE_SMOOTHING_FACTOR) && (i > Constants.ELEVATION_SMOOTHING_FACTOR)) {
- assertEquals(1.0, builder.getTripStatistics().getMaxGrade());
- assertEquals(-1.0, builder.getTripStatistics().getMinGrade());
+ /*
+ * The value of the elevation does not matter. This is just to fill the
+ * elevation buffer.
+ */
+ tripStatisticsBuilder.updateElevation(i);
+ tripStatisticsBuilder.updateGrade(100, -100);
+ if (i >= Constants.GRADE_SMOOTHING_FACTOR && i >= Constants.ELEVATION_SMOOTHING_FACTOR) {
+ assertEquals(1.0, tripStatisticsBuilder.getTripStatistics().getMaxGrade());
+ assertEquals(-1.0, tripStatisticsBuilder.getTripStatistics().getMinGrade());
}
}
}
+ /**
+ * Tests {@link TripStatisticsBuilder#updateGrade(double, double)} with
+ * distance of 1. The grade should get ignored.
+ */
public void testGradeIgnoreShort() throws Exception {
for (double i = 0; i < 100; i++) {
- // The value of the elevation does not matter. This is just to fill the
- // buffer.
- builder.updateElevation(i);
- builder.updateGrade(1, 100);
- assertEquals(Double.NEGATIVE_INFINITY, builder.getTripStatistics().getMaxGrade());
- assertEquals(Double.POSITIVE_INFINITY, builder.getTripStatistics().getMinGrade());
+ /*
+ * The value of the elevation does not matter. This is just to fill the
+ * elevation buffer.
+ */
+ tripStatisticsBuilder.updateElevation(i);
+ tripStatisticsBuilder.updateGrade(1, 100);
+ assertEquals(
+ Double.NEGATIVE_INFINITY, tripStatisticsBuilder.getTripStatistics().getMaxGrade());
+ assertEquals(
+ Double.POSITIVE_INFINITY, tripStatisticsBuilder.getTripStatistics().getMinGrade());
}
}
+ /**
+ * Tests {@link TripStatisticsBuilder#updateSpeed(long, double, long, double)}
+ * with speed of zero.
+ */
public void testUpdateSpeedIncludeZero() {
for (int i = 0; i < 1000; i++) {
- builder.updateSpeed(i + 1000, 0.0, i, 4.0);
- assertEquals(0.0, builder.getTripStatistics().getMaxSpeed());
+ tripStatisticsBuilder.updateSpeed(i + ONE_SECOND, 0.0, i, 4.0);
+ assertEquals(0.0, tripStatisticsBuilder.getTripStatistics().getMaxSpeed());
}
}
+ /**
+ * Tests {@link TripStatisticsBuilder#updateSpeed(long, double, long, double)}
+ * with the error code 128. The speed should get ignored.
+ */
public void testUpdateSpeedIngoreErrorCode() {
- builder.updateSpeed(12345000, 128.0, 12344000, 0.0);
- assertEquals(0.0, builder.getTripStatistics().getMaxSpeed());
+ long time = 12344000;
+ tripStatisticsBuilder.updateSpeed(time + ONE_SECOND, 128.0, time, 0.0);
+ assertEquals(0.0, tripStatisticsBuilder.getTripStatistics().getMaxSpeed());
}
+ /**
+ * Tests {@link TripStatisticsBuilder#updateSpeed(long, double, long, double)}
+ * with a large speed change. The speed should get ignored.
+ */
public void testUpdateSpeedIngoreLargeAcceleration() {
- builder.updateSpeed(12345000, 100.0, 12344000, 1.0);
- assertEquals(0.0, builder.getTripStatistics().getMaxSpeed());
+ long time = 12344000;
+ tripStatisticsBuilder.updateSpeed(time + ONE_SECOND, 100.0, time, 1.0);
+ assertEquals(0.0, tripStatisticsBuilder.getTripStatistics().getMaxSpeed());
}
+ /**
+ * Tests {@link TripStatisticsBuilder#updateSpeed(long, double, long, double)}
+ * with constant speed.
+ */
public void testUpdateSpeed() {
+ double speed = 4.0;
for (int i = 0; i < 1000; i++) {
- builder.updateSpeed(i + 1000, 4.0, i, 4.0);
- if (i > Constants.SPEED_SMOOTHING_FACTOR) {
- assertEquals(4.0, builder.getTripStatistics().getMaxSpeed());
+ tripStatisticsBuilder.updateSpeed(i + ONE_SECOND, speed, i, speed);
+ if (i >= Constants.SPEED_SMOOTHING_FACTOR) {
+ assertEquals(speed, tripStatisticsBuilder.getTripStatistics().getMaxSpeed());
}
}
}