forked from upstream-mirrors/OpenTracks
TrackPoint is now a record.
This commit is contained in:
@@ -22,11 +22,11 @@ import de.dennisguse.opentracks.data.models.Statistics;
|
||||
import de.dennisguse.opentracks.data.models.Track;
|
||||
import de.dennisguse.opentracks.data.models.TrackBuilder;
|
||||
import de.dennisguse.opentracks.data.models.TrackPoint;
|
||||
import de.dennisguse.opentracks.data.statistics.TrackStatisticsUpdater;
|
||||
import de.dennisguse.opentracks.sensors.sensorData.SensorDataSet;
|
||||
import de.dennisguse.opentracks.services.handlers.AltitudeCorrectionManager;
|
||||
import de.dennisguse.opentracks.services.handlers.TrackPointCreator;
|
||||
import de.dennisguse.opentracks.settings.PreferencesUtils;
|
||||
import de.dennisguse.opentracks.data.statistics.TrackStatisticsUpdater;
|
||||
import de.dennisguse.opentracks.util.TrackNameUtils;
|
||||
|
||||
public class TrackRecordingManager implements SharedPreferences.OnSharedPreferenceChangeListener {
|
||||
@@ -160,14 +160,14 @@ public class TrackRecordingManager implements SharedPreferences.OnSharedPreferen
|
||||
* @return TrackPoint was stored?
|
||||
*/
|
||||
synchronized boolean onNewTrackPoint(@NonNull TrackPoint trackPoint) {
|
||||
if (trackPoint.hasSpeed()) {
|
||||
if (trackPoint.position().hasSpeed()) {
|
||||
lastTrackPointUIWithSpeed = trackPoint;
|
||||
}
|
||||
if (trackPoint.hasAltitude()) {
|
||||
if (trackPoint.position().hasAltitude()) {
|
||||
lastTrackPointUIWithAltitude = trackPoint;
|
||||
}
|
||||
|
||||
if (trackPoint.getType() == TrackPoint.Type.IDLE) {
|
||||
if (trackPoint.type() == TrackPoint.Type.IDLE) {
|
||||
insertTrackPoint(trackPoint, true);
|
||||
handler.removeCallbacks(ON_IDLE);
|
||||
return true;
|
||||
@@ -180,14 +180,14 @@ public class TrackRecordingManager implements SharedPreferences.OnSharedPreferen
|
||||
return true;
|
||||
}
|
||||
|
||||
if (trackPoint.hasLocation() && lastStoredTrackPointWithLocation == null) {
|
||||
if (trackPoint.position().hasLocation() && lastStoredTrackPointWithLocation == null) {
|
||||
insertTrackPoint(trackPoint, true);
|
||||
|
||||
scheduleNewIdleTimeout();
|
||||
return true;
|
||||
}
|
||||
|
||||
if (!trackPoint.hasLocation() && !trackPoint.hasSensorDistance()) {
|
||||
if (!trackPoint.position().hasLocation() && trackPoint.sensorDistance() == null) {
|
||||
Duration minStorageInterval = Duration.ofSeconds(10); // TODO Should be configurable.
|
||||
boolean shouldStore = lastStoredTrackPoint.getTime().plus(minStorageInterval)
|
||||
.isBefore(trackPoint.getTime());
|
||||
@@ -201,14 +201,14 @@ public class TrackRecordingManager implements SharedPreferences.OnSharedPreferen
|
||||
}
|
||||
|
||||
Distance distanceToLastStoredTrackPoint;
|
||||
if (trackPoint.hasLocation() && !lastStoredTrackPoint.hasLocation()) {
|
||||
if (trackPoint.position().hasLocation() && !lastStoredTrackPoint.position().hasLocation()) {
|
||||
distanceToLastStoredTrackPoint = trackPoint.distanceToPreviousFromLocation(lastStoredTrackPointWithLocation);
|
||||
} else {
|
||||
distanceToLastStoredTrackPoint = trackPoint.distanceToPrevious(lastStoredTrackPoint);
|
||||
}
|
||||
|
||||
if (distanceToLastStoredTrackPoint.greaterThan(maxRecordingDistance)) {
|
||||
trackPoint.setType(TrackPoint.Type.SEGMENT_START_AUTOMATIC);
|
||||
trackPoint = trackPoint.with(TrackPoint.Type.SEGMENT_START_AUTOMATIC);
|
||||
insertTrackPoint(trackPoint, true);
|
||||
|
||||
scheduleNewIdleTimeout();
|
||||
@@ -248,8 +248,8 @@ public class TrackRecordingManager implements SharedPreferences.OnSharedPreferen
|
||||
Log.w(TAG, "Ignore insertTrackPoint. trackPoint time same as last valid trackId point time.");
|
||||
} else {
|
||||
insertTrackPointHelper(lastTrackPoint);
|
||||
// Remove the sensorDistance from trackPoint that is already going be stored with lastTrackPoint.
|
||||
trackPoint.minusCumulativeSensorData(lastTrackPoint);
|
||||
// Remove the sensorDistance from trackPoint that is already going be stored with lastTrackPoint.
|
||||
trackPoint = trackPoint.minusCumulativeSensorData(lastTrackPoint);
|
||||
}
|
||||
}
|
||||
lastTrackPoint = null;
|
||||
@@ -264,7 +264,7 @@ public class TrackRecordingManager implements SharedPreferences.OnSharedPreferen
|
||||
|
||||
contentProviderUtils.updateTrackStatistics(trackId, trackStatisticsUpdater.getTrackStatistics());
|
||||
lastStoredTrackPoint = trackPoint;
|
||||
if (trackPoint.hasLocation()) {
|
||||
if (trackPoint.position().hasLocation()) {
|
||||
lastStoredTrackPointWithLocation = lastStoredTrackPoint;
|
||||
}
|
||||
} catch (SQLiteException e) {
|
||||
|
||||
@@ -255,7 +255,7 @@ public class TrackRecordingService extends Service implements TrackPointCreator.
|
||||
"",
|
||||
"",
|
||||
"",
|
||||
trackPoint.getPosition(),
|
||||
trackPoint.position(),
|
||||
null
|
||||
);
|
||||
return new ContentProviderUtils(this).insertMarker(marker);
|
||||
|
||||
+4
-4
@@ -109,17 +109,17 @@ class TrackRecordingServiceNotificationManager implements SharedPreferences.OnSh
|
||||
.build(context);
|
||||
|
||||
//TODO The decision if a trackpoint is not accurate enough should happen in the TrackRecordingManager (via RecordingData).
|
||||
if (recordingData.latestTrackPoint().hasHorizontalAccuracy()) {
|
||||
formattedAccuracy = formatter.formatDistance(recordingData.latestTrackPoint().getHorizontalAccuracy());
|
||||
if (recordingData.latestTrackPoint().position().hasHorizontalAccuracy()) {
|
||||
formattedAccuracy = formatter.formatDistance(recordingData.latestTrackPoint().position().horizontalAccuracy());
|
||||
|
||||
boolean currentLocationWasAccurate = recordingData.latestTrackPoint().getHorizontalAccuracy().lessThan(thresholdHorizontalAccuracy);
|
||||
boolean currentLocationWasAccurate = recordingData.latestTrackPoint().position().horizontalAccuracy().lessThan(thresholdHorizontalAccuracy);
|
||||
boolean shouldAlert = !currentLocationWasAccurate && previousLocationWasAccurate;
|
||||
notificationBuilder.setOnlyAlertOnce(!shouldAlert);
|
||||
previousLocationWasAccurate = currentLocationWasAccurate;
|
||||
}
|
||||
|
||||
notificationBuilder.setContentTitle(context.getString(R.string.track_distance_notification, formatter.formatDistance(recordingData.trackStatistics().totalDistance())));
|
||||
String formattedSpeed = SpeedFormatter.Builder().setUnit(unitSystem).setReportSpeedOrPace(true).build(context).formatSpeed(recordingData.latestTrackPoint().getSpeed());
|
||||
String formattedSpeed = SpeedFormatter.Builder().setUnit(unitSystem).setReportSpeedOrPace(true).build(context).formatSpeed(recordingData.latestTrackPoint().position().speed());
|
||||
notificationBuilder.setContentText(context.getString(R.string.track_speed_notification, formattedSpeed));
|
||||
notificationBuilder.setSubText(context.getString(R.string.track_recording_notification_accuracy, formattedAccuracy));
|
||||
updateNotification();
|
||||
|
||||
+5
-5
@@ -18,7 +18,6 @@ import android.text.Spannable;
|
||||
import android.text.SpannableString;
|
||||
import android.text.SpannableStringBuilder;
|
||||
import android.text.style.TtsSpan;
|
||||
import android.util.Pair;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.annotation.Nullable;
|
||||
@@ -35,9 +34,10 @@ import de.dennisguse.opentracks.data.models.HeartRate;
|
||||
import de.dennisguse.opentracks.data.models.Speed;
|
||||
import de.dennisguse.opentracks.data.models.Statistics;
|
||||
import de.dennisguse.opentracks.data.models.Track;
|
||||
import de.dennisguse.opentracks.data.statistics.SensorStatistics;
|
||||
import de.dennisguse.opentracks.sensors.sensorData.SensorData;
|
||||
import de.dennisguse.opentracks.sensors.sensorData.SensorDataSet;
|
||||
import de.dennisguse.opentracks.settings.UnitSystem;
|
||||
import de.dennisguse.opentracks.data.statistics.SensorStatistics;
|
||||
import de.dennisguse.opentracks.util.StringUtils;
|
||||
|
||||
class VoiceAnnouncementUtils {
|
||||
@@ -160,9 +160,9 @@ class VoiceAnnouncementUtils {
|
||||
}
|
||||
}
|
||||
|
||||
Pair<HeartRate, String> heartrate = sensorDataSet.getHeartRate();
|
||||
if (shouldVoiceAnnounceHeartRateCurrent() && heartrate != null && heartrate.first != null && heartrate.first.isValid()) { //TODO Check has an announcable value?
|
||||
int averageHeartRate = Math.round(heartrate.first.getBPM());
|
||||
SensorData<HeartRate> heartrate = sensorDataSet.heartRate();
|
||||
if (shouldVoiceAnnounceHeartRateCurrent() && heartrate != null && heartrate.data().isValid()) { //TODO Check has an announceable value?
|
||||
int averageHeartRate = Math.round(heartrate.data().getBPM());
|
||||
|
||||
builder.append(" ")
|
||||
.append(context.getString(R.string.current_heart_rate));
|
||||
|
||||
+14
-12
@@ -19,22 +19,22 @@ public class AltitudeCorrectionManager {
|
||||
|
||||
private static final String TAG = AltitudeCorrectionManager.class.getSimpleName();
|
||||
|
||||
public void correctAltitude(Context context, TrackPoint trackPoint) {
|
||||
if (!trackPoint.hasLocation() || !trackPoint.hasAltitude()) {
|
||||
return;
|
||||
public TrackPoint correctAltitude(Context context, TrackPoint trackPoint) {
|
||||
if (!trackPoint.position().hasLocation() || !trackPoint.position().hasAltitude()) {
|
||||
return trackPoint;
|
||||
}
|
||||
|
||||
// TODO The following is doing IO and should not be done in main thread.
|
||||
final TrackPoint[] newTrackPoint = {null}; //TODO this is kind of ugly
|
||||
// AltitudeConverterCompat uses internally a RoomDatabase that cannot be access from main thread and thus fails on version <= 34.
|
||||
Thread t = new Thread(() -> {
|
||||
try {
|
||||
Location loc = trackPoint.getPosition().toLocation();
|
||||
AltitudeConverterCompat.addMslAltitudeToLocation(context, loc);
|
||||
trackPoint.setAltitude(Altitude.EGM2008.of(LocationCompat.getMslAltitudeMeters(loc)));
|
||||
} catch (IOException e) {
|
||||
Log.w(TAG, "Android's AltitudeConverterCompat failed with " + e.getMessage());
|
||||
}
|
||||
});
|
||||
try {
|
||||
Location loc = trackPoint.position().toLocation();
|
||||
AltitudeConverterCompat.addMslAltitudeToLocation(context, loc);
|
||||
newTrackPoint[0] = TrackPoint.with(trackPoint, Altitude.EGM2008.of(LocationCompat.getMslAltitudeMeters(loc)));
|
||||
} catch (IOException e) {
|
||||
Log.w(TAG, "Android's AltitudeConverterCompat failed with " + e.getMessage());
|
||||
}
|
||||
});
|
||||
t.start();
|
||||
|
||||
try {
|
||||
@@ -42,5 +42,7 @@ public class AltitudeCorrectionManager {
|
||||
} catch (InterruptedException e) {
|
||||
Log.w(TAG, "Android's AltitudeConverterCompat failed with " + e.getMessage());
|
||||
}
|
||||
|
||||
return newTrackPoint[0];
|
||||
}
|
||||
}
|
||||
@@ -3,7 +3,6 @@ package de.dennisguse.opentracks.services.handlers;
|
||||
import android.content.Context;
|
||||
import android.content.SharedPreferences;
|
||||
import android.os.Handler;
|
||||
import android.util.Log;
|
||||
import android.util.Pair;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
@@ -15,6 +14,7 @@ import java.time.Instant;
|
||||
import java.time.ZoneId;
|
||||
|
||||
import de.dennisguse.opentracks.data.models.Distance;
|
||||
import de.dennisguse.opentracks.data.models.Position;
|
||||
import de.dennisguse.opentracks.data.models.TrackPoint;
|
||||
import de.dennisguse.opentracks.sensors.GpsStatusValue;
|
||||
import de.dennisguse.opentracks.sensors.SensorManager;
|
||||
@@ -55,15 +55,6 @@ public class TrackPointCreator implements SharedPreferences.OnSharedPreferenceCh
|
||||
sensorManager.reset();
|
||||
}
|
||||
|
||||
private SensorDataSet addSensorData(TrackPoint trackPoint) {
|
||||
if (!isStarted()) {
|
||||
Log.w(TAG, "Not started, should not be called.");
|
||||
return null;
|
||||
}
|
||||
|
||||
return sensorManager.fill(trackPoint);
|
||||
}
|
||||
|
||||
public void stop() {
|
||||
sensorManager.stop(context);
|
||||
this.context = null;
|
||||
@@ -72,13 +63,8 @@ public class TrackPointCreator implements SharedPreferences.OnSharedPreferenceCh
|
||||
/**
|
||||
* Got a new TrackPoint from Bluetooth only; contains no GPS location.
|
||||
*/
|
||||
public synchronized void onChange(@NonNull SensorDataSet unused) {
|
||||
onNewTrackPoint(new TrackPoint(TrackPoint.Type.TRACKPOINT, createNow()));
|
||||
}
|
||||
|
||||
@VisibleForTesting
|
||||
public void onNewTrackPoint(@NonNull TrackPoint trackPoint) {
|
||||
addSensorData(trackPoint);
|
||||
public synchronized void onChange() {
|
||||
TrackPoint trackPoint = createTrackPoint(TrackPoint.Type.TRACKPOINT);
|
||||
|
||||
boolean stored = service.newTrackPoint(trackPoint, PreferencesUtils.getThresholdHorizontalAccuracy()); //TODO Cache preference for performance
|
||||
if (stored) {
|
||||
@@ -91,40 +77,73 @@ public class TrackPointCreator implements SharedPreferences.OnSharedPreferenceCh
|
||||
}
|
||||
|
||||
public synchronized TrackPoint createSegmentEnd() {
|
||||
TrackPoint segmentEnd = TrackPoint.createSegmentEndWithTime(createNow());
|
||||
addSensorData(segmentEnd);
|
||||
TrackPoint segmentEnd = createTrackPoint(TrackPoint.Type.SEGMENT_END_MANUAL);
|
||||
reset();
|
||||
return segmentEnd;
|
||||
}
|
||||
|
||||
public synchronized TrackPoint createIdle() {
|
||||
TrackPoint idle = new TrackPoint(TrackPoint.Type.IDLE, createNow());
|
||||
addSensorData(idle);
|
||||
TrackPoint idle = createTrackPoint(TrackPoint.Type.IDLE);
|
||||
reset();
|
||||
return idle;
|
||||
}
|
||||
|
||||
public Pair<TrackPoint, SensorDataSet> createCurrentTrackPoint(@Nullable TrackPoint lastTrackPointUISpeed, @Nullable TrackPoint lastTrackPointUIAltitude, @Nullable TrackPoint lastStoredTrackPointWithLocation) {
|
||||
TrackPoint currentTrackPoint = new TrackPoint(TrackPoint.Type.TRACKPOINT, createNow());
|
||||
Instant now = createNow();
|
||||
SensorDataSet sensorDataSet = sensorManager.getSensorDataSet(now);
|
||||
|
||||
SensorDataSet sensorDataSet = addSensorData(currentTrackPoint);
|
||||
Position position = sensorDataSet.position().data();
|
||||
|
||||
if (!currentTrackPoint.hasLocation() && lastStoredTrackPointWithLocation != null && lastStoredTrackPointWithLocation.hasLocation()) {
|
||||
if (!position.hasLocation()
|
||||
&& lastStoredTrackPointWithLocation != null
|
||||
&& lastStoredTrackPointWithLocation.position().hasLocation()) {
|
||||
//We are taking the coordinates from the last stored TrackPoint, so the distance is monotonously increasing.
|
||||
currentTrackPoint.setPosition(lastStoredTrackPointWithLocation.getPosition());
|
||||
position = position.withCoordinates(lastStoredTrackPointWithLocation.position());
|
||||
}
|
||||
|
||||
if (lastTrackPointUISpeed != null) {
|
||||
currentTrackPoint.setSpeed(lastTrackPointUISpeed.getSpeed());
|
||||
}
|
||||
if (lastTrackPointUISpeed != null)
|
||||
position.with(lastTrackPointUISpeed.position().speed());
|
||||
|
||||
if (lastTrackPointUIAltitude != null) {
|
||||
currentTrackPoint.setAltitude(lastTrackPointUIAltitude.getAltitude());
|
||||
}
|
||||
if (lastTrackPointUIAltitude != null)
|
||||
position.with(lastTrackPointUIAltitude.position().altitude());
|
||||
|
||||
TrackPoint currentTrackPoint = new TrackPoint(
|
||||
null,
|
||||
TrackPoint.Type.TRACKPOINT,
|
||||
position,
|
||||
sensorDataSet.distance() != null ? sensorDataSet.distance().data() : null,
|
||||
sensorDataSet.heartRate() != null ? sensorDataSet.heartRate().data() : null,
|
||||
sensorDataSet.cadence() != null ? sensorDataSet.cadence().data() : null,
|
||||
sensorDataSet.power() != null ? sensorDataSet.power().data() : null,
|
||||
sensorDataSet.altitudeGainLoss() != null ? sensorDataSet.altitudeGainLoss().data() : null
|
||||
);
|
||||
|
||||
return new Pair<>(currentTrackPoint, sensorDataSet);
|
||||
}
|
||||
|
||||
private TrackPoint createTrackPoint(TrackPoint.Type type) {
|
||||
Instant now = createNow();
|
||||
|
||||
SensorDataSet sensorDataSet = sensorManager.getSensorDataSet(now);
|
||||
|
||||
Position position = sensorDataSet.position().data();
|
||||
if (sensorDataSet.speed() != null) {
|
||||
position = position.with(sensorDataSet.speed().data());
|
||||
}
|
||||
|
||||
return
|
||||
new TrackPoint(
|
||||
null,
|
||||
type,
|
||||
position,
|
||||
sensorDataSet.distance() != null ? sensorDataSet.distance().data() : null,
|
||||
sensorDataSet.heartRate() != null ? sensorDataSet.heartRate().data() : null,
|
||||
sensorDataSet.cadence() != null ? sensorDataSet.cadence().data() : null,
|
||||
sensorDataSet.power() != null ? sensorDataSet.power().data() : null,
|
||||
sensorDataSet.altitudeGainLoss() != null ? sensorDataSet.altitudeGainLoss().data() : null
|
||||
);
|
||||
}
|
||||
|
||||
public Instant createNow() {
|
||||
return Instant.now(clock);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user