mirror of
https://codeberg.org/OpenTracksApp/OpenTracks.git
synced 2026-10-02 09:33:06 +02:00
Add support for pause and resume in TrackRecordingService.
This commit is contained in:
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
@@ -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);
|
||||
|
||||
+1
-1
@@ -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);
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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";
|
||||
|
||||
+244
-234
@@ -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()) {
|
||||
|
||||
+3
-3
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -23,6 +23,8 @@ limitations under the License.
|
||||
|
||||
<!-- Broadcast notification Intent constants -->
|
||||
<string name="track_started_broadcast_action">com.google.android.apps.mytracks.TRACK_STARTED</string>
|
||||
<string name="track_paused_broadcast_action">com.google.android.apps.mytracks.TRACK_PAUSED</string>
|
||||
<string name="track_resumed_broadcast_action">com.google.android.apps.mytracks.TRACK_RESUMED</string>
|
||||
<string name="track_stopped_broadcast_action">com.google.android.apps.mytracks.TRACK_STOPPED</string>
|
||||
<!-- Next API refactor depricate track_id_broadcast_extra and rename it to remove the broadcast as it is used when SHARING tracks as -->
|
||||
<string name="track_id_broadcast_extra">com.google.android.apps.mytracks.TRACK_ID</string>
|
||||
|
||||
@@ -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());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
+38
-13
@@ -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<TestRecordingServ
|
||||
MyTracksProvider myTracksProvider = new MyTracksProvider();
|
||||
myTracksProvider.attachInfo(context, null);
|
||||
mockContentResolver.addProvider(MyTracksProviderUtils.AUTHORITY, myTracksProvider);
|
||||
|
||||
MockContentProvider settingsProvider = new MockContentProvider(context) {
|
||||
@Override
|
||||
public Bundle call(String method, String arg, Bundle extras) {
|
||||
@@ -155,7 +157,36 @@ public class TrackRecordingServiceTest extends ServiceTestCase<TestRecordingServ
|
||||
}
|
||||
};
|
||||
mockContentResolver.addProvider(Settings.AUTHORITY, settingsProvider);
|
||||
|
||||
|
||||
MockContentProvider googleSettingsProvider = new MockContentProvider(context) {
|
||||
@Override
|
||||
public Bundle call(String method, String arg, Bundle extras) {
|
||||
return null;
|
||||
}
|
||||
@Override
|
||||
public Cursor query(Uri uri, String[] projection, String selection, String[] selectionArgs,
|
||||
String sortOrder) {
|
||||
MockCursor mockCursor = new MockCursor() {
|
||||
@Override
|
||||
public int getCount() {
|
||||
return 1;
|
||||
}
|
||||
@Override
|
||||
public boolean moveToNext() {
|
||||
return true;
|
||||
}
|
||||
@Override
|
||||
public String getString(int columnIndex) {
|
||||
return MyTracksLocationManager.USE_LOCATION_FOR_SERVICES_ON;
|
||||
}
|
||||
@Override
|
||||
public void close() {}
|
||||
};
|
||||
return mockCursor;
|
||||
}
|
||||
};
|
||||
mockContentResolver.addProvider("com.google.settings", googleSettingsProvider);
|
||||
|
||||
// Set the context
|
||||
setContext(context);
|
||||
|
||||
@@ -483,12 +514,8 @@ public class TrackRecordingServiceTest extends ServiceTestCase<TestRecordingServ
|
||||
ITrackRecordingService service = bindAndGetService(createStartIntent());
|
||||
assertFalse(service.isRecording());
|
||||
|
||||
try {
|
||||
service.insertWaypoint(WaypointCreationRequest.DEFAULT_STATISTICS);
|
||||
fail("Expecting IllegalStateException");
|
||||
} catch (IllegalStateException e) {
|
||||
// Expected.
|
||||
}
|
||||
long waypointId = service.insertWaypoint(WaypointCreationRequest.DEFAULT_STATISTICS);
|
||||
assertEquals(-1L, waypointId);
|
||||
}
|
||||
|
||||
@MediumTest
|
||||
@@ -497,6 +524,7 @@ public class TrackRecordingServiceTest extends ServiceTestCase<TestRecordingServ
|
||||
|
||||
ITrackRecordingService service = bindAndGetService(createStartIntent());
|
||||
assertTrue(service.isRecording());
|
||||
assertFalse(service.isPaused());
|
||||
insertLocation(service);
|
||||
|
||||
assertEquals(1, service.insertWaypoint(WaypointCreationRequest.DEFAULT_STATISTICS));
|
||||
@@ -520,12 +548,8 @@ public class TrackRecordingServiceTest extends ServiceTestCase<TestRecordingServ
|
||||
ITrackRecordingService service = bindAndGetService(createStartIntent());
|
||||
assertFalse(service.isRecording());
|
||||
|
||||
try {
|
||||
service.insertWaypoint(WaypointCreationRequest.DEFAULT_WAYPOINT);
|
||||
fail("Expecting IllegalStateException");
|
||||
} catch (IllegalStateException e) {
|
||||
// Expected.
|
||||
}
|
||||
long waypointId = service.insertWaypoint(WaypointCreationRequest.DEFAULT_WAYPOINT);
|
||||
assertEquals(-1L, waypointId);
|
||||
}
|
||||
|
||||
@MediumTest
|
||||
@@ -700,6 +724,7 @@ public class TrackRecordingServiceTest extends ServiceTestCase<TestRecordingServ
|
||||
assertEquals(track.getId(), providerUtils.getTrack(track.getId()).getId());
|
||||
PreferencesUtils.setLong(context, R.string.recording_track_id_key, isRecording ? track.getId()
|
||||
: PreferencesUtils.RECORDING_TRACK_ID_DEFAULT);
|
||||
PreferencesUtils.setBoolean(context, R.string.recording_track_paused_key, !isRecording);
|
||||
}
|
||||
|
||||
private void fullRecordingSession() throws Exception {
|
||||
|
||||
+118
-66
@@ -3,31 +3,35 @@
|
||||
package com.google.android.apps.mytracks.stats;
|
||||
|
||||
import com.google.android.apps.mytracks.Constants;
|
||||
import com.google.android.apps.mytracks.util.PreferencesUtils;
|
||||
|
||||
import android.location.Location;
|
||||
|
||||
import junit.framework.TestCase;
|
||||
|
||||
/**
|
||||
* Test the the function of the TripStatisticsBuilder class.
|
||||
* Tests {@link TripStatisticsBuilder}.
|
||||
*
|
||||
* @author Sandor Dornbush
|
||||
*/
|
||||
public class TripStatisticsBuilderTest extends TestCase {
|
||||
|
||||
private TripStatisticsBuilder builder = null;
|
||||
private static final long ONE_SECOND = 1000;
|
||||
private static final long TEN_SECONDS = 10 * ONE_SECOND;
|
||||
|
||||
private TripStatisticsBuilder tripStatisticsBuilder = null;
|
||||
|
||||
@Override
|
||||
protected void setUp() throws Exception {
|
||||
super.setUp();
|
||||
builder = new TripStatisticsBuilder(System.currentTimeMillis());
|
||||
tripStatisticsBuilder = new TripStatisticsBuilder(System.currentTimeMillis());
|
||||
}
|
||||
|
||||
public void testAddLocationSimple() throws Exception {
|
||||
builder = new TripStatisticsBuilder(1000);
|
||||
TripStatistics tripStatistics = builder.getTripStatistics();
|
||||
long startTime = 1000;
|
||||
tripStatisticsBuilder = new TripStatisticsBuilder(startTime);
|
||||
TripStatistics tripStatistics = tripStatisticsBuilder.getTripStatistics();
|
||||
|
||||
assertEquals(0.0, builder.getSmoothedElevation());
|
||||
assertEquals(0.0, tripStatisticsBuilder.getSmoothedElevation());
|
||||
assertEquals(Double.POSITIVE_INFINITY, tripStatistics.getMinElevation());
|
||||
assertEquals(Double.NEGATIVE_INFINITY, tripStatistics.getMaxElevation());
|
||||
assertEquals(0.0, tripStatistics.getMaxSpeed());
|
||||
@@ -36,6 +40,8 @@ public class TripStatisticsBuilderTest extends TestCase {
|
||||
assertEquals(0.0, tripStatistics.getTotalElevationGain());
|
||||
assertEquals(0, tripStatistics.getMovingTime());
|
||||
assertEquals(0.0, tripStatistics.getTotalDistance());
|
||||
|
||||
float speed = 11.1f;
|
||||
for (int i = 0; i < 100; i++) {
|
||||
Location location = new Location("test");
|
||||
location.setAccuracy(1.0f);
|
||||
@@ -43,126 +49,172 @@ public class TripStatisticsBuilderTest extends TestCase {
|
||||
|
||||
// Going up by 1 meter each time.
|
||||
location.setAltitude(i);
|
||||
location.setLatitude(i * .001); // Moving by .001 degree latitude (111 meters)
|
||||
location.setSpeed(11.1f);
|
||||
// Each time slice is 10 seconds.
|
||||
long time = 1000 + 10000 * i;
|
||||
location.setTime(time);
|
||||
boolean moving = builder.addLocation(location, time);
|
||||
assertEquals((i != 0), moving);
|
||||
|
||||
tripStatistics = builder.getTripStatistics();
|
||||
assertEquals(10000 * i, tripStatistics.getTotalTime());
|
||||
assertEquals(10000 * i, tripStatistics.getMovingTime());
|
||||
assertEquals(i, builder.getSmoothedElevation(), Constants.ELEVATION_SMOOTHING_FACTOR / 2);
|
||||
// Moving by .001 degree latitude (111 meters)
|
||||
location.setLatitude(i * .001);
|
||||
|
||||
location.setSpeed(speed);
|
||||
|
||||
// Each time slice is 10 seconds.
|
||||
location.setTime(startTime + i * TEN_SECONDS);
|
||||
tripStatisticsBuilder.addLocation(location, PreferencesUtils.MIN_RECORDING_DISTANCE_DEFAULT);
|
||||
|
||||
tripStatistics = tripStatisticsBuilder.getTripStatistics();
|
||||
assertEquals(i * TEN_SECONDS, tripStatistics.getTotalTime());
|
||||
assertEquals(i * TEN_SECONDS, tripStatistics.getMovingTime());
|
||||
assertEquals(i, tripStatisticsBuilder.getSmoothedElevation(),
|
||||
Constants.ELEVATION_SMOOTHING_FACTOR / 2);
|
||||
assertEquals(0.0, tripStatistics.getMinElevation());
|
||||
assertEquals(i, tripStatistics.getMaxElevation(), Constants.ELEVATION_SMOOTHING_FACTOR / 2);
|
||||
assertEquals(i, tripStatistics.getTotalElevationGain(), Constants.ELEVATION_SMOOTHING_FACTOR);
|
||||
|
||||
if (i > 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());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user