TrackDataHub computes TrackStatistics and passes it to listeners.

Part of #719.
This commit is contained in:
Dennis Guse
2021-04-26 19:24:49 +02:00
parent 391dfd30e2
commit 887d4700d4
9 changed files with 147 additions and 330 deletions
@@ -0,0 +1,94 @@
package de.dennisguse.opentracks.chart;
import androidx.test.ext.junit.runners.AndroidJUnit4;
import org.junit.Test;
import org.junit.runner.RunWith;
import java.time.Duration;
import de.dennisguse.opentracks.content.data.Distance;
import de.dennisguse.opentracks.content.data.Speed;
import de.dennisguse.opentracks.content.data.TrackPoint;
import de.dennisguse.opentracks.fragments.TrackStubUtils;
import de.dennisguse.opentracks.stats.TrackStatistics;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNull;
@RunWith(AndroidJUnit4.class)
public class ChartPointTest {
@Test
public void create_by_time() {
// given
TrackStatistics statistics = new TrackStatistics();
statistics.setTotalTime(Duration.ofSeconds(1000));
// when
ChartPoint point = new ChartPoint(statistics, TrackStubUtils.createDefaultTrackPoint(), Speed.of(0), 0, false, false);
// then
assertEquals(1000000, (long) point.getTimeOrDistance());
}
@Test
public void create_by_distance() {
// given
TrackStatistics statistics = new TrackStatistics();
statistics.setTotalDistance(Distance.of(1000));
// when
ChartPoint point = new ChartPoint(statistics, TrackStubUtils.createDefaultTrackPoint(), Speed.of(0), 0, true, true);
// then
assertEquals(1, (long) point.getTimeOrDistance());
}
@Test
public void create_get_altitude_speed_and_pace() {
// given
TrackStatistics statistics = new TrackStatistics();
// when
ChartPoint point = new ChartPoint(statistics, TrackStubUtils.createDefaultTrackPoint(), Speed.of(10), 50, false, true);
// then
assertEquals(0.05, point.getAltitude(), 0.01);
assertEquals(36, point.getSpeed(), 0.01);
assertEquals(1.66, point.getPace(), 0.01);
}
@Test
public void create_sensorNotAvailable() {
// given
TrackStatistics statistics = new TrackStatistics();
// when
ChartPoint point = new ChartPoint(statistics, TrackStubUtils.createDefaultTrackPoint(), Speed.of(10), 50, false, true);
// then
assertNull(point.getHeartRate());
assertNull(point.getCadence());
assertNull(point.getPower());
}
@Test
public void create_sensorAvailable() {
// given
TrackPoint trackPoint = TrackStubUtils.createDefaultTrackPoint();
trackPoint.setHeartRate_bpm(100f);
trackPoint.setCyclingCadence_rpm(101f);
trackPoint.setPower(102f);
TrackStatistics statistics = new TrackStatistics();
// when
ChartPoint point = new ChartPoint(statistics, trackPoint, Speed.of(10), 50, false, true);
// then
assertEquals(100.0, point.getHeartRate(), 0.01);
assertEquals(101.0, point.getCadence(), 0.01);
assertEquals(102.0, point.getPower(), 0.01);
}
}
@@ -16,7 +16,6 @@
package de.dennisguse.opentracks.fragments; package de.dennisguse.opentracks.fragments;
import android.location.Location;
import android.os.Looper; import android.os.Looper;
import androidx.test.core.app.ApplicationProvider; import androidx.test.core.app.ApplicationProvider;
@@ -28,24 +27,15 @@ import org.junit.BeforeClass;
import org.junit.Test; import org.junit.Test;
import org.junit.runner.RunWith; import org.junit.runner.RunWith;
import java.time.Duration;
import java.time.Instant;
import de.dennisguse.opentracks.chart.ChartPoint;
import de.dennisguse.opentracks.chart.ChartView; import de.dennisguse.opentracks.chart.ChartView;
import de.dennisguse.opentracks.content.data.Distance; import de.dennisguse.opentracks.content.data.Distance;
import de.dennisguse.opentracks.content.data.Speed;
import de.dennisguse.opentracks.content.data.TrackPoint;
import de.dennisguse.opentracks.util.UnitConversions;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNull;
/** /**
* Tests {@link ChartFragment}. * Tests {@link ChartFragment}.
* *
* @author Youtao Liu * @author Youtao Liu
*/ */
//TODO Add tests that check ChartFragment
@RunWith(AndroidJUnit4.class) @RunWith(AndroidJUnit4.class)
public class ChartFragmentTest { public class ChartFragmentTest {
@@ -72,261 +62,8 @@ public class ChartFragmentTest {
chartFragment.setRecordingDistanceInterval(Distance.of(50)); chartFragment.setRecordingDistanceInterval(Distance.of(50));
} }
/**
* Tests the logic to get the incorrect values of sensor in {@link ChartFragment#createPendingPoint(TrackPoint)}.
*/
@Test @Test
public void testCreatePendingPoint_sensorIncorrect() { public void nothing() {
// given //TODO
TrackPoint trackPoint = TrackStubUtils.createDefaultTrackPoint();
// when
ChartPoint point = chartFragment.createPendingPoint(trackPoint);
// then
assertNull(point.getHeartRate());
assertNull(point.getCadence());
assertNull(point.getPower());
}
/**
* Tests the logic to get the correct values of sensor in {@link ChartFragment#createPendingPoint(TrackPoint)}.
*/
@Test
public void testCreatePendingPoint_sensorCorrect() {
// given
TrackPoint trackPoint = TrackStubUtils.createDefaultTrackPoint();
trackPoint.setHeartRate_bpm(100f);
trackPoint.setCyclingCadence_rpm(101f);
trackPoint.setPower(102f);
// when
ChartPoint point = chartFragment.createPendingPoint(trackPoint);
// then
assertEquals(100.0, point.getHeartRate(), 0.01);
assertEquals(101.0, point.getCadence(), 0.01);
assertEquals(102.0, point.getPower(), 0.01);
}
/**
* Tests the logic to get the value of metric Distance in {@link ChartFragment#createPendingPoint(TrackPoint)}.
*/
@Test
public void testCreatePendingPoint_distanceMetric() {
chartFragment.setChartByDistance(true);
// Resets last location and writes first location.
TrackPoint trackPoint1 = TrackStubUtils.createDefaultTrackPoint();
ChartPoint point = chartFragment.createPendingPoint(trackPoint1);
assertEquals(0.0, point.getTimeOrDistance(), 0.01);
// The second is a same location, just different time.
TrackPoint trackPoint2 = TrackStubUtils.createDefaultTrackPoint();
point = chartFragment.createPendingPoint(trackPoint2);
assertEquals(0.0, point.getTimeOrDistance(), 0.01);
// The third location is a new location, and use metric.
TrackPoint trackPoint3 = TrackStubUtils.createDefaultTrackPoint();
trackPoint3.setLatitude(23);
point = chartFragment.createPendingPoint(trackPoint3);
// Computes the distance between Latitude 22 and 23.
float[] results = new float[4];
Location.distanceBetween(trackPoint2.getLatitude(), trackPoint2.getLongitude(),
trackPoint3.getLatitude(), trackPoint3.getLongitude(), results);
double distance1 = results[0] * UnitConversions.M_TO_KM;
assertEquals(distance1, point.getTimeOrDistance(), 0.01);
// The fourth location is a new location, and use metric.
TrackPoint trackPoint4 = TrackStubUtils.createDefaultTrackPoint();
trackPoint4.setLatitude(24);
point = chartFragment.createPendingPoint(trackPoint4);
// Computes the distance between Latitude 23 and 24.
Location.distanceBetween(trackPoint3.getLatitude(), trackPoint3.getLongitude(),
trackPoint4.getLatitude(), trackPoint4.getLongitude(), results);
double distance2 = results[0] * UnitConversions.M_TO_KM;
assertEquals((distance1 + distance2), point.getTimeOrDistance(), 0.01);
}
/**
* Tests the logic to get the value of imperial Distance in {@link ChartFragment#createPendingPoint(TrackPoint)}.
*/
@Test
public void testCreatePendingPoint_distanceImperial() {
// By distance.
chartFragment.setChartByDistance(true);
// Setups to use imperial.
chartFragment.setMetricUnits(false);
// The first is a same location, just different time.
TrackPoint trackPoint1 = TrackStubUtils.createDefaultTrackPoint();
ChartPoint point = chartFragment.createPendingPoint(trackPoint1);
assertEquals(0.0, point.getTimeOrDistance(), 0.01);
// The second location is a new location, and use imperial.
TrackPoint trackPoint2 = TrackStubUtils.createDefaultTrackPoint();
trackPoint2.setLatitude(23);
point = chartFragment.createPendingPoint(trackPoint2);
/*
* Computes the distance between Latitude 22 and 23.
* And for we set using * imperial, the distance should be multiplied by UnitConversions.KM_TO_MI.
*/
float[] results = new float[4];
Location.distanceBetween(trackPoint1.getLatitude(), trackPoint1.getLongitude(), trackPoint2.getLatitude(), trackPoint2.getLongitude(), results);
double distance1 = results[0] * UnitConversions.M_TO_KM * UnitConversions.KM_TO_MI;
assertEquals(distance1, point.getTimeOrDistance(), 0.01);
// The third location is a new location, and use imperial.
TrackPoint trackPoint3 = TrackStubUtils.createDefaultTrackPoint();
trackPoint3.setLatitude(24);
point = chartFragment.createPendingPoint(trackPoint3);
/*
* Computes the distance between Latitude 23 and 24.
* And for we set using * imperial, the distance should be multiplied by UnitConversions.KM_TO_MI.
*/
Location.distanceBetween(trackPoint2.getLatitude(), trackPoint2.getLongitude(), trackPoint3.getLatitude(), trackPoint3.getLongitude(), results);
double distance2 = results[0] * UnitConversions.M_TO_KM * UnitConversions.KM_TO_MI;
assertEquals(distance1 + distance2, point.getTimeOrDistance(), 0.01);
}
/**
* Tests the logic to get the values of time in {@link ChartFragment#createPendingPoint(TrackPoint)}.
*/
@Test
public void testCreatePendingPoint_time() {
// given
chartFragment.setChartByDistance(false);
TrackPoint trackPoint1 = TrackStubUtils.createDefaultTrackPoint();
trackPoint1.setTime(Instant.ofEpochMilli(TrackStubUtils.INITIAL_TIME)); //Keep old TrackPoint behavior of having time=0 for this test
// when
ChartPoint point = chartFragment.createPendingPoint(trackPoint1);
// then
assertEquals(0.0, point.getTimeOrDistance(), 0.01);
Duration timeSpan = Duration.ofMillis(222);
TrackPoint trackPoint2 = TrackStubUtils.createDefaultTrackPoint();
trackPoint2.setTime(Instant.ofEpochMilli(TrackStubUtils.INITIAL_TIME).plus(timeSpan));
point = chartFragment.createPendingPoint(trackPoint2);
assertEquals(timeSpan, Duration.ofMillis((long) point.getTimeOrDistance()));
}
/**
* Tests the logic to get the value of altitude in {@link ChartFragment#createPendingPoint(TrackPoint)} by one and two points.
*/
@Test
public void testCreatePendingPoint_altitude() {
TrackPoint trackPoint1 = TrackStubUtils.createDefaultTrackPoint();
/*
* At first, clear old points of altitude, so give true to the second parameter.
* Then only one value INITIAL_ALTITUDE in buffer.
*/
ChartPoint point = chartFragment.createPendingPoint(trackPoint1);
assertEquals(TrackStubUtils.INITIAL_ALTITUDE, point.getAltitude(), 0.01);
/*
* Send another value to buffer, now there are two values, INITIAL_ALTITUDE and INITIAL_ALTITUDE * 2.
*/
TrackPoint trackPoint2 = TrackStubUtils.createDefaultTrackPoint();
trackPoint2.setAltitude(TrackStubUtils.INITIAL_ALTITUDE * 2);
point = chartFragment.createPendingPoint(trackPoint2);
assertEquals((TrackStubUtils.INITIAL_ALTITUDE + TrackStubUtils.INITIAL_ALTITUDE * 2) / 2.0, point.getAltitude(), 0.01);
}
/**
* Tests the logic to get the value of speed in {@link ChartFragment#createPendingPoint(TrackPoint)}.
* In this test, firstly remove all points in memory, and then fill in two points one by one.
* The speed values of these points are 129, 130.
*/
@Test
public void testCreatePendingPoint_speed() {
/*
* At first, clear old points of speed, so give true to the second parameter.
* It will not be filled in to the speed buffer.
*/
TrackPoint trackPoint1 = TrackStubUtils.createDefaultTrackPoint();
trackPoint1.setSpeed(Speed.of(128.5f));
ChartPoint point = chartFragment.createPendingPoint(trackPoint1);
assertEquals(0.0, point.getSpeed(), 0.01);
/*
* Tests the logic when both metricUnits and reportSpeed are true.
* This location will be filled into speed buffer.
*/
TrackPoint trackPoint2 = TrackStubUtils.createDefaultTrackPoint();
/*
* Add a time span here to make sure the second point is valid, the value 222 here is doesn't matter.
*/
trackPoint2.setTime(trackPoint1.getTime().plusMillis(222));
trackPoint2.setSpeed(Speed.of(130f));
point = chartFragment.createPendingPoint(trackPoint2);
assertEquals(130.0 * UnitConversions.MPS_TO_KMH, point.getSpeed(), 0.01);
}
/**
* Tests the logic to compute speed when use Imperial.
*/
@Test
public void testCreatePendingPoint_speedImperial() {
chartFragment.setMetricUnits(false);
// First data point is not added to the speed buffer
TrackPoint trackPoint1 = TrackStubUtils.createDefaultTrackPoint();
trackPoint1.setSpeed(Speed.of(100.0f));
ChartPoint point = chartFragment.createPendingPoint(trackPoint1);
assertEquals(0.0, point.getSpeed(), 0.01);
TrackPoint trackPoint2 = TrackStubUtils.createDefaultTrackPoint();
/*
* Add a time span here to make sure the second point and the speed is valid.
* Speed is valid if: speedDifference > Constants.MAX_ACCELERATION * timeDifference speedDifference = 102 -100 timeDifference = 222
*/
trackPoint2.setTime(trackPoint2.getTime().plusMillis(222));
trackPoint2.setSpeed(Speed.of(102f));
point = chartFragment.createPendingPoint(trackPoint2);
assertEquals(102.0 * UnitConversions.MPS_TO_KMH * UnitConversions.KM_TO_MI, point.getSpeed(), 0.01);
}
/**
* Tests the logic to get pace value when reportSpeed is false.
*/
@Test
public void testCreatePendingPoint_pace_nonZeroSpeed() {
chartFragment.setReportSpeed(false);
// First data point is not added to the speed buffer
TrackPoint trackPoint1 = TrackStubUtils.createDefaultTrackPoint();
trackPoint1.setSpeed(Speed.of(100.0f));
ChartPoint point = chartFragment.createPendingPoint(trackPoint1);
assertEquals(0.0, point.getSpeed(), 0.01);
TrackPoint trackPoint2 = TrackStubUtils.createDefaultTrackPoint();
/*
* Add a time span here to make sure the second point and the speed is valid.
* Speed is valid if: speedDifference > Constants.MAX_ACCELERATION * timeDifference speedDifference = 102 -100 timeDifference = 222
*/
trackPoint2.setTime(trackPoint2.getTime().plusMillis(222));
trackPoint2.setSpeed(Speed.of(102f));
point = chartFragment.createPendingPoint(trackPoint2);
assertEquals(HOURS_PER_UNIT / (102.0 * UnitConversions.MPS_TO_KMH), point.getPace(), 0.01);
}
/**
* Tests the logic to get pace value when reportSpeed is false and average speed is zero.
*/
@Test
public void testCreatePendingPoint_pace_zeroSpeed() {
chartFragment.setReportSpeed(false);
TrackPoint trackPoint = TrackStubUtils.createDefaultTrackPoint();
trackPoint.setSpeed(Speed.of(0f));
ChartPoint point = chartFragment.createPendingPoint(trackPoint);
assertEquals(0.0, point.getPace(), 0.01);
} }
} }
@@ -3,9 +3,10 @@ package de.dennisguse.opentracks.chart;
import androidx.annotation.NonNull; import androidx.annotation.NonNull;
import androidx.annotation.VisibleForTesting; import androidx.annotation.VisibleForTesting;
import de.dennisguse.opentracks.content.data.Distance;
import de.dennisguse.opentracks.content.data.Speed;
import de.dennisguse.opentracks.content.data.TrackPoint; import de.dennisguse.opentracks.content.data.TrackPoint;
import de.dennisguse.opentracks.stats.TrackStatistics; import de.dennisguse.opentracks.stats.TrackStatistics;
import de.dennisguse.opentracks.stats.TrackStatisticsUpdater;
import de.dennisguse.opentracks.util.UnitConversions; import de.dennisguse.opentracks.util.UnitConversions;
public class ChartPoint { public class ChartPoint {
@@ -13,44 +14,38 @@ public class ChartPoint {
private double timeOrDistance; private double timeOrDistance;
//Y-axis //Y-axis
private double altitude; private final double altitude;
private Double speed; private Double speed;
private Double pace; private Double pace;
private Double heartRate; private Double heartRate;
private Double cadence; private Double cadence;
private Double power; private Double power;
@Deprecated
@VisibleForTesting @VisibleForTesting
ChartPoint(double altitude) { ChartPoint(double altitude) {
this.altitude = altitude; this.altitude = altitude;
} }
public ChartPoint(@NonNull TrackStatisticsUpdater trackStatisticsUpdater, TrackPoint trackPoint, boolean chartByDistance, boolean metricUnits) { public ChartPoint(@NonNull TrackStatistics trackStatistics, @NonNull TrackPoint trackPoint, Speed smoothedSpeed, double smoothedAltitude_m, boolean chartByDistance, boolean metricUnits) {
TrackStatistics trackStatistics = trackStatisticsUpdater.getTrackStatistics();
if (chartByDistance) { if (chartByDistance) {
timeOrDistance = trackStatistics.getTotalDistance().to(metricUnits); timeOrDistance = trackStatistics.getTotalDistance().to(metricUnits);
} else { } else {
timeOrDistance = trackStatistics.getTotalTime().toMillis(); timeOrDistance = trackStatistics.getTotalTime().toMillis();
} }
altitude = trackStatisticsUpdater.getSmoothedAltitude(); altitude = Distance.of(smoothedAltitude_m).to(metricUnits);
if (!metricUnits) {
altitude *= UnitConversions.M_TO_FT;
}
speed = trackStatisticsUpdater.getSmoothedSpeed().to(metricUnits); speed = smoothedSpeed.to(metricUnits);
pace = trackStatisticsUpdater.getSmoothedSpeed().toPace(metricUnits).toMillis() * UnitConversions.MS_TO_S * UnitConversions.S_TO_MIN; pace = smoothedSpeed.toPace(metricUnits).toMillis() * UnitConversions.MS_TO_S * UnitConversions.S_TO_MIN;
if (trackPoint != null) { if (trackPoint.hasHeartRate()) {
if (trackPoint.hasHeartRate()) { heartRate = (double) trackPoint.getHeartRate_bpm();
heartRate = (double) trackPoint.getHeartRate_bpm(); }
} if (trackPoint.hasCyclingCadence()) {
if (trackPoint.hasCyclingCadence()) { cadence = (double) trackPoint.getCyclingCadence_rpm();
cadence = (double) trackPoint.getCyclingCadence_rpm(); }
} if (trackPoint.hasPower()) {
if (trackPoint.hasPower()) { power = (double) trackPoint.getPower();
power = (double) trackPoint.getPower();
}
} }
} }
@@ -30,6 +30,7 @@ import androidx.annotation.VisibleForTesting;
import java.util.Collections; import java.util.Collections;
import java.util.Set; import java.util.Set;
import de.dennisguse.opentracks.content.data.Distance;
import de.dennisguse.opentracks.content.data.Marker; import de.dennisguse.opentracks.content.data.Marker;
import de.dennisguse.opentracks.content.data.MarkerColumns; import de.dennisguse.opentracks.content.data.MarkerColumns;
import de.dennisguse.opentracks.content.data.Track; import de.dennisguse.opentracks.content.data.Track;
@@ -38,6 +39,7 @@ import de.dennisguse.opentracks.content.data.TrackPointsColumns;
import de.dennisguse.opentracks.content.data.TracksColumns; import de.dennisguse.opentracks.content.data.TracksColumns;
import de.dennisguse.opentracks.content.provider.ContentProviderUtils; import de.dennisguse.opentracks.content.provider.ContentProviderUtils;
import de.dennisguse.opentracks.content.provider.TrackPointIterator; import de.dennisguse.opentracks.content.provider.TrackPointIterator;
import de.dennisguse.opentracks.stats.TrackStatisticsUpdater;
/** /**
* Track data hub. * Track data hub.
@@ -349,6 +351,8 @@ public class TrackDataHub {
next = new TrackPoint.Id(localLastSeenTrackPointIdId.getId() + 1); //TODO startTrackPointId + 1 is an assumption assumption; should be derived from the DB. next = new TrackPoint.Id(localLastSeenTrackPointIdId.getId() + 1); //TODO startTrackPointId + 1 is an assumption assumption; should be derived from the DB.
} }
TrackStatisticsUpdater trackStatisticsUpdater = new TrackStatisticsUpdater();
TrackPoint trackPoint = null; TrackPoint trackPoint = null;
try (TrackPointIterator trackPointIterator = contentProviderUtils.getTrackPointLocationIterator(selectedTrackId, next)) { try (TrackPointIterator trackPointIterator = contentProviderUtils.getTrackPointLocationIterator(selectedTrackId, next)) {
@@ -370,15 +374,16 @@ public class TrackDataHub {
samplingFrequency = 1 + (int) (numTotalPoints / targetNumPoints); samplingFrequency = 1 + (int) (numTotalPoints / targetNumPoints);
} }
trackStatisticsUpdater.addTrackPoint(trackPoint, Distance.of(50)); //TODO
// Also include the last point if the selected track is not recording. // Also include the last point if the selected track is not recording.
if ((localNumLoadedTrackPoints % samplingFrequency == 0) || (trackPointId == lastTrackPointId && !isSelectedTrackRecording())) { if ((localNumLoadedTrackPoints % samplingFrequency == 0) || (trackPointId == lastTrackPointId && !isSelectedTrackRecording())) {
for (TrackDataListener trackDataListener : sampledInListeners) { for (TrackDataListener trackDataListener : sampledInListeners) {
trackDataListener.onSampledInTrackPoint(trackPoint); trackDataListener.onSampledInTrackPoint(trackPoint, trackStatisticsUpdater.getTrackStatistics(), trackStatisticsUpdater.getSmoothedSpeed(), trackStatisticsUpdater.getSmoothedAltitude());
} }
} else { } else {
for (TrackDataListener trackDataListener : sampledOutListeners) { for (TrackDataListener trackDataListener : sampledOutListeners) {
trackDataListener.onSampledOutTrackPoint(trackPoint); trackDataListener.onSampledOutTrackPoint(trackPoint, trackStatisticsUpdater.getTrackStatistics());
} }
} }
@@ -398,7 +403,7 @@ public class TrackDataHub {
if (trackPoint != null) { if (trackPoint != null) {
for (TrackDataListener listener : sampledInListeners) { for (TrackDataListener listener : sampledInListeners) {
listener.onNewTrackPointsDone(trackPoint); listener.onNewTrackPointsDone(trackPoint, trackStatisticsUpdater.getTrackStatistics());
} }
} }
} }
@@ -19,8 +19,10 @@ package de.dennisguse.opentracks.content;
import androidx.annotation.NonNull; import androidx.annotation.NonNull;
import de.dennisguse.opentracks.content.data.Marker; import de.dennisguse.opentracks.content.data.Marker;
import de.dennisguse.opentracks.content.data.Speed;
import de.dennisguse.opentracks.content.data.Track; import de.dennisguse.opentracks.content.data.Track;
import de.dennisguse.opentracks.content.data.TrackPoint; import de.dennisguse.opentracks.content.data.TrackPoint;
import de.dennisguse.opentracks.stats.TrackStatistics;
/** /**
* Listener for track data changes. * Listener for track data changes.
@@ -47,20 +49,23 @@ public interface TrackDataListener {
* *
* @param trackPoint the trackPoint * @param trackPoint the trackPoint
*/ */
void onSampledInTrackPoint(@NonNull TrackPoint trackPoint); default void onSampledInTrackPoint(@NonNull TrackPoint trackPoint, @NonNull TrackStatistics trackStatistics, Speed smoothedSpeed, double smoothedAltitude_m) {
}
/** /**
* Called when a sampled out track point is read. * Called when a sampled out track point is read.
* *
* @param trackPoint the trackPoint * @param trackPoint the trackPoint
*/ */
void onSampledOutTrackPoint(@NonNull TrackPoint trackPoint); default void onSampledOutTrackPoint(@NonNull TrackPoint trackPoint, @NonNull TrackStatistics trackStatistics) {
}
/** /**
* Called when finish sending new track points. * Called when finish sending new track points.
* This gets called after every batch of calls to {@link #onSampledInTrackPoint(TrackPoint)} and {@link #onSampledOutTrackPoint(TrackPoint)}. * This gets called after every batch of calls to {@link #onSampledInTrackPoint(TrackPoint, TrackStatistics, Speed, double)} and {@link #onSampledOutTrackPoint(TrackPoint, TrackStatistics)}.
*/ */
void onNewTrackPointsDone(@NonNull TrackPoint lastTrackPoint); default void onNewTrackPointsDone(@NonNull TrackPoint lastTrackPoint, @NonNull TrackStatistics trackStatistics) {
}
/** /**
* Called to clear previously sent markers. * Called to clear previously sent markers.
@@ -39,6 +39,7 @@ import de.dennisguse.opentracks.content.TrackDataHub;
import de.dennisguse.opentracks.content.TrackDataListener; import de.dennisguse.opentracks.content.TrackDataListener;
import de.dennisguse.opentracks.content.data.Distance; import de.dennisguse.opentracks.content.data.Distance;
import de.dennisguse.opentracks.content.data.Marker; import de.dennisguse.opentracks.content.data.Marker;
import de.dennisguse.opentracks.content.data.Speed;
import de.dennisguse.opentracks.content.data.Track; import de.dennisguse.opentracks.content.data.Track;
import de.dennisguse.opentracks.content.data.TrackPoint; import de.dennisguse.opentracks.content.data.TrackPoint;
import de.dennisguse.opentracks.databinding.ChartBinding; import de.dennisguse.opentracks.databinding.ChartBinding;
@@ -72,7 +73,6 @@ public class ChartFragment extends Fragment implements TrackDataListener {
private TrackDataHub trackDataHub; private TrackDataHub trackDataHub;
// Stats gathered from the received data // Stats gathered from the received data
private TrackStatisticsUpdater trackStatisticsUpdater = new TrackStatisticsUpdater();
private final List<ChartPoint> pendingPoints = new ArrayList<>(); private final List<ChartPoint> pendingPoints = new ArrayList<>();
private String category = ""; private String category = "";
@@ -217,7 +217,6 @@ public class ChartFragment extends Fragment implements TrackDataListener {
@Override @Override
public void clearTrackPoints() { public void clearTrackPoints() {
if (isResumed()) { if (isResumed()) {
trackStatisticsUpdater = new TrackStatisticsUpdater();
pendingPoints.clear(); pendingPoints.clear();
chartView.reset(); chartView.reset();
runOnUiThread(() -> { runOnUiThread(() -> {
@@ -228,24 +227,15 @@ public class ChartFragment extends Fragment implements TrackDataListener {
} }
} }
@Override public void onSampledInTrackPoint(@NonNull TrackPoint trackPoint, @NonNull TrackStatistics trackStatistics, Speed smoothedSpeed, double smoothedAltitude_m) {
public void onSampledInTrackPoint(@NonNull TrackPoint trackPoint) {
if (isResumed()) { if (isResumed()) {
pendingPoints.add(createPendingPoint(trackPoint)); ChartPoint point = new ChartPoint(trackStatistics, trackPoint, smoothedSpeed, smoothedAltitude_m, chartByDistance, chartView.getMetricUnits());
pendingPoints.add(point);
} }
} }
@Override @Override
public void onSampledOutTrackPoint(@NonNull TrackPoint trackPoint) { public void onNewTrackPointsDone(@NonNull TrackPoint unused, @NonNull TrackStatistics alsoUnused) {
if (isResumed()) {
if (trackStatisticsUpdater != null) {
trackStatisticsUpdater.addTrackPoint(trackPoint, recordingDistanceInterval);
}
}
}
@Override
public void onNewTrackPointsDone(@NonNull TrackPoint unused) {
if (isResumed()) { if (isResumed()) {
chartView.addChartPoints(pendingPoints); chartView.addChartPoints(pendingPoints);
pendingPoints.clear(); pendingPoints.clear();
@@ -324,12 +314,6 @@ public class ChartFragment extends Fragment implements TrackDataListener {
} }
} }
@VisibleForTesting
ChartPoint createPendingPoint(@NonNull TrackPoint trackPoint) {
trackStatisticsUpdater.addTrackPoint(trackPoint, recordingDistanceInterval);
return new ChartPoint(trackStatisticsUpdater, trackPoint, chartByDistance, chartView.getMetricUnits());
}
@VisibleForTesting @VisibleForTesting
void setChartView(ChartView view) { void setChartView(ChartView view) {
chartView = view; chartView = view;
@@ -25,9 +25,11 @@ import de.dennisguse.opentracks.adapters.IntervalStatisticsAdapter;
import de.dennisguse.opentracks.content.TrackDataHub; import de.dennisguse.opentracks.content.TrackDataHub;
import de.dennisguse.opentracks.content.TrackDataListener; import de.dennisguse.opentracks.content.TrackDataListener;
import de.dennisguse.opentracks.content.data.Marker; import de.dennisguse.opentracks.content.data.Marker;
import de.dennisguse.opentracks.content.data.Speed;
import de.dennisguse.opentracks.content.data.Track; import de.dennisguse.opentracks.content.data.Track;
import de.dennisguse.opentracks.content.data.TrackPoint; import de.dennisguse.opentracks.content.data.TrackPoint;
import de.dennisguse.opentracks.databinding.IntervalListViewBinding; import de.dennisguse.opentracks.databinding.IntervalListViewBinding;
import de.dennisguse.opentracks.stats.TrackStatistics;
import de.dennisguse.opentracks.util.PreferencesUtils; import de.dennisguse.opentracks.util.PreferencesUtils;
import de.dennisguse.opentracks.viewmodels.IntervalStatistics; import de.dennisguse.opentracks.viewmodels.IntervalStatistics;
import de.dennisguse.opentracks.viewmodels.IntervalStatisticsModel; import de.dennisguse.opentracks.viewmodels.IntervalStatisticsModel;
@@ -229,21 +231,21 @@ public class IntervalsFragment extends Fragment implements TrackDataListener {
} }
@Override @Override
public void onSampledInTrackPoint(@NonNull TrackPoint trackPoint) { public void onSampledInTrackPoint(@NonNull TrackPoint trackPoint, @NonNull TrackStatistics unused, Speed unused2, double unused3) {
if (isResumed()) { if (isResumed()) {
viewModel.add(trackPoint); viewModel.add(trackPoint);
} }
} }
@Override @Override
public void onSampledOutTrackPoint(@NonNull TrackPoint trackPoint) { public void onSampledOutTrackPoint(@NonNull TrackPoint trackPoint, @NonNull TrackStatistics unused) {
if (isResumed()) { if (isResumed()) {
viewModel.add(trackPoint); viewModel.add(trackPoint);
} }
} }
@Override @Override
public void onNewTrackPointsDone(@NonNull TrackPoint unused) { public void onNewTrackPointsDone(@NonNull TrackPoint unused, @NonNull TrackStatistics alsoUnused) {
if (isResumed()) { if (isResumed()) {
runOnUiThread(viewModel::onNewTrackPoints); runOnUiThread(viewModel::onNewTrackPoints);
} }
@@ -34,6 +34,7 @@ import de.dennisguse.opentracks.content.sensor.SensorDataSet;
import de.dennisguse.opentracks.databinding.StatisticsRecordingBinding; import de.dennisguse.opentracks.databinding.StatisticsRecordingBinding;
import de.dennisguse.opentracks.services.TrackRecordingService; import de.dennisguse.opentracks.services.TrackRecordingService;
import de.dennisguse.opentracks.services.TrackRecordingServiceConnection; import de.dennisguse.opentracks.services.TrackRecordingServiceConnection;
import de.dennisguse.opentracks.stats.TrackStatistics;
import de.dennisguse.opentracks.util.PreferencesUtils; import de.dennisguse.opentracks.util.PreferencesUtils;
import de.dennisguse.opentracks.util.StringUtils; import de.dennisguse.opentracks.util.StringUtils;
import de.dennisguse.opentracks.util.TrackIconUtils; import de.dennisguse.opentracks.util.TrackIconUtils;
@@ -211,17 +212,7 @@ public class StatisticsRecordingFragment extends Fragment implements TrackDataLi
} }
@Override @Override
public void onSampledInTrackPoint(@NonNull TrackPoint trackPoint) { public void onNewTrackPointsDone(@NonNull TrackPoint newLastTrackPoint, @NonNull TrackStatistics trackStatistics) {
// We don't care.
}
@Override
public void onSampledOutTrackPoint(@NonNull TrackPoint trackPoint) {
// We don't care.
}
@Override
public void onNewTrackPointsDone(@NonNull TrackPoint newLastTrackPoint) {
if (isResumed()) { if (isResumed()) {
getActivity().runOnUiThread(() -> { getActivity().runOnUiThread(() -> {
if (isResumed()) { if (isResumed()) {
@@ -388,7 +379,7 @@ public class StatisticsRecordingFragment extends Fragment implements TrackDataLi
} }
// Set time // Set time
if (track != null && track.getTrackStatistics() != null) { if (track != null) {
viewBinding.statsMovingTimeValue.setText(StringUtils.formatElapsedTime(track.getTrackStatistics().getMovingTime())); viewBinding.statsMovingTimeValue.setText(StringUtils.formatElapsedTime(track.getTrackStatistics().getMovingTime()));
updateTotalTime(); updateTotalTime();
} }
@@ -18,6 +18,7 @@ package de.dennisguse.opentracks.stats;
import androidx.annotation.NonNull; import androidx.annotation.NonNull;
import androidx.annotation.Nullable; import androidx.annotation.Nullable;
import androidx.annotation.VisibleForTesting;
import java.time.Duration; import java.time.Duration;
import java.time.Instant; import java.time.Instant;
@@ -193,6 +194,7 @@ public class TrackStatistics {
this.movingTime = movingTime; this.movingTime = movingTime;
} }
@VisibleForTesting(otherwise = VisibleForTesting.PACKAGE_PRIVATE)
public void addMovingTime(Duration time) { public void addMovingTime(Duration time) {
movingTime = movingTime.plus(time); movingTime = movingTime.plus(time);
} }
@@ -265,6 +267,7 @@ public class TrackStatistics {
this.totalAltitudeGain_m = totalAltitudeGain_m; this.totalAltitudeGain_m = totalAltitudeGain_m;
} }
@VisibleForTesting(otherwise = VisibleForTesting.PACKAGE_PRIVATE)
public void addTotalAltitudeGain(float gain_m) { public void addTotalAltitudeGain(float gain_m) {
if (totalAltitudeGain_m == null) { if (totalAltitudeGain_m == null) {
totalAltitudeGain_m = 0f; totalAltitudeGain_m = 0f;
@@ -285,6 +288,7 @@ public class TrackStatistics {
this.totalAltitudeLoss_m = totalAltitudeLoss_m; this.totalAltitudeLoss_m = totalAltitudeLoss_m;
} }
@VisibleForTesting(otherwise = VisibleForTesting.PACKAGE_PRIVATE)
public void addTotalAltitudeLoss(float loss_m) { public void addTotalAltitudeLoss(float loss_m) {
if (totalAltitudeLoss_m == null) { if (totalAltitudeLoss_m == null) {
totalAltitudeLoss_m = 0f; totalAltitudeLoss_m = 0f;