From 1a6baf2bebaaddbe2564c47011e6d7885825c437 Mon Sep 17 00:00:00 2001 From: Jimmy Shih Date: Mon, 13 Aug 2012 15:14:00 -0700 Subject: [PATCH] Refactor location recording logic so that all location filtering code is done at TripStatisticsBuilder.java. --- .../apps/mytracks/io/file/GpxImporter.java | 2 +- .../SendFusionTablesAsyncTask.java | 2 +- .../mytracks/io/maps/SendMapsAsyncTask.java | 2 +- .../services/TrackRecordingService.java | 272 ++++++------------ .../mytracks/stats/TripStatisticsBuilder.java | 158 +++------- .../android/apps/mytracks/content/Track.java | 6 +- .../stats/TripStatisticsBuilderTest.java | 5 +- 7 files changed, 139 insertions(+), 308 deletions(-) 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..537da7eac 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 @@ -380,7 +380,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, lastSegmentLocation); // 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..e975f97f7 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, lastLocation); // 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..5d1be7e9e 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, lastLocation); // 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/TrackRecordingService.java b/MyTracks/src/com/google/android/apps/mytracks/services/TrackRecordingService.java index 806d9c6ac..af8dab066 100644 --- a/MyTracks/src/com/google/android/apps/mytracks/services/TrackRecordingService.java +++ b/MyTracks/src/com/google/android/apps/mytracks/services/TrackRecordingService.java @@ -25,7 +25,6 @@ import com.google.android.apps.mytracks.content.MyTracksLocation; import com.google.android.apps.mytracks.content.MyTracksProvider; import com.google.android.apps.mytracks.content.MyTracksProviderUtils; import com.google.android.apps.mytracks.content.Sensor; -import com.google.android.apps.mytracks.content.Sensor.SensorDataSet; import com.google.android.apps.mytracks.content.Track; import com.google.android.apps.mytracks.content.Waypoint; import com.google.android.apps.mytracks.content.WaypointCreationRequest; @@ -104,24 +103,11 @@ 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; // Timer to periodically invoke checkLocationListener @@ -195,10 +181,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 || PreferencesUtils.getKey(context, R.string.max_recording_distance_key) .equals(key)) { @@ -256,7 +238,6 @@ public class TrackRecordingService extends Service { if (isRecording()) { handler.post(new Runnable() { public void run() { - unregisterLocationListener(); registerLocationListener(); } }); @@ -298,9 +279,9 @@ public class TrackRecordingService extends Service { * 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); @@ -451,11 +432,14 @@ public class TrackRecordingService extends Service { isStatistics ? R.string.marker_statistics_icon_url : R.string.marker_waypoint_icon_url); int type = isStatistics ? Waypoint.TYPE_STATISTICS : Waypoint.TYPE_WAYPOINT; + double length; long duration; Location location = myTracksProviderUtils.getLastTrackLocation(recordingTrackId); if (location != null && trackTripStatisticsBuilder != null && trackTripStatisticsBuilder.getTripStatistics() != null) { - duration = location.getTime() - trackTripStatisticsBuilder.getTripStatistics().getStartTime(); + TripStatistics trackTripStatistics = trackTripStatisticsBuilder.getTripStatistics(); + length = trackTripStatistics.getTotalDistance(); + duration = location.getTime() - trackTripStatistics.getStartTime(); } else { if (!waypointCreationRequest.isTrackStatistics()) { return -1L; @@ -464,6 +448,7 @@ public class TrackRecordingService extends Service { location = new Location(""); location.setLatitude(100); location.setLongitude(180); + length = 0; duration = 0; } Waypoint waypoint = new Waypoint(name, description, category, icon, recordingTrackId, type, @@ -499,7 +484,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(myTracksProviderUtils.getTrack(recordingTrackId))) { Log.i(TAG, "Stop resume track."); stopRecording(); stopSelfResult(startId); @@ -554,12 +539,8 @@ 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); Track track = new Track(); track.setStartId(-1L); @@ -591,11 +572,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); @@ -605,7 +583,6 @@ public class TrackRecordingService extends Service { markerStartTime = tripStatistics.getStartTime(); } markerTripStatisticsBuilder = new TripStatisticsBuilder(markerStartTime); - markerTripStatisticsBuilder.setMinRecordingDistance(minRecordingDistance); Cursor cursor = null; try { @@ -615,22 +592,22 @@ public class TrackRecordingService extends Service { Log.e(TAG, "Cursor is null."); } else { if (cursor.moveToLast()) { + Location lastValidTripLocation = null; + Location lastValidMarkerLocation = null; do { Location location = myTracksProviderUtils.createLocation(cursor); if (LocationUtils.isValidLocation(location)) { - trackTripStatisticsBuilder.addLocation(location, location.getTime()); + trackTripStatisticsBuilder.addLocation( + location, lastValidTripLocation, location.getTime(), false, minRecordingDistance); + lastValidTripLocation = location; if (location.getTime() > markerStartTime) { - markerTripStatisticsBuilder.addLocation(location, location.getTime()); + markerTripStatisticsBuilder.addLocation( + location, lastValidMarkerLocation, location.getTime(), false, minRecordingDistance); + lastValidMarkerLocation = location; } - if (lastLengthLocation != null) { - length += location.distanceTo(lastLengthLocation); - } - lastLengthLocation = location; } } while (cursor.moveToPrevious()); } - trackTripStatisticsBuilder.pauseAt(tripStatistics.getStopTime()); - trackTripStatisticsBuilder.resumeAt(System.currentTimeMillis()); } } catch (RuntimeException e) { Log.e(TAG, "RuntimeException", e); @@ -647,11 +624,7 @@ public class TrackRecordingService extends Service { */ private void startRecording() { acquireWakeLock(); - - lastLocation = null; - isMoving = true; sensorManager = SensorManagerFactory.getSystemSensorManager(this); - showNotification(); sendTrackBroadcast(R.string.track_started_broadcast_action, recordingTrackId); announcementExecutor.restore(); @@ -668,19 +641,16 @@ public class TrackRecordingService extends Service { } 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 trackId before calling stopRecording long trackId = recordingTrackId; stopRecording(); + Track track = myTracksProviderUtils.getTrack(recordingTrackId); + if (track != null) { + updateTrack(track, myTracksProviderUtils.getLastTrackLocationId(recordingTrackId), false); + } + if (sensorManager != null) { SensorManagerFactory.releaseSystemSensorManager(); sensorManager = null; @@ -701,14 +671,27 @@ public class TrackRecordingService extends Service { } /** - * Updates a {@link TripStatistics} to a time. + * Update track. * - * @param tripStatistics the trip statistics - * @param time the time + * @param track the track + * @param lastTrackPointId the last track point id + * @param isLastTrackPointNew true if the last track point id is new */ - private void updateTripStatisticsToTime(TripStatistics tripStatistics, long time) { - tripStatistics.setStopTime(time); - tripStatistics.setTotalTime(time - tripStatistics.getStartTime()); + private void updateTrack(Track track, long lastTrackPointId, boolean isLastTrackPointNew) { + if (lastTrackPointId >= 0) { + if (track.getStartId() < 0) { + track.setStartId(lastTrackPointId); + } + track.setStopId(lastTrackPointId); + if (isLastTrackPointNew) { + track.setNumberOfPoints(track.getNumberOfPoints() + 1); + } + } + if (!isLastTrackPointNew) { + trackTripStatisticsBuilder.pauseAt(System.currentTimeMillis()); + } + track.setTripStatistics(trackTripStatisticsBuilder.getTripStatistics()); + myTracksProviderUtils.updateTrack(track); } /** @@ -718,13 +701,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()) { + Log.w(TAG, "Ignore onLocationChangedAsync. Not recording."); return; } - if (!isRecording()) { - Log.w(TAG, "Ignore onLocationChangedAsync. Not recording."); + if (!LocationUtils.isValidLocation(location)) { + Log.w(TAG, "Ignore onLocationChangedAsync. Not valid location."); return; } @@ -733,73 +716,59 @@ public class TrackRecordingService extends Service { return; } - recordingTrack = myTracksProviderUtils.getTrack(recordingTrackId); - if (recordingTrack == null) { + Track track = myTracksProviderUtils.getTrack(recordingTrackId); + if (track == 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 lastLocation = myTracksProviderUtils.getLastTrackLocation(recordingTrackId); + if (!LocationUtils.isValidLocation(lastLocation)) { + lastLocation = null; } + // Update location listener policy + locationListenerPolicy.updateIdleTime( + lastLocation != null ? location.getTime() - lastLocation.getTime() : 0L); 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; - } + long now = System.currentTimeMillis(); + markerTripStatisticsBuilder.addLocation(location, lastLocation, now, hasSensorData, minRecordingDistance); + if (trackTripStatisticsBuilder.addLocation( + location, lastLocation, now, hasSensorData, minRecordingDistance)) { + try { + if (lastLocation != null && location.distanceTo(lastLocation) + > maxRecordingDistance) { + Log.d(TAG, "Inserting a separator track point."); + Location separator = new Location(LocationManager.GPS_PROVIDER); + separator.setLongitude(0); + separator.setLatitude(100); + separator.setTime(lastLocation.getTime()); + myTracksProviderUtils.insertTrackPoint(separator, recordingTrackId); } - } - 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; - } + + if (hasSensorData) { + location = new MyTracksLocation(location, sensorManager.getSensorDataSet()); } - } - - 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)) { + Uri uri = myTracksProviderUtils.insertTrackPoint(location, recordingTrackId); + int trackPointId = Integer.parseInt(uri.getLastPathSegment()); + + updateTrack(track, trackPointId, true); + } catch (SQLiteException e) { + /* + * Insert failed, most likely because of SqlLite error code 5 + * (SQLite_BUSY). This is expected to happen extremely rarely (if our + * listener gets invoked twice at about the same time). + */ + Log.w(TAG, "SQLiteException", e); return; } - lastLocation = location; - } else { - Log.d(TAG, "Ignore location. distanceToLastRecordedLocation < minRecordingDistance."); + announcementExecutor.update(); + splitExecutor.update(); } } catch (Error e) { Log.e(TAG, "Error in onLocationChangedAsync", e); @@ -810,84 +779,11 @@ 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; - } - - 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); - } - } - } catch (SQLiteException e) { - /* - * Insert failed, most likely because of SqlLite error code 5 - * (SQLite_BUSY). This is expected to happen extremely rarely (if our - * listener gets invoked twice at about the same time). - */ - Log.w(TAG, "SQLiteException", e); - return false; - } - announcementExecutor.update(); - splitExecutor.update(); - return true; - } - /** * Registers the location listener. */ private void registerLocationListener() { - if (locationManager == null) { - Log.e(TAG, "locationManager is null."); - return; - } + unregisterLocationListener(); try { long interval = locationListenerPolicy.getDesiredPollingInterval(); locationManager.requestLocationUpdates(LocationManager.GPS_PROVIDER, interval, 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..3514145f5 100644 --- a/MyTracks/src/com/google/android/apps/mytracks/stats/TripStatisticsBuilder.java +++ b/MyTracks/src/com/google/android/apps/mytracks/stats/TripStatisticsBuilder.java @@ -26,8 +26,8 @@ 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. * * @author Sandor Dornbush * @author Rodrigo Damazio @@ -37,18 +37,6 @@ public class TripStatisticsBuilder { // The trip statistics. private final TripStatistics tripStatistics; - // The minimum recording distance. - private int minRecordingDistance = PreferencesUtils.MIN_RECORDING_DISTANCE_DEFAULT; - - // True if the trip is paused. All trips start as paused. - private boolean paused = true; - - // The last location as reported by GPS. - private Location lastLocation; - - // The last moving location that contributed to the moving statistics. - private Location lastMovingLocation; - // A buffer of the recent speed readings (m/s) for calculating max speed. private final DoubleBuffer speedBuffer = new DoubleBuffer(Constants.SPEED_SMOOTHING_FACTOR); @@ -63,97 +51,42 @@ public class TripStatisticsBuilder { private final DoubleBuffer gradeBuffer = new DoubleBuffer(Constants.GRADE_SMOOTHING_FACTOR); /** - * Creates a new trip starting at a start time. + * Creates a new {@link TripStatistics} starting at a start time. * * @param startTime the start time */ public TripStatisticsBuilder(long startTime) { tripStatistics = new TripStatistics(); - resumeAt(startTime); + tripStatistics.setStartTime(startTime); } /** - * Creates a new trip, starting with an existing {@link TripStatistics}. + * Pauses the {@link TripStatistics} at a stop time. * - * @param other the existing {@link TripStatistics} + * @param stopTime the stop time */ - public TripStatisticsBuilder(TripStatistics other) { - tripStatistics = new TripStatistics(other); - if (tripStatistics.getStartTime() > 0) { - resumeAt(tripStatistics.getStartTime()); - } + public void pauseAt(long stopTime) { + tripStatistics.setStopTime(stopTime); + tripStatistics.setTotalTime(stopTime - 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 {@link TripStatistics}. */ public TripStatistics getTripStatistics() { - // Take a snapshot - we don't want anyone messing with our internals + // Take a snapshot - we don't want anyone messing with our tripStatistics return new TripStatistics(tripStatistics); } /** - * Returns the amount of time the user has been idle or 0 if he is moving. + * Adds a location. + * + * @param location the location + * @param lastLocation the last location */ - public long getIdleTime() { - if (lastLocation == null || lastMovingLocation == null) { - return 0; - } - return lastLocation.getTime() - lastMovingLocation.getTime(); - } - - /** - * Gets the smoothed elevation over several readings. The elevation readings - * is noisy so the smoothed elevation is better than the raw elevation for - * many tasks. - */ - public double getSmoothedElevation() { - return elevationBuffer.getAverage(); + public void addLocation(Location location, Location lastLocation) { + addLocation(location, lastLocation, location.getTime(), false, + PreferencesUtils.MIN_RECORDING_DISTANCE_DEFAULT); } /** @@ -161,53 +94,54 @@ public class TripStatisticsBuilder { * 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 + * @param lastLocation the last location + * @param time the time + * @param alwaysAdd true to always return true + * @param minRecordingDistance the min recording distance + * @return true if the location should be added */ - public boolean addLocation(Location location, long systemTime) { - if (paused) { - Log.w(TAG, "Track is paused. Ignore addLocation."); - return false; - } - - tripStatistics.setTotalTime(systemTime - tripStatistics.getStartTime()); + public boolean addLocation(Location location, Location lastLocation, long time, boolean alwaysAdd, + int minRecordingDistance) { + pauseAt(time); 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; + // If lastLocation is null, returns true. + if (lastLocation == null) { + return true; } - // 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; + return alwaysAdd; } - long timeDifference = location.getTime() - lastLocation.getTime(); - if (timeDifference < 0) { - Log.e(TAG, "Negative time difference: " + timeDifference); - lastLocation = location; - return false; + long movingTime = location.getTime() - lastLocation.getTime(); + if (movingTime < 0) { + Log.e(TAG, "Negative moving time: " + movingTime); + return alwaysAdd; } - tripStatistics.addTotalDistance(lastMovingLocation.distanceTo(location)); - tripStatistics.addMovingTime(timeDifference); + tripStatistics.addTotalDistance(distance); + tripStatistics.addMovingTime(movingTime); updateSpeed( location.getTime(), location.getSpeed(), lastLocation.getTime(), lastLocation.getSpeed()); updateGrade(distance, elevationDifference); - lastLocation = location; - lastMovingLocation = location; return true; } + /** + * Gets the smoothed elevation over several readings. The elevation readings + * is noisy so the smoothed elevation is better than the raw elevation for + * many tasks. + */ + @VisibleForTesting + double getSmoothedElevation() { + return elevationBuffer.getAverage(); + } + /** * Updates a speed reading. Assumes the user is moving. * diff --git a/MyTracksLib/src/com/google/android/apps/mytracks/content/Track.java b/MyTracksLib/src/com/google/android/apps/mytracks/content/Track.java index d3656a64e..c00c1aed9 100644 --- a/MyTracksLib/src/com/google/android/apps/mytracks/content/Track.java +++ b/MyTracksLib/src/com/google/android/apps/mytracks/content/Track.java @@ -32,12 +32,12 @@ import java.util.ArrayList; */ public class Track implements Parcelable { - private long id = -1; + private long id = -1L; private String name = ""; private String description = ""; private String category = ""; - private long startId = -1; - private long stopId = -1; + private long startId = -1L; + private long stopId = -1L; // The number of location points (present even if the points themselves are // not loaded) diff --git a/MyTracksTest/src/com/google/android/apps/mytracks/stats/TripStatisticsBuilderTest.java b/MyTracksTest/src/com/google/android/apps/mytracks/stats/TripStatisticsBuilderTest.java index 8b897e2e3..c95ed1acf 100644 --- a/MyTracksTest/src/com/google/android/apps/mytracks/stats/TripStatisticsBuilderTest.java +++ b/MyTracksTest/src/com/google/android/apps/mytracks/stats/TripStatisticsBuilderTest.java @@ -36,6 +36,7 @@ public class TripStatisticsBuilderTest extends TestCase { assertEquals(0.0, tripStatistics.getTotalElevationGain()); assertEquals(0, tripStatistics.getMovingTime()); assertEquals(0.0, tripStatistics.getTotalDistance()); + Location lastLocation = null; for (int i = 0; i < 100; i++) { Location location = new Location("test"); location.setAccuracy(1.0f); @@ -48,8 +49,7 @@ public class TripStatisticsBuilderTest extends TestCase { // Each time slice is 10 seconds. long time = 1000 + 10000 * i; location.setTime(time); - boolean moving = builder.addLocation(location, time); - assertEquals((i != 0), moving); + builder.addLocation(location, lastLocation); tripStatistics = builder.getTripStatistics(); assertEquals(10000 * i, tripStatistics.getTotalTime()); @@ -68,6 +68,7 @@ public class TripStatisticsBuilderTest extends TestCase { assertEquals(0.009, tripStatistics.getMaxGrade(), 0.0001); } assertEquals(111.0 * i, tripStatistics.getTotalDistance(), 100); + lastLocation = location; } }