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 537da7eac..2bfa6c1a2 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, lastSegmentLocation); + tripStatisticsBuilder.addLocation(location, location.getTime()); // 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 e975f97f7..31370e64a 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, lastLocation); + tripStatisticsBuilder.addLocation(location, location.getTime()); // 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 5d1be7e9e..e46720b8c 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, lastLocation); + tripStatisticsBuilder.addLocation(location, location.getTime()); // 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 af8dab066..806d9c6ac 100644 --- a/MyTracks/src/com/google/android/apps/mytracks/services/TrackRecordingService.java +++ b/MyTracks/src/com/google/android/apps/mytracks/services/TrackRecordingService.java @@ -25,6 +25,7 @@ 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; @@ -103,11 +104,24 @@ 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 @@ -181,6 +195,10 @@ 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)) { @@ -238,6 +256,7 @@ public class TrackRecordingService extends Service { if (isRecording()) { handler.post(new Runnable() { public void run() { + unregisterLocationListener(); registerLocationListener(); } }); @@ -279,9 +298,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. */ - Track track = myTracksProviderUtils.getTrack(recordingTrackId); - if (track != null) { - restartTrack(track); + recordingTrack = myTracksProviderUtils.getTrack(recordingTrackId); + if (recordingTrack != null) { + restartTrack(recordingTrack); } else { if (recordingTrackId != PreferencesUtils.RECORDING_TRACK_ID_DEFAULT) { Log.w(TAG, "recordingTrackId not -1L, but recordingTrack is null. " + recordingTrackId); @@ -432,14 +451,11 @@ 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) { - TripStatistics trackTripStatistics = trackTripStatisticsBuilder.getTripStatistics(); - length = trackTripStatistics.getTotalDistance(); - duration = location.getTime() - trackTripStatistics.getStartTime(); + duration = location.getTime() - trackTripStatisticsBuilder.getTripStatistics().getStartTime(); } else { if (!waypointCreationRequest.isTrackStatistics()) { return -1L; @@ -448,7 +464,6 @@ 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, @@ -484,7 +499,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(myTracksProviderUtils.getTrack(recordingTrackId))) { + if (!shouldResumeTrack(recordingTrack)) { Log.i(TAG, "Stop resume track."); stopRecording(); stopSelfResult(startId); @@ -539,8 +554,12 @@ 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); @@ -572,8 +591,11 @@ 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); @@ -583,6 +605,7 @@ public class TrackRecordingService extends Service { markerStartTime = tripStatistics.getStartTime(); } markerTripStatisticsBuilder = new TripStatisticsBuilder(markerStartTime); + markerTripStatisticsBuilder.setMinRecordingDistance(minRecordingDistance); Cursor cursor = null; try { @@ -592,22 +615,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, lastValidTripLocation, location.getTime(), false, minRecordingDistance); - lastValidTripLocation = location; + trackTripStatisticsBuilder.addLocation(location, location.getTime()); if (location.getTime() > markerStartTime) { - markerTripStatisticsBuilder.addLocation( - location, lastValidMarkerLocation, location.getTime(), false, minRecordingDistance); - lastValidMarkerLocation = location; + markerTripStatisticsBuilder.addLocation(location, location.getTime()); } + 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); @@ -624,7 +647,11 @@ 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(); @@ -641,16 +668,19 @@ 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; @@ -671,27 +701,14 @@ public class TrackRecordingService extends Service { } /** - * Update track. + * Updates a {@link TripStatistics} to a time. * - * @param track the track - * @param lastTrackPointId the last track point id - * @param isLastTrackPointNew true if the last track point id is new + * @param tripStatistics the trip statistics + * @param time the time */ - 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); + private void updateTripStatisticsToTime(TripStatistics tripStatistics, long time) { + tripStatistics.setStopTime(time); + tripStatistics.setTotalTime(time - tripStatistics.getStartTime()); } /** @@ -701,13 +718,13 @@ public class TrackRecordingService extends Service { */ private void onLocationChangedAsync(Location location) { try { - if (!isRecording()) { - Log.w(TAG, "Ignore onLocationChangedAsync. Not recording."); + if (location == null) { + Log.w(TAG, "Ignore onLocationChangedAsync. location is null."); return; } - if (!LocationUtils.isValidLocation(location)) { - Log.w(TAG, "Ignore onLocationChangedAsync. Not valid location."); + if (!isRecording()) { + Log.w(TAG, "Ignore onLocationChangedAsync. Not recording."); return; } @@ -716,59 +733,73 @@ public class TrackRecordingService extends Service { return; } - Track track = myTracksProviderUtils.getTrack(recordingTrackId); - if (track == null) { + recordingTrack = myTracksProviderUtils.getTrack(recordingTrackId); + if (recordingTrack == null) { Log.d(TAG, "Ignore onLocationChangedAsync. recodingTrack is null."); return; } - Location lastLocation = myTracksProviderUtils.getLastTrackLocation(recordingTrackId); - if (!LocationUtils.isValidLocation(lastLocation)) { - lastLocation = null; + locationListenerPolicy.updateIdleTime(trackTripStatisticsBuilder.getIdleTime()); + + if (LocationUtils.isValidLocation(location)) { + long now = System.currentTimeMillis(); + trackTripStatisticsBuilder.addLocation(location, now); + markerTripStatisticsBuilder.addLocation(location, now); } - // 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(); - 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); + + // 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; + } } - - if (hasSensorData) { - location = new MyTracksLocation(location, sensorManager.getSensorDataSet()); + } + 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; + } } - 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); + } + + 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; } - announcementExecutor.update(); - splitExecutor.update(); + lastLocation = location; + } else { + Log.d(TAG, "Ignore location. distanceToLastRecordedLocation < minRecordingDistance."); } } catch (Error e) { Log.e(TAG, "Error in onLocationChangedAsync", e); @@ -779,11 +810,84 @@ 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() { - unregisterLocationListener(); + if (locationManager == null) { + Log.e(TAG, "locationManager is null."); + return; + } 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 3514145f5..18f7103b9 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 track statistics as new - * locations are added. + * Builder for {@link TripStatistics}. For keeping statistics as a track is + * paused/resumed and new locations are added. * * @author Sandor Dornbush * @author Rodrigo Damazio @@ -37,6 +37,18 @@ 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); @@ -51,85 +63,88 @@ public class TripStatisticsBuilder { private final DoubleBuffer gradeBuffer = new DoubleBuffer(Constants.GRADE_SMOOTHING_FACTOR); /** - * Creates a new {@link TripStatistics} starting at a start time. + * Creates a new trip starting at a start time. * * @param startTime the start time */ public TripStatisticsBuilder(long startTime) { tripStatistics = new TripStatistics(); - tripStatistics.setStartTime(startTime); + resumeAt(startTime); } /** - * Pauses the {@link TripStatistics} at a stop time. + * Creates a new trip, starting with an existing {@link TripStatistics}. * - * @param stopTime the stop time + * @param other the existing {@link TripStatistics} */ - public void pauseAt(long stopTime) { - tripStatistics.setStopTime(stopTime); - tripStatistics.setTotalTime(stopTime - tripStatistics.getStartTime()); + public TripStatisticsBuilder(TripStatistics other) { + tripStatistics = new TripStatistics(other); + if (tripStatistics.getStartTime() > 0) { + resumeAt(tripStatistics.getStartTime()); + } } /** - * Gets the {@link TripStatistics}. + * 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. */ public TripStatistics getTripStatistics() { - // Take a snapshot - we don't want anyone messing with our tripStatistics + // Take a snapshot - we don't want anyone messing with our internals return new TripStatistics(tripStatistics); } /** - * Adds a location. - * - * @param location the location - * @param lastLocation the last location + * Returns the amount of time the user has been idle or 0 if he is moving. */ - public void addLocation(Location location, Location lastLocation) { - addLocation(location, lastLocation, location.getTime(), false, - PreferencesUtils.MIN_RECORDING_DISTANCE_DEFAULT); - } - - /** - * Adds a location. This will update all of the internal variables with this - * new location. - * - * @param location the location - * @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, Location lastLocation, long time, boolean alwaysAdd, - int minRecordingDistance) { - pauseAt(time); - - double elevationDifference = updateElevation(location.getAltitude()); - tripStatistics.updateLatitudeExtremities(location.getLatitude()); - tripStatistics.updateLongitudeExtremities(location.getLongitude()); - - // If lastLocation is null, returns true. - if (lastLocation == null) { - return true; + public long getIdleTime() { + if (lastLocation == null || lastMovingLocation == null) { + return 0; } - - double distance = lastLocation.distanceTo(location); - if (distance < minRecordingDistance && location.getSpeed() < Constants.MAX_NO_MOVEMENT_SPEED) { - return alwaysAdd; - } - - long movingTime = location.getTime() - lastLocation.getTime(); - if (movingTime < 0) { - Log.e(TAG, "Negative moving time: " + movingTime); - return alwaysAdd; - } - - tripStatistics.addTotalDistance(distance); - tripStatistics.addMovingTime(movingTime); - updateSpeed( - location.getTime(), location.getSpeed(), lastLocation.getTime(), lastLocation.getSpeed()); - updateGrade(distance, elevationDifference); - return true; + return lastLocation.getTime() - lastMovingLocation.getTime(); } /** @@ -137,11 +152,62 @@ 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(); } + /** + * 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. * 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 c00c1aed9..d3656a64e 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 = -1L; + private long id = -1; private String name = ""; private String description = ""; private String category = ""; - private long startId = -1L; - private long stopId = -1L; + private long startId = -1; + private long stopId = -1; // 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 c95ed1acf..8b897e2e3 100644 --- a/MyTracksTest/src/com/google/android/apps/mytracks/stats/TripStatisticsBuilderTest.java +++ b/MyTracksTest/src/com/google/android/apps/mytracks/stats/TripStatisticsBuilderTest.java @@ -36,7 +36,6 @@ 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); @@ -49,7 +48,8 @@ public class TripStatisticsBuilderTest extends TestCase { // Each time slice is 10 seconds. long time = 1000 + 10000 * i; location.setTime(time); - builder.addLocation(location, lastLocation); + boolean moving = builder.addLocation(location, time); + assertEquals((i != 0), moving); tripStatistics = builder.getTripStatistics(); assertEquals(10000 * i, tripStatistics.getTotalTime()); @@ -68,7 +68,6 @@ public class TripStatisticsBuilderTest extends TestCase { assertEquals(0.009, tripStatistics.getMaxGrade(), 0.0001); } assertEquals(111.0 * i, tripStatistics.getTotalDistance(), 100); - lastLocation = location; } }