Cleanup: SegmentStatisticUpdater is always initialized.

This commit is contained in:
Dennis Guse
2025-11-21 16:19:32 +01:00
parent 1ef7a6ecc4
commit 73d6ca6ca6
6 changed files with 48 additions and 57 deletions
@@ -166,14 +166,14 @@ public class TrackStatisticsUpdaterTest {
assertEquals(Speed.of(2), subject.getTrackStatistics().maxSpeed()); assertEquals(Speed.of(2), subject.getTrackStatistics().maxSpeed());
// when // when
subject.addTrackPoints(List.of( List.of(
new TrackPoint(TrackPoint.Type.SEGMENT_START_MANUAL, Instant.ofEpochSecond(5)), new TrackPoint(TrackPoint.Type.SEGMENT_START_MANUAL, Instant.ofEpochSecond(5)),
createTrackPoint(0, 0, Altitude.WGS84.of(0), Instant.ofEpochSecond(6)) createTrackPoint(0, 0, Altitude.WGS84.of(0), Instant.ofEpochSecond(6))
.setSpeed(Speed.of(1f)), .setSpeed(Speed.of(1f)),
createTrackPoint(0, 0, Altitude.WGS84.of(0), Instant.ofEpochSecond(7)) createTrackPoint(0, 0, Altitude.WGS84.of(0), Instant.ofEpochSecond(7))
.setSpeed(Speed.of(1f)), .setSpeed(Speed.of(1f)),
new TrackPoint(TrackPoint.Type.SEGMENT_END_MANUAL, Instant.ofEpochSecond(8)) new TrackPoint(TrackPoint.Type.SEGMENT_END_MANUAL, Instant.ofEpochSecond(8))
)); ).forEach(subject::addTrackPoint);
// then // then
assertEquals(Speed.of(2f), subject.getTrackStatistics().maxSpeed()); assertEquals(Speed.of(2f), subject.getTrackStatistics().maxSpeed());
@@ -7,15 +7,20 @@ import java.time.Duration;
import java.time.Instant; import java.time.Instant;
import java.util.Objects; import java.util.Objects;
//TODO Add @NonNull to attributes
public record Statistics( public record Statistics(
@NonNull
Instant startTime, Instant startTime,
@NonNull
Instant stopTime, Instant stopTime,
@NonNull
Duration totalDuration, Duration totalDuration,
@NonNull
Duration movingDuration, // Based on when we believe the user is traveling Duration movingDuration, // Based on when we believe the user is traveling
@NonNull
Distance totalDistance, Distance totalDistance,
@NonNull
Speed maxSpeed, Speed maxSpeed,
@Nullable @Nullable
@@ -31,6 +36,22 @@ public record Statistics(
Power avgPower Power avgPower
) { ) {
//TODO: Should not be necessary; refactor and remove.
@Deprecated
public static final Statistics DEFAULT =
new Statistics(
null,
null,
Duration.ZERO,
Duration.ZERO,
Distance.ZERO,
Speed.ZERO,
null,
null,
null,
null
);
public Duration getStoppedTime() { public Duration getStoppedTime() {
return totalDuration.minus(movingDuration); return totalDuration.minus(movingDuration);
} }
@@ -24,6 +24,7 @@ import androidx.annotation.NonNull;
import androidx.annotation.Nullable; import androidx.annotation.Nullable;
import androidx.annotation.VisibleForTesting; import androidx.annotation.VisibleForTesting;
import java.time.Duration;
import java.time.OffsetDateTime; import java.time.OffsetDateTime;
import java.time.ZoneOffset; import java.time.ZoneOffset;
import java.util.UUID; import java.util.UUID;
@@ -67,7 +68,7 @@ public class Track {
@Deprecated //TODO Remove @Deprecated //TODO Remove
public Track(@NonNull ZoneOffset zoneOffset) { public Track(@NonNull ZoneOffset zoneOffset) {
this(zoneOffset, new SegmentStatisticUpdater().getStatistics()); this(zoneOffset, Statistics.DEFAULT);
} }
public Track(@NonNull ZoneOffset zoneOffset, @NonNull Statistics trackStatistics) { public Track(@NonNull ZoneOffset zoneOffset, @NonNull Statistics trackStatistics) {
@@ -23,7 +23,7 @@ public record RecordingData(Track track, TrackPoint latestTrackPoint, SensorData
@NonNull @NonNull
public Statistics getStatisticsTrack() { public Statistics getStatisticsTrack() {
if (track == null) { if (track == null) {
return new SegmentStatisticUpdater().getStatistics(); return Statistics.DEFAULT; //TODO Refactor code that this is not necessary.
} }
return track.getStatistics(); return track.getStatistics();
@@ -46,9 +46,11 @@ public class SegmentStatisticUpdater {
private final ExtremityMonitor altitudeExtremities = new ExtremityMonitor(); private final ExtremityMonitor altitudeExtremities = new ExtremityMonitor();
// The track start time. // The track start time.
private Instant startTime; //TODO Should never be null! @NonNull
private final Instant startTime;
// The track stop time. // The track stop time.
private Instant stopTime; //TODO Should never be null! @NonNull
private Instant stopTime;
private Distance totalDistance; private Distance totalDistance;
/** /**
@@ -67,12 +69,14 @@ public class SegmentStatisticUpdater {
private HeartRate avgHeartRate = null; private HeartRate avgHeartRate = null;
private Power avgPower = null; private Power avgPower = null;
public SegmentStatisticUpdater() { public SegmentStatisticUpdater(@NonNull Instant startTime) {
reset(); this.startTime = this.stopTime = startTime;
} totalDuration = Duration.ZERO;
movingDuration = Duration.ZERO;
public SegmentStatisticUpdater(Instant startTime) { totalDistance = Distance.ZERO;
reset(startTime); maxSpeed = Speed.ZERO;
totalAltitudeGain_m = null;
totalAltitudeLoss_m = null;
} }
/** /**
@@ -106,27 +110,6 @@ public class SegmentStatisticUpdater {
return statistics.merge(getStatistics()); return statistics.merge(getStatistics());
} }
public boolean isInitialized() {
return startTime != null;
}
public void reset() {
startTime = null;
stopTime = null;
totalDuration = Duration.ZERO;
movingDuration = Duration.ZERO;
totalDistance = Distance.ZERO;
maxSpeed = Speed.ZERO;
totalAltitudeGain_m = null;
totalAltitudeLoss_m = null;
}
public void reset(Instant startTime) {
reset();
setStartTime(startTime);
}
public Statistics getStatistics() { public Statistics getStatistics() {
// Times may not be live (i.e., updated automatically). // Times may not be live (i.e., updated automatically).
return new Statistics( return new Statistics(
@@ -146,14 +129,6 @@ public class SegmentStatisticUpdater {
); );
} }
/**
* Should only be called on start.
*/
public void setStartTime(Instant startTime) {
this.startTime = startTime;
setStopTime(startTime);
}
public void setStopTime(Instant stopTime) { public void setStopTime(Instant stopTime) {
if (stopTime.isBefore(startTime)) { if (stopTime.isBefore(startTime)) {
// Time must be monotonically increasing, but we might have events at the same point in time (BLE and GPS) // Time must be monotonically increasing, but we might have events at the same point in time (BLE and GPS)
@@ -32,8 +32,7 @@ import de.dennisguse.opentracks.settings.PreferencesUtils;
/** /**
* Updater for {@link SegmentStatisticUpdater}. * Updater for {@link SegmentStatisticUpdater}.
* For updating track {@link SegmentStatisticUpdater} as new {@link TrackPoint}s are added. * For updating track {@link SegmentStatisticUpdater} as new {@link TrackPoint}s are added.
* NOTE: Some of the locations represent pause/resume separator. * NOTE: {@link TrackPoint} represent pause/resume separator.
* NOTE: Has still support for segments (at the moment unused).
* *
* @author Sandor Dornbush * @author Sandor Dornbush
* @author Rodrigo Damazio * @author Rodrigo Damazio
@@ -51,7 +50,7 @@ public class TrackStatisticsUpdater {
private Duration totalPowerDuration = Duration.ZERO; private Duration totalPowerDuration = Duration.ZERO;
// The current segment's statistics // The current segment's statistics
private final SegmentStatisticUpdater currentSegment; private SegmentStatisticUpdater currentSegment;
// Current segment's last trackPoint // Current segment's last trackPoint
private TrackPoint lastTrackPoint; private TrackPoint lastTrackPoint;
@@ -59,7 +58,7 @@ public class TrackStatisticsUpdater {
@Deprecated @Deprecated
public TrackStatisticsUpdater() { public TrackStatisticsUpdater() {
this(new SegmentStatisticUpdater().getStatistics()); this(Statistics.DEFAULT);
} }
public TrackStatisticsUpdater(@NonNull TrackPoint trackPoint) { public TrackStatisticsUpdater(@NonNull TrackPoint trackPoint) {
@@ -71,12 +70,12 @@ public class TrackStatisticsUpdater {
this(); this();
assert !trackPoints.isEmpty(); //TODO Enforce that this is always true (e.g., import) assert !trackPoints.isEmpty(); //TODO Enforce that this is always true (e.g., import)
addTrackPoints(trackPoints); trackPoints.forEach(this::addTrackPoint);
} }
public TrackStatisticsUpdater(@NonNull Statistics statistics) { public TrackStatisticsUpdater(@NonNull Statistics statistics) {
this.statisticsWithoutCurrentSegment = statistics; this.statisticsWithoutCurrentSegment = statistics;
this.currentSegment = new SegmentStatisticUpdater(); this.currentSegment = null;
resetAverageHeartRate(); resetAverageHeartRate();
} }
@@ -105,17 +104,12 @@ public class TrackStatisticsUpdater {
return currentSegment.getStatistics(); return currentSegment.getStatistics();
} }
public void addTrackPoints(List<TrackPoint> trackPoints) {
trackPoints.forEach(this::addTrackPoint);
}
public void addTrackPoint(TrackPoint trackPoint) { public void addTrackPoint(TrackPoint trackPoint) {
if (trackPoint.isSegmentManualStart()) { if (trackPoint.isSegmentManualStart()) {
reset(trackPoint); reset(trackPoint);
} }
if (currentSegment == null) {
if (!currentSegment.isInitialized()) { currentSegment = new SegmentStatisticUpdater(trackPoint.getTime());
currentSegment.setStartTime(trackPoint.getTime());
} }
// Always update time // Always update time
@@ -199,10 +193,10 @@ public class TrackStatisticsUpdater {
} }
private void reset(TrackPoint trackPoint) { private void reset(TrackPoint trackPoint) {
if (currentSegment.isInitialized()) { if (currentSegment != null) {
statisticsWithoutCurrentSegment = currentSegment.merge(statisticsWithoutCurrentSegment); statisticsWithoutCurrentSegment = currentSegment.merge(statisticsWithoutCurrentSegment);
} }
currentSegment.reset(trackPoint.getTime()); currentSegment = new SegmentStatisticUpdater(trackPoint.getTime());
lastTrackPoint = null; lastTrackPoint = null;
resetAverageHeartRate(); resetAverageHeartRate();