forked from upstream-mirrors/OpenTracks
TrackPoint is now a record.
This commit is contained in:
@@ -17,9 +17,13 @@ import de.dennisguse.opentracks.data.MarkerIterator;
|
||||
import de.dennisguse.opentracks.data.TrackPointIterator;
|
||||
import de.dennisguse.opentracks.data.models.ActivityType;
|
||||
import de.dennisguse.opentracks.data.models.Altitude;
|
||||
import de.dennisguse.opentracks.data.models.AltitudeGainLoss;
|
||||
import de.dennisguse.opentracks.data.models.Cadence;
|
||||
import de.dennisguse.opentracks.data.models.Distance;
|
||||
import de.dennisguse.opentracks.data.models.HeartRate;
|
||||
import de.dennisguse.opentracks.data.models.Marker;
|
||||
import de.dennisguse.opentracks.data.models.Position;
|
||||
import de.dennisguse.opentracks.data.models.Power;
|
||||
import de.dennisguse.opentracks.data.models.Speed;
|
||||
import de.dennisguse.opentracks.data.models.Statistics;
|
||||
import de.dennisguse.opentracks.data.models.Track;
|
||||
@@ -92,9 +96,9 @@ public class TestDataUtil {
|
||||
);
|
||||
|
||||
List<Marker> markers = List.of(
|
||||
new Marker(null, trackId, "Marker 1", "Marker description 1", "Marker typeLocalized 3", trackPoints.get(1).getPosition(), null),
|
||||
new Marker(null, trackId, "Marker 2", "Marker description 2", "Marker typeLocalized 3", trackPoints.get(4).getPosition(), null),
|
||||
new Marker(null, trackId, "Marker 3", "Marker description 3", "Marker typeLocalized 3", trackPoints.get(5).getPosition(), null)
|
||||
new Marker(null, trackId, "Marker 1", "Marker description 1", "Marker typeLocalized 3", trackPoints.get(1).position(), null),
|
||||
new Marker(null, trackId, "Marker 2", "Marker description 2", "Marker typeLocalized 3", trackPoints.get(4).position(), null),
|
||||
new Marker(null, trackId, "Marker 3", "Marker description 3", "Marker typeLocalized 3", trackPoints.get(5).position(), null)
|
||||
);
|
||||
|
||||
return new TrackData(track, trackPoints, markers);
|
||||
@@ -112,8 +116,10 @@ public class TestDataUtil {
|
||||
return pair.first;
|
||||
}
|
||||
|
||||
public static TrackPoint createTrackPoint(int i) {
|
||||
TrackPoint trackPoint = new TrackPoint(TrackPoint.Type.TRACKPOINT,
|
||||
public static TrackPoint createTrackPoint(int i, TrackPoint.Type type, AltitudeGainLoss altitudeGainLoss) {
|
||||
return new TrackPoint(
|
||||
null,
|
||||
type,
|
||||
new Position(
|
||||
Instant.ofEpochSecond(i),
|
||||
INITIAL_LATITUDE + (double) i / 10000.0,
|
||||
@@ -123,21 +129,24 @@ public class TestDataUtil {
|
||||
null,
|
||||
null,
|
||||
Speed.of(5f + (i / 10f))
|
||||
));
|
||||
|
||||
trackPoint.setHeartRate(100f + i % 80);
|
||||
trackPoint.setCadence(300f + i);
|
||||
trackPoint.setPower(400f + i);
|
||||
trackPoint.setAltitudeGainLoss(ALTITUDE_GAIN, ALTITUDE_LOSS);
|
||||
return trackPoint;
|
||||
),
|
||||
null,
|
||||
HeartRate.of(100f + i % 80),
|
||||
Cadence.of(300f + i),
|
||||
Power.of(400f + i),
|
||||
altitudeGainLoss
|
||||
);
|
||||
}
|
||||
|
||||
public static TrackPoint createTrackPoint(int i, TrackPoint.Type type) {
|
||||
TrackPoint trackPoint = createTrackPoint(i);
|
||||
trackPoint.setType(type);
|
||||
return trackPoint;
|
||||
return createTrackPoint(i, TrackPoint.Type.TRACKPOINT, new AltitudeGainLoss(ALTITUDE_GAIN, ALTITUDE_LOSS));
|
||||
}
|
||||
|
||||
public static TrackPoint createTrackPoint(int i) {
|
||||
return createTrackPoint(i, TrackPoint.Type.TRACKPOINT);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Inserts a track with locations into the database.
|
||||
*
|
||||
@@ -164,7 +173,7 @@ public class TestDataUtil {
|
||||
"Marker name",
|
||||
description,
|
||||
"Marker typeLocalized",
|
||||
trackPoint.getPosition(),
|
||||
trackPoint.position(),
|
||||
photoUri);
|
||||
}
|
||||
|
||||
|
||||
+14
-12
@@ -6,8 +6,12 @@ import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import de.dennisguse.opentracks.data.models.Altitude;
|
||||
import de.dennisguse.opentracks.data.models.AltitudeGainLoss;
|
||||
import de.dennisguse.opentracks.data.models.Cadence;
|
||||
import de.dennisguse.opentracks.data.models.Distance;
|
||||
import de.dennisguse.opentracks.data.models.HeartRate;
|
||||
import de.dennisguse.opentracks.data.models.Position;
|
||||
import de.dennisguse.opentracks.data.models.Power;
|
||||
import de.dennisguse.opentracks.data.models.Speed;
|
||||
import de.dennisguse.opentracks.data.models.TrackPoint;
|
||||
|
||||
@@ -20,7 +24,9 @@ public class TestSensorDataUtil {
|
||||
sensorDataList.add(new TestSensorDataUtil.SensorData(time, hr, cadence, power, type));
|
||||
int i = trackPointList.size() + 1;
|
||||
|
||||
TrackPoint tp = new TrackPoint(type,
|
||||
TrackPoint tp = new TrackPoint(
|
||||
null,
|
||||
type,
|
||||
new Position(
|
||||
time,
|
||||
TestDataUtil.INITIAL_LATITUDE + (double) i / 10000.0,
|
||||
@@ -30,18 +36,14 @@ public class TestSensorDataUtil {
|
||||
null,
|
||||
null,
|
||||
Speed.of(5f + (i / 10f))
|
||||
)
|
||||
),
|
||||
null,
|
||||
HeartRate.ofOrNull(hr),
|
||||
cadence != null ? Cadence.of(cadence) : null,
|
||||
power != null ? Power.of(power) : null,
|
||||
new AltitudeGainLoss(3, 3)
|
||||
);
|
||||
if (hr != null) {
|
||||
tp.setHeartRate(hr);
|
||||
}
|
||||
if (cadence != null) {
|
||||
tp.setCadence(cadence);
|
||||
}
|
||||
if (power != null) {
|
||||
tp.setPower(power);
|
||||
}
|
||||
tp.setAltitudeGainLoss(3, 3);
|
||||
|
||||
trackPointList.add(tp);
|
||||
}
|
||||
|
||||
|
||||
+43
-28
@@ -54,19 +54,22 @@ import de.dennisguse.opentracks.data.models.ActivityType;
|
||||
import de.dennisguse.opentracks.data.models.Altitude;
|
||||
import de.dennisguse.opentracks.data.models.AltitudeExtremities;
|
||||
import de.dennisguse.opentracks.data.models.AltitudeGainLoss;
|
||||
import de.dennisguse.opentracks.data.models.Cadence;
|
||||
import de.dennisguse.opentracks.data.models.Distance;
|
||||
import de.dennisguse.opentracks.data.models.HeartRate;
|
||||
import de.dennisguse.opentracks.data.models.Marker;
|
||||
import de.dennisguse.opentracks.data.models.MarkerBuilder;
|
||||
import de.dennisguse.opentracks.data.models.Position;
|
||||
import de.dennisguse.opentracks.data.models.Power;
|
||||
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.models.TrackBuilder;
|
||||
import de.dennisguse.opentracks.data.models.TrackPoint;
|
||||
import de.dennisguse.opentracks.data.statistics.SensorStatistics;
|
||||
import de.dennisguse.opentracks.data.tables.MarkerColumns;
|
||||
import de.dennisguse.opentracks.data.tables.TrackPointsColumns;
|
||||
import de.dennisguse.opentracks.data.tables.TracksColumns;
|
||||
import de.dennisguse.opentracks.data.statistics.SensorStatistics;
|
||||
import de.dennisguse.opentracks.util.FileUtils;
|
||||
|
||||
/**
|
||||
@@ -124,7 +127,7 @@ public class CustomContentProviderUtilsTest {
|
||||
assertNotNull(trackPoint);
|
||||
locations.add(trackPoint);
|
||||
// Make sure the IDs are returned in the right order.
|
||||
assertEquals(lastPointId.id() - numPoints + locations.size(), trackPoint.getId().id());
|
||||
assertEquals(lastPointId.id() - numPoints + locations.size(), trackPoint.id().id());
|
||||
}
|
||||
assertEquals(numPoints, locations.size());
|
||||
}
|
||||
@@ -167,7 +170,7 @@ public class CustomContentProviderUtilsTest {
|
||||
try (TrackPointIterator it = contentProviderUtils.getTrackPointIterator(id, null)) {
|
||||
while (it.hasNext()) {
|
||||
TrackPoint trackPoint = it.next();
|
||||
lastPointId = trackPoint.getId();
|
||||
lastPointId = trackPoint.id();
|
||||
counter++;
|
||||
}
|
||||
}
|
||||
@@ -237,7 +240,7 @@ public class CustomContentProviderUtilsTest {
|
||||
"",
|
||||
"",
|
||||
"",
|
||||
getLastValidTrackPoint(trackId).getPosition(),
|
||||
getLastValidTrackPoint(trackId).position(),
|
||||
null
|
||||
));
|
||||
|
||||
@@ -300,7 +303,7 @@ public class CustomContentProviderUtilsTest {
|
||||
"",
|
||||
"",
|
||||
"",
|
||||
getLastValidTrackPoint(trackId1).getPosition(),
|
||||
getLastValidTrackPoint(trackId1).position(),
|
||||
null
|
||||
));
|
||||
|
||||
@@ -473,7 +476,7 @@ public class CustomContentProviderUtilsTest {
|
||||
"",
|
||||
TEST_DESC,
|
||||
"",
|
||||
getLastValidTrackPoint(trackId).getPosition(),
|
||||
getLastValidTrackPoint(trackId).position(),
|
||||
null
|
||||
));
|
||||
|
||||
@@ -553,7 +556,7 @@ public class CustomContentProviderUtilsTest {
|
||||
"",
|
||||
MOCK_DESC,
|
||||
"",
|
||||
getLastValidTrackPoint(trackId).getPosition(),
|
||||
getLastValidTrackPoint(trackId).position(),
|
||||
null
|
||||
));
|
||||
Marker.Id marker2Id = contentProviderUtils.insertMarker(new Marker(
|
||||
@@ -562,7 +565,7 @@ public class CustomContentProviderUtilsTest {
|
||||
"",
|
||||
MOCK_DESC,
|
||||
"",
|
||||
getLastValidTrackPoint(trackId).getPosition(),
|
||||
getLastValidTrackPoint(trackId).position(),
|
||||
null
|
||||
));
|
||||
|
||||
@@ -589,7 +592,7 @@ public class CustomContentProviderUtilsTest {
|
||||
"",
|
||||
MOCK_DESC,
|
||||
"",
|
||||
getLastValidTrackPoint(trackId).getPosition(),
|
||||
getLastValidTrackPoint(trackId).position(),
|
||||
null
|
||||
));
|
||||
}
|
||||
@@ -613,7 +616,7 @@ public class CustomContentProviderUtilsTest {
|
||||
"",
|
||||
TEST_DESC,
|
||||
"",
|
||||
getLastValidTrackPoint(trackId).getPosition(),
|
||||
getLastValidTrackPoint(trackId).position(),
|
||||
null
|
||||
));
|
||||
|
||||
@@ -636,7 +639,7 @@ public class CustomContentProviderUtilsTest {
|
||||
"",
|
||||
TEST_DESC,
|
||||
"",
|
||||
getLastValidTrackPoint(trackId).getPosition(),
|
||||
getLastValidTrackPoint(trackId).position(),
|
||||
null
|
||||
));
|
||||
|
||||
@@ -647,7 +650,7 @@ public class CustomContentProviderUtilsTest {
|
||||
"",
|
||||
TEST_DESC_NEW,
|
||||
"",
|
||||
getLastValidTrackPoint(trackId).getPosition(),
|
||||
getLastValidTrackPoint(trackId).position(),
|
||||
null
|
||||
));
|
||||
|
||||
@@ -803,20 +806,26 @@ public class CustomContentProviderUtilsTest {
|
||||
Track.Id trackId = new Track.Id(System.currentTimeMillis());
|
||||
TestDataUtil.createTrackAndInsert(contentProviderUtils, trackId, 10);
|
||||
|
||||
TrackPoint trackPoint = TestDataUtil.createTrackPoint(5);
|
||||
trackPoint.setHeartRate(1F);
|
||||
trackPoint.setCadence(2F);
|
||||
trackPoint.setPower(3F);
|
||||
TrackPoint trackPoint = new TrackPoint(
|
||||
null,
|
||||
TrackPoint.Type.TRACKPOINT,
|
||||
TestDataUtil.createTrackPoint(5).position(),
|
||||
null,
|
||||
HeartRate.of(1),
|
||||
Cadence.of(2),
|
||||
Power.of(3),
|
||||
null
|
||||
);
|
||||
|
||||
// when
|
||||
contentProviderUtils.insertTrackPoint(trackPoint, trackId);
|
||||
|
||||
// then
|
||||
List<TrackPoint> trackPoints = TestDataUtil.getTrackPoints(contentProviderUtils, trackId);
|
||||
assertTrue(trackPoints.get(10).hasHeartRate());
|
||||
assertEquals(trackPoint.getHeartRate(), trackPoints.get(10).getHeartRate());
|
||||
assertEquals(trackPoint.getCadence(), trackPoints.get(10).getCadence());
|
||||
assertEquals(trackPoint.getPower(), trackPoints.get(10).getPower());
|
||||
assertNotNull(trackPoints.get(10).heartRate());
|
||||
assertEquals(trackPoint.heartRate(), trackPoints.get(10).heartRate());
|
||||
assertEquals(trackPoint.cadence(), trackPoints.get(10).cadence());
|
||||
assertEquals(trackPoint.power(), trackPoints.get(10).power());
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -857,9 +866,9 @@ public class CustomContentProviderUtilsTest {
|
||||
for (int i = 0; i < trackpointIds.size(); i++) {
|
||||
assertTrue(trackPointIterator.hasNext());
|
||||
TrackPoint trackPoint = trackPointIterator.next();
|
||||
assertEquals(startTrackPointId.id() + i, trackPoint.getId().id());
|
||||
assertEquals(startTrackPointId.id() + i, trackPoint.id().id());
|
||||
|
||||
Position position = trackPoint.getPosition();
|
||||
Position position = trackPoint.position();
|
||||
assertEquals(TestDataUtil.INITIAL_LATITUDE + i / 10000.0, position.latitude(), 0.01);
|
||||
assertEquals(TestDataUtil.INITIAL_LONGITUDE - i / 10000.0, position.longitude(), 0.01);
|
||||
assertEquals(i / 100.0, position.horizontalAccuracy().distance_m(), 0.01);
|
||||
@@ -879,11 +888,17 @@ public class CustomContentProviderUtilsTest {
|
||||
public void testGetSensorStats_noSensorData() {
|
||||
// given
|
||||
List<TrackPoint> trackPointList = new ArrayList<>();
|
||||
TrackPoint trackPoint = TestDataUtil.createTrackPoint(1);
|
||||
trackPoint.setType(TrackPoint.Type.TRACKPOINT);
|
||||
trackPoint.setPower(null);
|
||||
trackPoint.setCadence(null);
|
||||
trackPoint.setHeartRate(null);
|
||||
TrackPoint trackPoint = new TrackPoint(
|
||||
null,
|
||||
TrackPoint.Type.TRACKPOINT,
|
||||
TestDataUtil.createTrackPoint(1).position(),
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
null
|
||||
);
|
||||
|
||||
trackPointList.add(trackPoint);
|
||||
Track.Id trackId = new Track.Id(System.currentTimeMillis());
|
||||
Track track = TestDataUtil.createTrack(trackId);
|
||||
@@ -1217,7 +1232,7 @@ public class CustomContentProviderUtilsTest {
|
||||
|
||||
return trackpoints.reversed()
|
||||
.stream()
|
||||
.filter(it -> List.of(TrackPoint.Type.TRACKPOINT, TrackPoint.Type.SEGMENT_START_AUTOMATIC).contains(it.getType()))
|
||||
.filter(it -> List.of(TrackPoint.Type.TRACKPOINT, TrackPoint.Type.SEGMENT_START_AUTOMATIC).contains(it.type()))
|
||||
.findFirst().orElseThrow();
|
||||
|
||||
}
|
||||
|
||||
@@ -13,7 +13,7 @@ import de.dennisguse.opentracks.data.models.TrackPoint;
|
||||
public class TrackPointTest {
|
||||
|
||||
@Test
|
||||
public void distanceToPrevious() {
|
||||
public void distanceToPrevious_gps() {
|
||||
TrackPoint tp1 = new TrackPoint(TrackPoint.Type.TRACKPOINT,
|
||||
new Position(
|
||||
Instant.ofEpochMilli(0),
|
||||
@@ -38,20 +38,119 @@ public class TrackPointTest {
|
||||
|
||||
// without sensor distance
|
||||
assertEquals(11.13, tp2.distanceToPrevious(tp1).toM(), 0.01);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void distanceToPrevious_sensor_distance_first() {
|
||||
TrackPoint tp1 = new TrackPoint(
|
||||
null,
|
||||
TrackPoint.Type.TRACKPOINT,
|
||||
new Position(
|
||||
Instant.ofEpochMilli(0),
|
||||
0d,
|
||||
0.0001,
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
null),
|
||||
Distance.of(5),
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
null
|
||||
);
|
||||
|
||||
TrackPoint tp2 = new TrackPoint(TrackPoint.Type.TRACKPOINT,
|
||||
new Position(
|
||||
Instant.ofEpochMilli(1),
|
||||
0d,
|
||||
0.0002,
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
null));
|
||||
|
||||
// tp1 has sensor distance
|
||||
tp1.setSensorDistance(Distance.of(5));
|
||||
tp1.setSensorDistance(null);
|
||||
assertEquals(11.13, tp2.distanceToPrevious(tp1).toM(), 0.01);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void distanceToPrevious_sensor_distance_second() {
|
||||
TrackPoint tp1 = new TrackPoint(TrackPoint.Type.TRACKPOINT,
|
||||
new Position(
|
||||
Instant.ofEpochMilli(0),
|
||||
0d,
|
||||
0.0001,
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
null));
|
||||
|
||||
TrackPoint tp2 = new TrackPoint(
|
||||
null,
|
||||
TrackPoint.Type.TRACKPOINT,
|
||||
new Position(
|
||||
Instant.ofEpochMilli(1),
|
||||
0d,
|
||||
0.0002,
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
null),
|
||||
Distance.of(5),
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
null);
|
||||
|
||||
// tp2 has sensor distance
|
||||
tp1.setSensorDistance(null);
|
||||
tp2.setSensorDistance(Distance.of(5));
|
||||
assertEquals(5, tp2.distanceToPrevious(tp1).toM(), 0.01);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void distanceToPrevious_sensor_distance_both() {
|
||||
TrackPoint tp1 = new TrackPoint(
|
||||
null,
|
||||
TrackPoint.Type.TRACKPOINT,
|
||||
new Position(
|
||||
Instant.ofEpochMilli(0),
|
||||
0d,
|
||||
0.0001,
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
null),
|
||||
Distance.of(10),
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
null
|
||||
);
|
||||
|
||||
TrackPoint tp2 = new TrackPoint(
|
||||
null,
|
||||
TrackPoint.Type.TRACKPOINT,
|
||||
new Position(
|
||||
Instant.ofEpochMilli(1),
|
||||
0d,
|
||||
0.0002,
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
null),
|
||||
Distance.of(5),
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
null);
|
||||
|
||||
// tp1 and tp2 have sensor distance
|
||||
tp1.setSensorDistance(Distance.of(10));
|
||||
tp2.setSensorDistance(Distance.of(5));
|
||||
assertEquals(5, tp2.distanceToPrevious(tp1).toM(), 0.01);
|
||||
}
|
||||
}
|
||||
+219
-121
@@ -26,7 +26,6 @@ import org.junit.BeforeClass;
|
||||
import org.junit.Rule;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
import org.mockito.Mockito;
|
||||
|
||||
import java.io.BufferedReader;
|
||||
import java.io.File;
|
||||
@@ -74,7 +73,7 @@ import de.dennisguse.opentracks.sensors.sensorData.AggregatorCyclingDistanceSpee
|
||||
import de.dennisguse.opentracks.sensors.sensorData.AggregatorCyclingPower;
|
||||
import de.dennisguse.opentracks.sensors.sensorData.AggregatorHeartRate;
|
||||
import de.dennisguse.opentracks.sensors.sensorData.Raw;
|
||||
import de.dennisguse.opentracks.sensors.sensorData.SensorDataSet;
|
||||
import de.dennisguse.opentracks.sensors.sensorData.SensorDataAggregator;
|
||||
import de.dennisguse.opentracks.services.TrackRecordingService;
|
||||
import de.dennisguse.opentracks.services.handlers.TrackPointCreator;
|
||||
|
||||
@@ -123,11 +122,13 @@ public class ExportImportTest {
|
||||
private TrackImporter trackImporter;
|
||||
|
||||
@Before
|
||||
public void fileSetup() throws IOException {
|
||||
public void fileSetup() throws IOException, TimeoutException {
|
||||
tmpFile = File.createTempFile("test", "test", context.getFilesDir());
|
||||
tmpFileUri = Uri.fromFile(tmpFile);
|
||||
|
||||
trackImporter = new TrackImporter(context, contentProviderUtils, Distance.of(200), true);
|
||||
|
||||
setUp();
|
||||
}
|
||||
|
||||
@After
|
||||
@@ -158,7 +159,15 @@ public class ExportImportTest {
|
||||
"Marker 1",
|
||||
"Marker 1 desc",
|
||||
"Marker 1 typeLocalized",
|
||||
service.getLastStoredTrackPointWithLocation().getPosition(),
|
||||
new Position(
|
||||
Instant.parse("2020-02-02T02:02:03Z"),
|
||||
3.1234567, 14.0014567,
|
||||
Distance.of(10),
|
||||
Altitude.WGS84.of(1020.25),
|
||||
Distance.of(13),
|
||||
null,
|
||||
Speed.of(15)
|
||||
),
|
||||
null
|
||||
));
|
||||
|
||||
@@ -166,7 +175,7 @@ public class ExportImportTest {
|
||||
trackPointCreator.setClock("2020-02-02T02:02:04Z");
|
||||
mockSensorData(trackPointCreator, 15f, sensorDistance, 66f, 3f, 50f, 1f);
|
||||
|
||||
trackPointCreator.setClock("2020-02-02T02:02:14Z");
|
||||
trackPointCreator.setClock("2020-02-02T02:02:14Z"); //ignored
|
||||
mockSensorData(trackPointCreator, 15f, null, 67f, 3f, 50f, null);
|
||||
trackPointCreator.setClock("2020-02-02T02:02:15Z");
|
||||
mockSensorData(trackPointCreator, null, null, 68f, 3f, 50f, null);
|
||||
@@ -181,12 +190,12 @@ public class ExportImportTest {
|
||||
"Marker 2",
|
||||
"Marker 2 desc",
|
||||
"Marker 2 typeLocalized",
|
||||
service.getLastStoredTrackPointWithLocation().getPosition(),
|
||||
service.getLastStoredTrackPointWithLocation().position(),
|
||||
null
|
||||
));
|
||||
|
||||
trackPointCreator.setClock("2020-02-02T02:02:18Z");
|
||||
trackPointCreator.getSensorManager().sensorDataSet = new SensorDataSet(trackPointCreator);
|
||||
trackPointCreator.getSensorManager().sensorDataAggregator = new SensorDataAggregator();
|
||||
service.endCurrentTrack();
|
||||
|
||||
trackPointCreator.setClock("2020-02-02T02:03:20Z");
|
||||
@@ -201,7 +210,7 @@ public class ExportImportTest {
|
||||
|
||||
sendLocation(trackPointCreator, "2020-02-02T02:03:50Z", 3.1234567, 16.001, 10, 27, 15, 999.123, 0f);
|
||||
|
||||
trackPointCreator.getSensorManager().sensorDataSet = new SensorDataSet(trackPointCreator);
|
||||
trackPointCreator.getSensorManager().sensorDataAggregator = new SensorDataAggregator();
|
||||
trackPointCreator.setClock("2020-02-02T02:04:00Z");
|
||||
service.endCurrentTrack();
|
||||
|
||||
@@ -218,13 +227,126 @@ public class ExportImportTest {
|
||||
@LargeTest
|
||||
@Test
|
||||
public void track() throws TimeoutException {
|
||||
setUp();
|
||||
|
||||
Track track = contentProviderUtils.getTrack(trackId);
|
||||
Statistics trackStatistics = track.statistics();
|
||||
|
||||
assertEquals(ZoneOffset.of("+01:00"), track.zoneOffset());
|
||||
|
||||
List<TrackPoint> actual = TestDataUtil.getTrackPoints(contentProviderUtils, trackId);
|
||||
new TrackPointAssert().assertEquals(List.of(
|
||||
new TrackPoint(TrackPoint.Type.SEGMENT_START_MANUAL, Instant.parse("2020-02-02T02:02:02Z")),
|
||||
new TrackPoint(
|
||||
null,
|
||||
TrackPoint.Type.TRACKPOINT,
|
||||
new Position(
|
||||
Instant.parse("2020-02-02T02:02:03Z"),
|
||||
3.123456, 14.001456, Distance.of(10),
|
||||
Altitude.WGS84.of(1020.25), null,
|
||||
null,
|
||||
Speed.of(15)),
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
new AltitudeGainLoss(1, 1)
|
||||
),
|
||||
new TrackPoint(
|
||||
null,
|
||||
TrackPoint.Type.TRACKPOINT,
|
||||
new Position(
|
||||
Instant.parse("2020-02-02T02:02:04Z"),
|
||||
null, null, null,
|
||||
null, null,
|
||||
null,
|
||||
Speed.of(15)),
|
||||
Distance.of(10),
|
||||
HeartRate.of(66),
|
||||
Cadence.of(3),
|
||||
Power.of(50),
|
||||
new AltitudeGainLoss(1, 1)
|
||||
),
|
||||
new TrackPoint(
|
||||
null,
|
||||
TrackPoint.Type.TRACKPOINT,
|
||||
Position.of(Instant.parse("2020-02-02T02:02:15Z")),
|
||||
null,
|
||||
HeartRate.of(68),
|
||||
Cadence.of(3),
|
||||
Power.of(50),
|
||||
null
|
||||
),
|
||||
new TrackPoint(
|
||||
null,
|
||||
TrackPoint.Type.TRACKPOINT,
|
||||
new Position(
|
||||
Instant.parse("2020-02-02T02:02:17Z"),
|
||||
3.123456, 14.001456, Distance.of(10),
|
||||
Altitude.WGS84.of(1020.25), null,
|
||||
null,
|
||||
Speed.of(5)),
|
||||
Distance.of(2),
|
||||
HeartRate.of(69),
|
||||
Cadence.of(3),
|
||||
Power.of(50),
|
||||
new AltitudeGainLoss(0, 0)
|
||||
),
|
||||
new TrackPoint(TrackPoint.Type.SEGMENT_END_MANUAL, Instant.parse("2020-02-02T02:02:18Z")),
|
||||
new TrackPoint(TrackPoint.Type.SEGMENT_START_MANUAL, Instant.parse("2020-02-02T02:03:20Z")),
|
||||
new TrackPoint(
|
||||
null,
|
||||
TrackPoint.Type.TRACKPOINT,
|
||||
new Position(
|
||||
Instant.parse("2020-02-02T02:03:21Z"),
|
||||
3.123456, 14.002456, Distance.of(10),
|
||||
Altitude.WGS84.of(999.1229858398438), null,
|
||||
null,
|
||||
Speed.of(15)),
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
new AltitudeGainLoss(0, 0)
|
||||
),
|
||||
new TrackPoint(
|
||||
null,
|
||||
TrackPoint.Type.SEGMENT_START_AUTOMATIC,
|
||||
new Position(Instant.parse("2020-02-02T02:03:22Z"),
|
||||
3.123456, 16d, Distance.of(10),
|
||||
Altitude.WGS84.of(999.1229858398438), null,
|
||||
null,
|
||||
Speed.of(15)),
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
new AltitudeGainLoss(0, 0)
|
||||
),
|
||||
new TrackPoint(
|
||||
null,
|
||||
TrackPoint.Type.IDLE,
|
||||
Position.of(Instant.parse("2020-02-02T02:03:30Z")),
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
new AltitudeGainLoss(0, 0)
|
||||
),
|
||||
new TrackPoint(
|
||||
null,
|
||||
TrackPoint.Type.TRACKPOINT,
|
||||
new Position(
|
||||
Instant.parse("2020-02-02T02:03:50Z"),
|
||||
3.123456, 16.001, Distance.of(10),
|
||||
Altitude.WGS84.of(999.1229858398438), null,
|
||||
null, Speed.of(15)),
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
new AltitudeGainLoss(0, 0)),
|
||||
new TrackPoint(TrackPoint.Type.SEGMENT_END_MANUAL, Instant.parse("2020-02-02T02:04:00Z"))
|
||||
), actual);
|
||||
|
||||
assertEquals(new Statistics(
|
||||
Instant.parse("2020-02-02T02:02:02Z"),
|
||||
Instant.parse("2020-02-02T02:04:00Z"),
|
||||
@@ -238,77 +360,12 @@ public class ExportImportTest {
|
||||
null
|
||||
),
|
||||
trackStatistics);
|
||||
|
||||
List<TrackPoint> actual = TestDataUtil.getTrackPoints(contentProviderUtils, trackId);
|
||||
new TrackPointAssert().assertEquals(List.of(
|
||||
new TrackPoint(TrackPoint.Type.SEGMENT_START_MANUAL, Instant.parse("2020-02-02T02:02:02Z")),
|
||||
new TrackPoint(TrackPoint.Type.TRACKPOINT,
|
||||
new Position(
|
||||
Instant.parse("2020-02-02T02:02:03Z"),
|
||||
3.123456, 14.001456, Distance.of(10),
|
||||
Altitude.WGS84.of(1020.25), null,
|
||||
null,
|
||||
Speed.of(15)))
|
||||
.setAltitudeGainLoss(1, 1),
|
||||
new TrackPoint(TrackPoint.Type.TRACKPOINT,
|
||||
Instant.parse("2020-02-02T02:02:04Z"))
|
||||
.setSensorDistance(Distance.of(10))
|
||||
.setSpeed(Speed.of(15))
|
||||
.setHeartRate(HeartRate.of(66))
|
||||
.setCadence(3)
|
||||
.setPower(50).setAltitudeGainLoss(1, 1),
|
||||
new TrackPoint(TrackPoint.Type.TRACKPOINT, Instant.parse("2020-02-02T02:02:15Z"))
|
||||
.setHeartRate(HeartRate.of(68))
|
||||
.setCadence(3)
|
||||
.setPower(50),
|
||||
new TrackPoint(TrackPoint.Type.TRACKPOINT,
|
||||
new Position(
|
||||
Instant.parse("2020-02-02T02:02:17Z"),
|
||||
3.123456, 14.001456, Distance.of(10),
|
||||
Altitude.WGS84.of(1020.25), null,
|
||||
null,
|
||||
Speed.of(5)))
|
||||
.setSensorDistance(Distance.of(2))
|
||||
.setAltitudeGainLoss(0, 0)
|
||||
.setHeartRate(HeartRate.of(69))
|
||||
.setCadence(3)
|
||||
.setPower(50),
|
||||
new TrackPoint(TrackPoint.Type.SEGMENT_END_MANUAL, Instant.parse("2020-02-02T02:02:18Z")),
|
||||
new TrackPoint(TrackPoint.Type.SEGMENT_START_MANUAL, Instant.parse("2020-02-02T02:03:20Z")),
|
||||
new TrackPoint(TrackPoint.Type.TRACKPOINT,
|
||||
new Position(
|
||||
Instant.parse("2020-02-02T02:03:21Z"),
|
||||
3.123456, 14.002456, Distance.of(10),
|
||||
Altitude.WGS84.of(999.1229858398438), null,
|
||||
null,
|
||||
Speed.of(15)))
|
||||
.setAltitudeGainLoss(0, 0),
|
||||
new TrackPoint(TrackPoint.Type.SEGMENT_START_AUTOMATIC,
|
||||
new Position(Instant.parse("2020-02-02T02:03:22Z"),
|
||||
3.123456, 16d, Distance.of(10),
|
||||
Altitude.WGS84.of(999.1229858398438), null,
|
||||
null,
|
||||
Speed.of(15)))
|
||||
.setAltitudeGainLoss(0, 0),
|
||||
new TrackPoint(TrackPoint.Type.IDLE, Instant.parse("2020-02-02T02:03:30Z"))
|
||||
.setAltitudeGainLoss(0, 0),
|
||||
new TrackPoint(TrackPoint.Type.TRACKPOINT,
|
||||
new Position(
|
||||
Instant.parse("2020-02-02T02:03:50Z"),
|
||||
3.123456, 16.001, Distance.of(10),
|
||||
Altitude.WGS84.of(999.1229858398438), null,
|
||||
null, Speed.of(15)))
|
||||
.setAltitudeGainLoss(0, 0),
|
||||
new TrackPoint(TrackPoint.Type.SEGMENT_END_MANUAL, Instant.parse("2020-02-02T02:04:00Z"))
|
||||
), actual);
|
||||
}
|
||||
|
||||
//TODO Does not test marker images
|
||||
@LargeTest
|
||||
@Test
|
||||
public void kmz_with_trackdetail_and_sensordata() throws TimeoutException, IOException {
|
||||
setUp();
|
||||
|
||||
// given
|
||||
Track track = contentProviderUtils.getTrack(trackId);
|
||||
|
||||
@@ -361,8 +418,6 @@ public class ExportImportTest {
|
||||
@LargeTest
|
||||
@Test(expected = ImportAlreadyExistsException.class)
|
||||
public void kml_with_trackdetail_and_sensordata_duplicate_trackUUID() throws TimeoutException, IOException {
|
||||
setUp();
|
||||
|
||||
// given
|
||||
SharedPreferences.Editor editor = PreferenceManager.getDefaultSharedPreferences(context).edit();
|
||||
editor.putBoolean(context.getString(R.string.import_prevent_reimport_key), true);
|
||||
@@ -387,8 +442,6 @@ public class ExportImportTest {
|
||||
@LargeTest
|
||||
@Test
|
||||
public void gpx() throws TimeoutException, IOException {
|
||||
setUp();
|
||||
|
||||
// given
|
||||
Track track = contentProviderUtils.getTrack(trackId);
|
||||
|
||||
@@ -421,51 +474,81 @@ public class ExportImportTest {
|
||||
.setDelta(0.05); // speed is not fully
|
||||
List<TrackPoint> actual = TestDataUtil.getTrackPoints(contentProviderUtils, importTrackId);
|
||||
a.assertEquals(List.of(
|
||||
new TrackPoint(TrackPoint.Type.SEGMENT_START_AUTOMATIC,
|
||||
new TrackPoint(
|
||||
null,
|
||||
TrackPoint.Type.SEGMENT_START_AUTOMATIC,
|
||||
new Position(
|
||||
Instant.parse("2020-02-02T02:02:03Z"),
|
||||
3.123456, 14.001456d, Distance.of(10),
|
||||
Altitude.WGS84.of(1020.2), null,
|
||||
null,
|
||||
Speed.of(15)))
|
||||
.setAltitudeGainLoss(1, 1),
|
||||
new TrackPoint(TrackPoint.Type.TRACKPOINT,
|
||||
Speed.of(15)),
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
new AltitudeGainLoss(1, 1)
|
||||
),
|
||||
new TrackPoint(
|
||||
null,
|
||||
TrackPoint.Type.TRACKPOINT,
|
||||
new Position(
|
||||
Instant.parse("2020-02-02T02:02:17Z"),
|
||||
3.123456, 14.001456, Distance.of(10),
|
||||
Altitude.WGS84.of(1020.2), null,
|
||||
null,
|
||||
Speed.of(5)))
|
||||
.setAltitudeGainLoss(1, 1)
|
||||
.setSensorDistance(Distance.of(12))
|
||||
.setHeartRate(69)
|
||||
.setPower(50f)
|
||||
.setCadence(3f),
|
||||
new TrackPoint(TrackPoint.Type.SEGMENT_START_AUTOMATIC,
|
||||
Speed.of(5)),
|
||||
Distance.of(12),
|
||||
HeartRate.of(69),
|
||||
Cadence.of(3),
|
||||
Power.of(50),
|
||||
new AltitudeGainLoss(1, 1)
|
||||
),
|
||||
new TrackPoint(
|
||||
null,
|
||||
TrackPoint.Type.SEGMENT_START_AUTOMATIC,
|
||||
new Position(
|
||||
Instant.parse("2020-02-02T02:03:21Z"),
|
||||
3.123456, 14.002456, Distance.of(10),
|
||||
Altitude.WGS84.of(999.0999755859375), null,
|
||||
null,
|
||||
Speed.of(15)))
|
||||
.setAltitudeGainLoss(0, 0),
|
||||
new TrackPoint(TrackPoint.Type.SEGMENT_START_AUTOMATIC,
|
||||
Speed.of(15)),
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
new AltitudeGainLoss(0, 0)
|
||||
),
|
||||
new TrackPoint(
|
||||
null,
|
||||
TrackPoint.Type.SEGMENT_START_AUTOMATIC,
|
||||
new Position(
|
||||
Instant.parse("2020-02-02T02:03:22Z"),
|
||||
3.123456, 16d, Distance.of(10),
|
||||
Altitude.WGS84.of(999.0999755859375), null,
|
||||
null,
|
||||
Speed.of(15)))
|
||||
.setAltitudeGainLoss(0, 0),
|
||||
new TrackPoint(TrackPoint.Type.TRACKPOINT,
|
||||
Speed.of(15)),
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
new AltitudeGainLoss(0, 0)
|
||||
),
|
||||
new TrackPoint(
|
||||
null,
|
||||
TrackPoint.Type.TRACKPOINT,
|
||||
new Position(
|
||||
Instant.parse("2020-02-02T02:03:50Z"),
|
||||
3.123456, 16.001, Distance.of(10),
|
||||
Altitude.WGS84.of(999.0999755859375), null,
|
||||
null,
|
||||
Speed.of(10)))
|
||||
.setAltitudeGainLoss(0, 0)
|
||||
.setSpeed(Speed.of(15))
|
||||
Speed.of(15)),
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
new AltitudeGainLoss(0, 0)
|
||||
)
|
||||
), actual);
|
||||
|
||||
// 3. trackstatistics
|
||||
@@ -493,8 +576,6 @@ public class ExportImportTest {
|
||||
@LargeTest
|
||||
@Test(expected = ImportAlreadyExistsException.class)
|
||||
public void gpx_duplicate_trackUUID() throws TimeoutException, IOException {
|
||||
setUp();
|
||||
|
||||
// given
|
||||
SharedPreferences.Editor editor = PreferenceManager.getDefaultSharedPreferences(context).edit();
|
||||
editor.putBoolean(context.getString(R.string.import_prevent_reimport_key), true);
|
||||
@@ -521,8 +602,6 @@ public class ExportImportTest {
|
||||
@LargeTest
|
||||
@Test
|
||||
public void csv_export_only() throws TimeoutException, IOException {
|
||||
setUp();
|
||||
|
||||
// given
|
||||
Track track = contentProviderUtils.getTrack(trackId);
|
||||
|
||||
@@ -567,16 +646,16 @@ public class ExportImportTest {
|
||||
}
|
||||
|
||||
private void mockSensorData(TrackPointCreator trackPointCreator, Float speed, Distance distance, float heartRate, float cadence, Float power, Float altitudeGain) {
|
||||
SensorDataSet sensorDataSet = trackPointCreator.getSensorManager().sensorDataSet;
|
||||
SensorDataAggregator sensorDataAggregator = trackPointCreator.getSensorManager().sensorDataAggregator;
|
||||
|
||||
AggregatorCyclingPower cyclingPower = new AggregatorCyclingPower("", "");
|
||||
cyclingPower.add(new Raw<>(trackPointCreator.createNow(), new BluetoothHandlerManagerCyclingPower.Data(Power.of(power), null)));
|
||||
sensorDataSet.add(cyclingPower);
|
||||
sensorDataAggregator.add(cyclingPower);
|
||||
|
||||
|
||||
AggregatorHeartRate avgHeartRate = new AggregatorHeartRate("", "");
|
||||
avgHeartRate.add(new Raw<>(trackPointCreator.createNow(), HeartRate.of(heartRate)));
|
||||
sensorDataSet.add(avgHeartRate);
|
||||
sensorDataAggregator.add(avgHeartRate);
|
||||
|
||||
AggregatorCyclingCadence cyclingCadence = new AggregatorCyclingCadence("", "") {
|
||||
@NonNull
|
||||
@@ -590,33 +669,52 @@ public class ExportImportTest {
|
||||
return true;
|
||||
}
|
||||
};
|
||||
sensorDataSet.add(cyclingCadence);
|
||||
sensorDataAggregator.add(cyclingCadence);
|
||||
|
||||
if (distance != null && speed != null) {
|
||||
AggregatorCyclingDistanceSpeed distanceSpeed = Mockito.mock(AggregatorCyclingDistanceSpeed.class);
|
||||
Mockito.when(distanceSpeed.hasReceivedData()).thenReturn(true);
|
||||
Mockito.when(distanceSpeed.getAggregatedValue(Mockito.any())).thenReturn(new AggregatorCyclingDistanceSpeed.Data(null, distance, Speed.of(speed)));
|
||||
sensorDataSet.add(distanceSpeed);
|
||||
AggregatorCyclingDistanceSpeed aggregatorCyclingDistanceSpeed = new AggregatorCyclingDistanceSpeed("", "") {
|
||||
@NonNull
|
||||
@Override
|
||||
public Data getAggregatedValue(Instant now) {
|
||||
return new AggregatorCyclingDistanceSpeed.Data(null, distance, Speed.of(speed));
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean hasReceivedData() {
|
||||
return true;
|
||||
}
|
||||
};
|
||||
sensorDataAggregator.add(aggregatorCyclingDistanceSpeed);
|
||||
} else {
|
||||
sensorDataSet.add(new AggregatorCyclingDistanceSpeed("", ""));
|
||||
sensorDataAggregator.add(new AggregatorCyclingDistanceSpeed("", ""));
|
||||
}
|
||||
|
||||
mockAltitudeChange(trackPointCreator, altitudeGain);
|
||||
|
||||
trackPointCreator.onChange(sensorDataSet);
|
||||
trackPointCreator.onChange();
|
||||
}
|
||||
|
||||
private void mockAltitudeChange(TrackPointCreator trackPointCreator, Float altitudeGain) {
|
||||
SensorDataSet sensorDataSet = trackPointCreator.getSensorManager().sensorDataSet;
|
||||
SensorDataAggregator sensorDataAggregator = trackPointCreator.getSensorManager().sensorDataAggregator;
|
||||
|
||||
if (altitudeGain != null) {
|
||||
AggregatorBarometer barometer = Mockito.mock(AggregatorBarometer.class);
|
||||
Mockito.when(barometer.hasReceivedData()).thenReturn(true);
|
||||
Mockito.when(barometer.getAggregatedValue(Mockito.any())).thenReturn(new AltitudeGainLoss(altitudeGain, altitudeGain));
|
||||
sensorDataSet.add(barometer);
|
||||
} else {
|
||||
sensorDataSet.add(new AggregatorBarometer("test", null));
|
||||
if (altitudeGain == null) {
|
||||
sensorDataAggregator.add(new AggregatorBarometer("test", null));
|
||||
return;
|
||||
}
|
||||
|
||||
AggregatorBarometer aggregatorBarometer = new AggregatorBarometer("", "") {
|
||||
@NonNull
|
||||
@Override
|
||||
public AltitudeGainLoss getAggregatedValue(Instant now) {
|
||||
return new AltitudeGainLoss(altitudeGain, altitudeGain);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean hasReceivedData() {
|
||||
return true;
|
||||
}
|
||||
};
|
||||
sensorDataAggregator.add(aggregatorBarometer);
|
||||
}
|
||||
|
||||
private void sendLocation(TrackPointCreator trackPointCreator, String time, double latitude, double longitude, float accuracy, float verticalAccuracy, float speed, double altitude, Float altitudeGain) {
|
||||
|
||||
+55
-21
@@ -27,8 +27,12 @@ import de.dennisguse.opentracks.content.data.TestDataUtil;
|
||||
import de.dennisguse.opentracks.data.ContentProviderUtils;
|
||||
import de.dennisguse.opentracks.data.models.ActivityType;
|
||||
import de.dennisguse.opentracks.data.models.Altitude;
|
||||
import de.dennisguse.opentracks.data.models.AltitudeGainLoss;
|
||||
import de.dennisguse.opentracks.data.models.Cadence;
|
||||
import de.dennisguse.opentracks.data.models.Distance;
|
||||
import de.dennisguse.opentracks.data.models.HeartRate;
|
||||
import de.dennisguse.opentracks.data.models.Position;
|
||||
import de.dennisguse.opentracks.data.models.Power;
|
||||
import de.dennisguse.opentracks.data.models.Speed;
|
||||
import de.dennisguse.opentracks.data.models.Statistics;
|
||||
import de.dennisguse.opentracks.data.models.Track;
|
||||
@@ -193,51 +197,81 @@ public class GPXTrackImporterTest {
|
||||
TrackPointAssert a = new TrackPointAssert()
|
||||
.setDelta(0.05); // speed is not fully
|
||||
a.assertEquals(List.of(
|
||||
new TrackPoint(TrackPoint.Type.SEGMENT_START_AUTOMATIC,
|
||||
new TrackPoint(
|
||||
null,
|
||||
TrackPoint.Type.SEGMENT_START_AUTOMATIC,
|
||||
new Position(
|
||||
Instant.parse("2020-02-02T02:02:03Z"),
|
||||
3d, 14d, Distance.of(10),
|
||||
Altitude.WGS84.of(10), null,
|
||||
null,
|
||||
Speed.of(15)))
|
||||
.setAltitudeGainLoss(1, 1),
|
||||
new TrackPoint(TrackPoint.Type.TRACKPOINT,
|
||||
Speed.of(15)),
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
new AltitudeGainLoss(1, 1)
|
||||
),
|
||||
new TrackPoint(
|
||||
null,
|
||||
TrackPoint.Type.TRACKPOINT,
|
||||
new Position(
|
||||
Instant.parse("2020-02-02T02:02:17Z"),
|
||||
3d, 14.001, Distance.of(10),
|
||||
Altitude.WGS84.of(10), null,
|
||||
null,
|
||||
Speed.of(5)))
|
||||
.setAltitudeGainLoss(1, 1)
|
||||
.setSensorDistance(Distance.of(12))
|
||||
.setHeartRate(69)
|
||||
.setPower(50f)
|
||||
.setCadence(3f),
|
||||
new TrackPoint(TrackPoint.Type.SEGMENT_START_AUTOMATIC,
|
||||
Speed.of(5)),
|
||||
Distance.of(12),
|
||||
HeartRate.of(69),
|
||||
Cadence.of(3),
|
||||
Power.of(50),
|
||||
new AltitudeGainLoss(1, 1)
|
||||
),
|
||||
new TrackPoint(
|
||||
null,
|
||||
TrackPoint.Type.SEGMENT_START_AUTOMATIC,
|
||||
new Position(
|
||||
Instant.parse("2020-02-02T02:03:21Z"),
|
||||
3d, 14.002, Distance.of(10),
|
||||
Altitude.WGS84.of(10), null,
|
||||
null,
|
||||
Speed.of(15)))
|
||||
.setAltitudeGainLoss(0, 0),
|
||||
new TrackPoint(TrackPoint.Type.SEGMENT_START_AUTOMATIC,
|
||||
Speed.of(15)),
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
new AltitudeGainLoss(0, 0)
|
||||
),
|
||||
new TrackPoint(
|
||||
null,
|
||||
TrackPoint.Type.SEGMENT_START_AUTOMATIC,
|
||||
new Position(
|
||||
Instant.parse("2020-02-02T02:03:22Z"),
|
||||
3d, 16d, Distance.of(10),
|
||||
Altitude.WGS84.of(10), null,
|
||||
null,
|
||||
Speed.of(15)))
|
||||
.setAltitudeGainLoss(0, 0),
|
||||
new TrackPoint(TrackPoint.Type.TRACKPOINT,
|
||||
Speed.of(15)),
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
new AltitudeGainLoss(0, 0)
|
||||
),
|
||||
new TrackPoint(
|
||||
null,
|
||||
TrackPoint.Type.TRACKPOINT,
|
||||
new Position(
|
||||
Instant.parse("2020-02-02T02:03:50Z"),
|
||||
3d, 16.001, Distance.of(10),
|
||||
Altitude.WGS84.of(10), null,
|
||||
null,
|
||||
Speed.of(10)))
|
||||
.setAltitudeGainLoss(0, 0)
|
||||
.setSpeed(Speed.of(15))
|
||||
Speed.of(15)),
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
new AltitudeGainLoss(0, 0)
|
||||
)
|
||||
), actual);
|
||||
}
|
||||
|
||||
@@ -247,7 +281,7 @@ public class GPXTrackImporterTest {
|
||||
// given
|
||||
XMLImporter importer = new XMLImporter(new GPXTrackImporter(context, trackImporter));
|
||||
InputStream inputStream = InstrumentationRegistry.getInstrumentation().getContext().getResources().openRawResource(de.dennisguse.opentracks.test.R.raw.gpx_timezone);
|
||||
try(InputStream inputStreamExpected = InstrumentationRegistry.getInstrumentation().getContext().getResources().openRawResource(de.dennisguse.opentracks.test.R.raw.gpx_timezone)) {
|
||||
try (InputStream inputStreamExpected = InstrumentationRegistry.getInstrumentation().getContext().getResources().openRawResource(de.dennisguse.opentracks.test.R.raw.gpx_timezone)) {
|
||||
ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
|
||||
|
||||
// when
|
||||
|
||||
+26
-38
@@ -16,57 +16,45 @@ public class TrackPointAssert {
|
||||
public void assertEquals(TrackPoint expected, TrackPoint actual) {
|
||||
Assert.assertEquals("time", expected.getTime(), actual.getTime());
|
||||
|
||||
Assert.assertEquals("type", expected.getType(), actual.getType());
|
||||
Assert.assertEquals("type", expected.type(), actual.type());
|
||||
|
||||
Assert.assertEquals("has location,", expected.hasLocation(), actual.hasLocation());
|
||||
if (expected.hasLocation()) {
|
||||
Assert.assertEquals("latitude", expected.getPosition().latitude(), actual.getPosition().latitude(), 0.001);
|
||||
Assert.assertEquals("longitude", expected.getPosition().longitude(), actual.getPosition().longitude(), 0.001);
|
||||
Assert.assertEquals("has location,", expected.position().hasLocation(), actual.position().hasLocation());
|
||||
if (expected.position().hasLocation()) {
|
||||
Assert.assertEquals("latitude", expected.position().latitude(), actual.position().latitude(), 0.001);
|
||||
Assert.assertEquals("longitude", expected.position().longitude(), actual.position().longitude(), 0.001);
|
||||
}
|
||||
|
||||
Assert.assertEquals("has altitude", expected.hasAltitude(), actual.hasAltitude());
|
||||
if (expected.hasAltitude()) {
|
||||
Assert.assertEquals("altitude", expected.getAltitude().toM(), actual.getAltitude().toM(), delta);
|
||||
Assert.assertEquals("has altitude", expected.position().hasAltitude(), actual.position().hasAltitude());
|
||||
if (expected.position().hasAltitude()) {
|
||||
Assert.assertEquals("altitude", expected.position().altitude().toM(), actual.position().altitude().toM(), delta);
|
||||
}
|
||||
|
||||
Assert.assertEquals("has altitudeGainLoss", expected.hasAltitudeGainLoss(), actual.hasAltitudeGainLoss());
|
||||
if (expected.hasAltitudeGainLoss()) {
|
||||
Assert.assertEquals("altitudeGainLoss", expected.getAltitudeGainLoss(), actual.getAltitudeGainLoss());
|
||||
Assert.assertEquals("altitudeGainLoss", expected.altitudeGainLoss(), actual.altitudeGainLoss());
|
||||
|
||||
Assert.assertEquals("has speed", expected.position().hasSpeed(), actual.position().hasSpeed());
|
||||
if (expected.position().hasSpeed()) {
|
||||
Assert.assertEquals("speed", expected.position().speed().toMPS(), actual.position().speed().toMPS(), delta);
|
||||
}
|
||||
|
||||
Assert.assertEquals("has speed", expected.hasSpeed(), actual.hasSpeed());
|
||||
if (expected.hasSpeed()) {
|
||||
Assert.assertEquals("speed", expected.getSpeed().toMPS(), actual.getSpeed().toMPS(), delta);
|
||||
Assert.assertEquals("has horizontalAccuracy", expected.position().hasHorizontalAccuracy(), actual.position().hasHorizontalAccuracy());
|
||||
if (expected.position().hasHorizontalAccuracy()) {
|
||||
Assert.assertEquals("horizontalAccuracy", expected.position().horizontalAccuracy().toM(), actual.position().horizontalAccuracy().toM(), delta);
|
||||
}
|
||||
Assert.assertEquals("has verticalAccuracy", expected.position().hasVerticalAccuracy(), actual.position().hasVerticalAccuracy());
|
||||
if (expected.position().hasVerticalAccuracy()) {
|
||||
Assert.assertEquals("verticalAccuracy", expected.position().verticalAccuracy().toM(), actual.position().verticalAccuracy().toM(), delta);
|
||||
}
|
||||
|
||||
Assert.assertEquals("has horizontalAccuracy", expected.hasHorizontalAccuracy(), actual.hasHorizontalAccuracy());
|
||||
if (expected.hasHorizontalAccuracy()) {
|
||||
Assert.assertEquals("horizontalAccuracy", expected.getHorizontalAccuracy().toM(), actual.getHorizontalAccuracy().toM(), delta);
|
||||
}
|
||||
Assert.assertEquals("has verticalAccuracy", expected.hasVerticalAccuracy(), actual.hasVerticalAccuracy());
|
||||
if (expected.hasVerticalAccuracy()) {
|
||||
Assert.assertEquals("verticalAccuracy", expected.getVerticalAccuracy().toM(), actual.getVerticalAccuracy().toM(), delta);
|
||||
Assert.assertEquals("has sensorDistance", expected.sensorDistance() != null, actual.sensorDistance() != null);
|
||||
if (expected.sensorDistance() != null) {
|
||||
Assert.assertEquals("sensorDistance", expected.sensorDistance().toM(), actual.sensorDistance().toM(), delta);
|
||||
}
|
||||
|
||||
Assert.assertEquals("has sensorDistance", expected.hasSensorDistance(), actual.hasSensorDistance());
|
||||
if (expected.hasSensorDistance()) {
|
||||
Assert.assertEquals("sensorDistance", expected.getSensorDistance().toM(), actual.getSensorDistance().toM(), delta);
|
||||
}
|
||||
Assert.assertEquals("heartRate", expected.heartRate(), actual.heartRate());
|
||||
|
||||
Assert.assertEquals("has heartrate", expected.hasHeartRate(), actual.hasHeartRate());
|
||||
if (expected.hasHeartRate()) {
|
||||
Assert.assertEquals("heartrate", expected.getHeartRate(), actual.getHeartRate());
|
||||
}
|
||||
Assert.assertEquals("power", expected.power(), actual.power());
|
||||
|
||||
Assert.assertEquals("has power", expected.hasPower(), actual.hasPower());
|
||||
if (expected.hasPower()) {
|
||||
Assert.assertEquals("power", expected.getPower(), actual.getPower());
|
||||
}
|
||||
|
||||
Assert.assertEquals("has cadence", expected.hasCadence(), actual.hasCadence());
|
||||
if (expected.hasCadence()) {
|
||||
Assert.assertEquals("cadence", expected.getCadence(), actual.getCadence());
|
||||
}
|
||||
Assert.assertEquals("cadence", expected.cadence(), actual.cadence());
|
||||
}
|
||||
|
||||
public void assertEquals(List<TrackPoint> expected, List<TrackPoint> actual) {
|
||||
|
||||
+283
-81
@@ -35,6 +35,7 @@ import de.dennisguse.opentracks.TestUtil;
|
||||
import de.dennisguse.opentracks.content.data.TestDataUtil;
|
||||
import de.dennisguse.opentracks.data.ContentProviderUtils;
|
||||
import de.dennisguse.opentracks.data.models.AltitudeGainLoss;
|
||||
import de.dennisguse.opentracks.data.models.Cadence;
|
||||
import de.dennisguse.opentracks.data.models.Distance;
|
||||
import de.dennisguse.opentracks.data.models.HeartRate;
|
||||
import de.dennisguse.opentracks.data.models.Position;
|
||||
@@ -126,8 +127,15 @@ public class TrackRecordingServiceRecordingTest {
|
||||
|
||||
new TrackPointAssert().assertEquals(List.of(
|
||||
new TrackPoint(TrackPoint.Type.SEGMENT_START_MANUAL, Instant.parse(startTime)),
|
||||
new TrackPoint(TrackPoint.Type.SEGMENT_END_MANUAL, Instant.parse(stopTime))
|
||||
.setAltitudeGainLoss(0, 0)
|
||||
new TrackPoint(
|
||||
null,
|
||||
TrackPoint.Type.SEGMENT_END_MANUAL,
|
||||
Position.of(Instant.parse(stopTime)),
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
new AltitudeGainLoss(0, 0))
|
||||
), TestDataUtil.getTrackPoints(contentProviderUtils, trackId));
|
||||
}
|
||||
|
||||
@@ -201,8 +209,15 @@ public class TrackRecordingServiceRecordingTest {
|
||||
|
||||
new TrackPointAssert().assertEquals(List.of(
|
||||
new TrackPoint(TrackPoint.Type.SEGMENT_START_MANUAL, Instant.parse(startTime)),
|
||||
new TrackPoint(TrackPoint.Type.SEGMENT_END_MANUAL, Instant.parse(pauseTime))
|
||||
.setAltitudeGainLoss(0, 0)
|
||||
new TrackPoint(
|
||||
null,
|
||||
TrackPoint.Type.SEGMENT_END_MANUAL,
|
||||
Position.of(Instant.parse(pauseTime)),
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
new AltitudeGainLoss(0, 0))
|
||||
), TestDataUtil.getTrackPoints(contentProviderUtils, trackId));
|
||||
|
||||
//when
|
||||
@@ -216,8 +231,16 @@ public class TrackRecordingServiceRecordingTest {
|
||||
|
||||
new TrackPointAssert().assertEquals(List.of(
|
||||
new TrackPoint(TrackPoint.Type.SEGMENT_START_MANUAL, Instant.parse(startTime)),
|
||||
new TrackPoint(TrackPoint.Type.SEGMENT_END_MANUAL, Instant.parse(pauseTime))
|
||||
.setAltitudeGainLoss(0, 0),
|
||||
new TrackPoint(
|
||||
null,
|
||||
TrackPoint.Type.SEGMENT_END_MANUAL,
|
||||
Position.of(Instant.parse(pauseTime)),
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
new AltitudeGainLoss(0, 0)
|
||||
),
|
||||
new TrackPoint(TrackPoint.Type.SEGMENT_START_MANUAL, Instant.parse(resumeTime))
|
||||
), TestDataUtil.getTrackPoints(contentProviderUtils, trackId));
|
||||
}
|
||||
@@ -249,8 +272,15 @@ public class TrackRecordingServiceRecordingTest {
|
||||
List<TrackPoint> trackPoints = TestDataUtil.getTrackPoints(contentProviderUtils, trackId);
|
||||
new TrackPointAssert().assertEquals(List.of(
|
||||
new TrackPoint(TrackPoint.Type.SEGMENT_START_MANUAL, Instant.parse(starTime)),
|
||||
new TrackPoint(TrackPoint.Type.SEGMENT_END_MANUAL, Instant.parse(pauseTime))
|
||||
.setAltitudeGainLoss(0, 0)
|
||||
new TrackPoint(
|
||||
null,
|
||||
TrackPoint.Type.SEGMENT_END_MANUAL,
|
||||
Position.of(Instant.parse(pauseTime)),
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
new AltitudeGainLoss(0, 0))
|
||||
), trackPoints);
|
||||
}
|
||||
|
||||
@@ -278,8 +308,16 @@ public class TrackRecordingServiceRecordingTest {
|
||||
// then
|
||||
new TrackPointAssert().assertEquals(List.of(
|
||||
new TrackPoint(TrackPoint.Type.SEGMENT_START_MANUAL, Instant.parse(startTime)),
|
||||
new TrackPoint(TrackPoint.Type.SEGMENT_END_MANUAL, Instant.parse(stopTime))
|
||||
.setAltitudeGainLoss(0, 0),
|
||||
new TrackPoint(
|
||||
null,
|
||||
TrackPoint.Type.SEGMENT_END_MANUAL,
|
||||
Position.of(Instant.parse(stopTime)),
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
new AltitudeGainLoss(0, 0)
|
||||
),
|
||||
new TrackPoint(TrackPoint.Type.SEGMENT_START_MANUAL, Instant.parse(resumeTime))
|
||||
), TestDataUtil.getTrackPoints(contentProviderUtils, trackId));
|
||||
}
|
||||
@@ -296,7 +334,7 @@ public class TrackRecordingServiceRecordingTest {
|
||||
mockAltitudeChange(trackPointCreator, 0);
|
||||
|
||||
SensorManager sensorManager = trackPointCreator.getSensorManager();
|
||||
sensorManager.sensorDataSet.add(new AggregatorHeartRate("", ""));
|
||||
sensorManager.sensorDataAggregator.add(new AggregatorHeartRate("", ""));
|
||||
// when
|
||||
String sensor1 = "2020-02-02T02:02:03Z";
|
||||
trackPointCreator.setClock(sensor1);
|
||||
@@ -314,12 +352,26 @@ public class TrackRecordingServiceRecordingTest {
|
||||
// then
|
||||
new TrackPointAssert().assertEquals(List.of(
|
||||
new TrackPoint(TrackPoint.Type.SEGMENT_START_MANUAL, Instant.parse(startTime)),
|
||||
new TrackPoint(TrackPoint.Type.TRACKPOINT, Instant.parse(sensor3))
|
||||
.setAltitudeGainLoss(0, 0)
|
||||
.setHeartRate(HeartRate.of(7)),
|
||||
new TrackPoint(TrackPoint.Type.SEGMENT_END_MANUAL, Instant.parse(stopTime))
|
||||
.setAltitudeGainLoss(0, 0)
|
||||
.setHeartRate(HeartRate.of(7))
|
||||
new TrackPoint(
|
||||
null,
|
||||
TrackPoint.Type.TRACKPOINT,
|
||||
Position.of(Instant.parse(sensor3)),
|
||||
null,
|
||||
HeartRate.of(7),
|
||||
null,
|
||||
null,
|
||||
new AltitudeGainLoss(0, 0)
|
||||
),
|
||||
new TrackPoint(
|
||||
null,
|
||||
TrackPoint.Type.SEGMENT_END_MANUAL,
|
||||
Position.of(Instant.parse(stopTime)),
|
||||
null,
|
||||
HeartRate.of(7),
|
||||
null,
|
||||
null,
|
||||
new AltitudeGainLoss(0, 0)
|
||||
)
|
||||
), TestDataUtil.getTrackPoints(contentProviderUtils, trackId));
|
||||
}
|
||||
|
||||
@@ -369,32 +421,61 @@ public class TrackRecordingServiceRecordingTest {
|
||||
|
||||
new TrackPointAssert().assertEquals(List.of(
|
||||
new TrackPoint(TrackPoint.Type.SEGMENT_START_MANUAL, Instant.parse(startTime)),
|
||||
new TrackPoint(TrackPoint.Type.TRACKPOINT,
|
||||
new TrackPoint(
|
||||
null,
|
||||
TrackPoint.Type.TRACKPOINT,
|
||||
new Position(
|
||||
Instant.parse(gps1),
|
||||
45d, 35d, Distance.of(1),
|
||||
null, null,
|
||||
null,
|
||||
Speed.of(15)))
|
||||
.setAltitudeGainLoss(0, 0),
|
||||
new TrackPoint(TrackPoint.Type.TRACKPOINT,
|
||||
Speed.of(15)),
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
new AltitudeGainLoss(0, 0)
|
||||
),
|
||||
new TrackPoint(
|
||||
null,
|
||||
TrackPoint.Type.TRACKPOINT,
|
||||
new Position(
|
||||
Instant.parse(gps2),
|
||||
45.001, 35d, Distance.of(1),
|
||||
null, null,
|
||||
null,
|
||||
Speed.of(15)))
|
||||
.setAltitudeGainLoss(0, 0),
|
||||
new TrackPoint(TrackPoint.Type.TRACKPOINT,
|
||||
Speed.of(15)),
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
new AltitudeGainLoss(0, 0)
|
||||
),
|
||||
new TrackPoint(
|
||||
null,
|
||||
TrackPoint.Type.TRACKPOINT,
|
||||
new Position(
|
||||
Instant.parse(gps3),
|
||||
45.001, 35d, Distance.of(1),
|
||||
null, null,
|
||||
null,
|
||||
Speed.of(15)))
|
||||
.setAltitudeGainLoss(0, 0),
|
||||
new TrackPoint(TrackPoint.Type.SEGMENT_END_MANUAL, Instant.parse(stopTime))
|
||||
.setAltitudeGainLoss(0, 0)
|
||||
Speed.of(15)),
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
new AltitudeGainLoss(0, 0)
|
||||
),
|
||||
new TrackPoint(
|
||||
null,
|
||||
TrackPoint.Type.SEGMENT_END_MANUAL,
|
||||
Position.of(Instant.parse(stopTime)),
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
new AltitudeGainLoss(0, 0)
|
||||
)
|
||||
), TestDataUtil.getTrackPoints(contentProviderUtils, trackId));
|
||||
}
|
||||
|
||||
@@ -441,30 +522,51 @@ public class TrackRecordingServiceRecordingTest {
|
||||
|
||||
new TrackPointAssert().assertEquals(List.of(
|
||||
new TrackPoint(TrackPoint.Type.SEGMENT_START_MANUAL, Instant.parse(startTime)),
|
||||
new TrackPoint(TrackPoint.Type.TRACKPOINT,
|
||||
new TrackPoint(
|
||||
null,
|
||||
TrackPoint.Type.TRACKPOINT,
|
||||
new Position(
|
||||
Instant.parse(gps1),
|
||||
45d, 35d, Distance.of(1),
|
||||
null, null,
|
||||
null,
|
||||
Speed.of(15)))
|
||||
.setAltitudeGainLoss(0, 0),
|
||||
new TrackPoint(TrackPoint.Type.TRACKPOINT,
|
||||
Speed.of(15)),
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
new AltitudeGainLoss(0, 0)
|
||||
),
|
||||
new TrackPoint(
|
||||
null,
|
||||
TrackPoint.Type.TRACKPOINT,
|
||||
new Position(
|
||||
Instant.parse(gps3),
|
||||
45.00002, 35d, Distance.of(1),
|
||||
null, null,
|
||||
null,
|
||||
Speed.of(15)))
|
||||
.setAltitudeGainLoss(0, 0),
|
||||
new TrackPoint(TrackPoint.Type.SEGMENT_END_MANUAL,
|
||||
Speed.of(15)),
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
new AltitudeGainLoss(0, 0)
|
||||
),
|
||||
new TrackPoint(
|
||||
null,
|
||||
TrackPoint.Type.SEGMENT_END_MANUAL,
|
||||
new Position(
|
||||
Instant.parse(stopTime),
|
||||
45.00002, 35d, Distance.of(1),
|
||||
null, null,
|
||||
null,
|
||||
Speed.of(15)))
|
||||
.setAltitudeGainLoss(0, 0)
|
||||
Speed.of(15)),
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
new AltitudeGainLoss(0, 0)
|
||||
)
|
||||
), TestDataUtil.getTrackPoints(contentProviderUtils, trackId));
|
||||
}
|
||||
|
||||
@@ -499,24 +601,44 @@ public class TrackRecordingServiceRecordingTest {
|
||||
// then
|
||||
new TrackPointAssert().assertEquals(List.of(
|
||||
new TrackPoint(TrackPoint.Type.SEGMENT_START_MANUAL, Instant.parse(startTime)),
|
||||
new TrackPoint(TrackPoint.Type.TRACKPOINT,
|
||||
new TrackPoint(
|
||||
null,
|
||||
TrackPoint.Type.TRACKPOINT,
|
||||
new Position(
|
||||
Instant.parse(gps1),
|
||||
45d, 35d, Distance.of(1),
|
||||
null, null,
|
||||
null,
|
||||
Speed.of(15)))
|
||||
.setAltitudeGainLoss(0, 0),
|
||||
new TrackPoint(TrackPoint.Type.TRACKPOINT,
|
||||
Speed.of(15)),
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
new AltitudeGainLoss(0, 0)
|
||||
),
|
||||
new TrackPoint(
|
||||
null,
|
||||
TrackPoint.Type.TRACKPOINT,
|
||||
new Position(
|
||||
Instant.parse(gps3),
|
||||
45d, 35d, Distance.of(1),
|
||||
null, null,
|
||||
null,
|
||||
Speed.of(15)))
|
||||
.setAltitudeGainLoss(0, 0),
|
||||
new TrackPoint(TrackPoint.Type.SEGMENT_END_MANUAL, Instant.parse(stopTime))
|
||||
.setAltitudeGainLoss(0, 0)
|
||||
Speed.of(15)),
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
new AltitudeGainLoss(0, 0)),
|
||||
new TrackPoint(
|
||||
null,
|
||||
TrackPoint.Type.SEGMENT_END_MANUAL,
|
||||
Position.of(Instant.parse(stopTime)),
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
new AltitudeGainLoss(0, 0))
|
||||
), TestDataUtil.getTrackPoints(contentProviderUtils, trackId));
|
||||
}
|
||||
|
||||
@@ -559,8 +681,15 @@ public class TrackRecordingServiceRecordingTest {
|
||||
// then
|
||||
new TrackPointAssert().assertEquals(List.of(
|
||||
new TrackPoint(TrackPoint.Type.SEGMENT_START_MANUAL, Instant.parse(startTime)),
|
||||
new TrackPoint(TrackPoint.Type.SEGMENT_END_MANUAL, Instant.parse(stopTime))
|
||||
.setAltitudeGainLoss(0, 0)
|
||||
new TrackPoint(
|
||||
null,
|
||||
TrackPoint.Type.SEGMENT_END_MANUAL,
|
||||
Position.of(Instant.parse(stopTime)),
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
new AltitudeGainLoss(0, 0))
|
||||
), TestDataUtil.getTrackPoints(contentProviderUtils, trackId));
|
||||
}
|
||||
|
||||
@@ -603,24 +732,45 @@ public class TrackRecordingServiceRecordingTest {
|
||||
// then
|
||||
new TrackPointAssert().assertEquals(List.of(
|
||||
new TrackPoint(TrackPoint.Type.SEGMENT_START_MANUAL, Instant.parse(startTime)),
|
||||
new TrackPoint(TrackPoint.Type.TRACKPOINT,
|
||||
new TrackPoint(
|
||||
null,
|
||||
TrackPoint.Type.TRACKPOINT,
|
||||
new Position(
|
||||
Instant.parse(gps1),
|
||||
45d, 35d, Distance.of(1),
|
||||
null, null,
|
||||
null,
|
||||
Speed.of(15)))
|
||||
.setAltitudeGainLoss(0, 0),
|
||||
new TrackPoint(TrackPoint.Type.SEGMENT_START_AUTOMATIC,
|
||||
Speed.of(15)),
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
new AltitudeGainLoss(0, 0)
|
||||
),
|
||||
new TrackPoint(
|
||||
null,
|
||||
TrackPoint.Type.SEGMENT_START_AUTOMATIC,
|
||||
new Position(
|
||||
Instant.parse(gps2),
|
||||
45.1, 35d, Distance.of(1),
|
||||
null, null,
|
||||
null,
|
||||
Speed.of(15)))
|
||||
.setAltitudeGainLoss(0, 0),
|
||||
new TrackPoint(TrackPoint.Type.SEGMENT_END_MANUAL, Instant.parse(stopTime))
|
||||
.setAltitudeGainLoss(0, 0)
|
||||
Speed.of(15)),
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
new AltitudeGainLoss(0, 0)
|
||||
),
|
||||
new TrackPoint(
|
||||
null,
|
||||
TrackPoint.Type.SEGMENT_END_MANUAL,
|
||||
Position.of(Instant.parse(stopTime)),
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
new AltitudeGainLoss(0, 0))
|
||||
), TestDataUtil.getTrackPoints(contentProviderUtils, trackId));
|
||||
}
|
||||
|
||||
@@ -638,18 +788,18 @@ public class TrackRecordingServiceRecordingTest {
|
||||
Track.Id trackId = service.startNewTrack();
|
||||
|
||||
SensorManager sensorManager = trackPointCreator.getSensorManager();
|
||||
sensorManager.sensorDataSet.add(new AggregatorRunning("", ""));
|
||||
sensorManager.sensorDataSet.barometer = null;
|
||||
sensorManager.sensorDataAggregator.add(new AggregatorRunning("", ""));
|
||||
sensorManager.sensorDataAggregator.barometer = null;
|
||||
|
||||
// when
|
||||
String sensor1 = "2020-02-02T02:02:03Z";
|
||||
trackPointCreator.setClock(sensor1);
|
||||
sensorManager.onChanged(new Raw<>(sensor1, new BluetoothHandlerRunningSpeedAndCadence.Data(Speed.of(5), null, Distance.ZERO))); //Should be ignored
|
||||
sensorManager.onChanged(new Raw<>(sensor1, new BluetoothHandlerRunningSpeedAndCadence.Data(Speed.of(5), Cadence.of(1), Distance.ZERO))); //Should be ignored
|
||||
|
||||
// when
|
||||
String sensor2 = "2020-02-02T02:02:04Z";
|
||||
trackPointCreator.setClock(sensor2);
|
||||
sensorManager.onChanged(new Raw<>(sensor2, new BluetoothHandlerRunningSpeedAndCadence.Data(Speed.of(5), null, Distance.of(2))));
|
||||
sensorManager.onChanged(new Raw<>(sensor2, new BluetoothHandlerRunningSpeedAndCadence.Data(Speed.of(5), Cadence.of(2), Distance.of(2))));
|
||||
|
||||
// when
|
||||
String gps1 = "2020-02-02T02:02:05Z";
|
||||
@@ -658,12 +808,12 @@ public class TrackRecordingServiceRecordingTest {
|
||||
// when
|
||||
String sensor3 = "2020-02-02T02:02:06Z";
|
||||
trackPointCreator.setClock(sensor3);
|
||||
sensorManager.onChanged(new Raw<>(sensor3, new BluetoothHandlerRunningSpeedAndCadence.Data(Speed.of(5), null, Distance.of(12))));
|
||||
sensorManager.onChanged(new Raw<>(sensor3, new BluetoothHandlerRunningSpeedAndCadence.Data(Speed.of(5), Cadence.of(3), Distance.of(12))));
|
||||
|
||||
// when
|
||||
String sensor4 = "2020-02-02T02:02:07Z";
|
||||
trackPointCreator.setClock(sensor4);
|
||||
sensorManager.onChanged(new Raw<>(sensor4, new BluetoothHandlerRunningSpeedAndCadence.Data(Speed.of(5), null, Distance.of(14)))); //Should be ignored
|
||||
sensorManager.onChanged(new Raw<>(sensor4, new BluetoothHandlerRunningSpeedAndCadence.Data(Speed.of(5), Cadence.of(4), Distance.of(14)))); //Should be ignored
|
||||
|
||||
// when
|
||||
String gps2 = "2020-02-02T02:02:08Z";
|
||||
@@ -672,7 +822,7 @@ public class TrackRecordingServiceRecordingTest {
|
||||
// when
|
||||
String sensor5 = "2020-02-02T02:02:10Z";
|
||||
trackPointCreator.setClock(sensor5);
|
||||
sensorManager.onChanged(new Raw<>(sensor5, new BluetoothHandlerRunningSpeedAndCadence.Data(Speed.of(5), null, Distance.of(16)))); //Should be ignored
|
||||
sensorManager.onChanged(new Raw<>(sensor5, new BluetoothHandlerRunningSpeedAndCadence.Data(Speed.of(5), Cadence.of(5), Distance.of(16)))); //Should be ignored
|
||||
|
||||
// when
|
||||
String gps3 = "2020-02-02T02:02:12Z";
|
||||
@@ -691,49 +841,101 @@ public class TrackRecordingServiceRecordingTest {
|
||||
// then
|
||||
new TrackPointAssert().assertEquals(List.of(
|
||||
new TrackPoint(TrackPoint.Type.SEGMENT_START_MANUAL, Instant.parse(startTime)),
|
||||
new TrackPoint(TrackPoint.Type.TRACKPOINT, Instant.parse(sensor2)) //First moving TrackPoint: store as the time might be interesting.
|
||||
.setSpeed(Speed.of(5))
|
||||
.setSensorDistance(Distance.of(2)),
|
||||
new TrackPoint(TrackPoint.Type.TRACKPOINT,
|
||||
new TrackPoint( //First moving TrackPoint: stored as the time might be interesting.
|
||||
null,
|
||||
TrackPoint.Type.TRACKPOINT,
|
||||
new Position(
|
||||
Instant.parse(sensor2),
|
||||
null, null, null,
|
||||
null, null,
|
||||
null,
|
||||
Speed.of(5)),
|
||||
Distance.of(2),
|
||||
null,
|
||||
Cadence.of(2),
|
||||
null,
|
||||
null
|
||||
),
|
||||
new TrackPoint(
|
||||
null,
|
||||
TrackPoint.Type.TRACKPOINT,
|
||||
new Position(
|
||||
Instant.parse(gps1),
|
||||
45d, 35d, Distance.of(1),
|
||||
null, null,
|
||||
null,
|
||||
Speed.of(5)))
|
||||
.setSensorDistance(Distance.ZERO),
|
||||
new TrackPoint(TrackPoint.Type.TRACKPOINT, Instant.parse(sensor3))
|
||||
.setSpeed(Speed.of(5))
|
||||
.setSensorDistance(Distance.of(10)),
|
||||
new TrackPoint(TrackPoint.Type.TRACKPOINT,
|
||||
Speed.of(5)),
|
||||
Distance.ZERO,
|
||||
null,
|
||||
Cadence.of(2),
|
||||
null,
|
||||
null
|
||||
),
|
||||
new TrackPoint(
|
||||
null,
|
||||
TrackPoint.Type.TRACKPOINT,
|
||||
new Position(
|
||||
Instant.parse(sensor3),
|
||||
null, null, null,
|
||||
null, null,
|
||||
null,
|
||||
Speed.of(5)),
|
||||
Distance.of(10),
|
||||
null,
|
||||
Cadence.of(3),
|
||||
null,
|
||||
null
|
||||
),
|
||||
new TrackPoint(
|
||||
null,
|
||||
TrackPoint.Type.TRACKPOINT,
|
||||
new Position(
|
||||
Instant.parse(gps3),
|
||||
45.001, 35d, Distance.of(1),
|
||||
null, null,
|
||||
null,
|
||||
Speed.of(5)))
|
||||
.setSensorDistance(Distance.of(4.0)),
|
||||
new TrackPoint(TrackPoint.Type.TRACKPOINT,
|
||||
Speed.of(5)),
|
||||
Distance.of(4.0),
|
||||
null,
|
||||
Cadence.of(5),
|
||||
null,
|
||||
null
|
||||
),
|
||||
new TrackPoint(
|
||||
null,
|
||||
TrackPoint.Type.TRACKPOINT,
|
||||
new Position(
|
||||
Instant.parse(gps4),
|
||||
45.001, 35d, Distance.of(1),
|
||||
null, null,
|
||||
null,
|
||||
Speed.of(5)))
|
||||
.setSensorDistance(Distance.ZERO),
|
||||
new TrackPoint(TrackPoint.Type.SEGMENT_END_MANUAL,
|
||||
Speed.of(5)),
|
||||
Distance.ZERO,
|
||||
null,
|
||||
Cadence.of(5),
|
||||
null,
|
||||
null
|
||||
),
|
||||
new TrackPoint(
|
||||
null,
|
||||
TrackPoint.Type.SEGMENT_END_MANUAL,
|
||||
new Position(
|
||||
Instant.parse(stopTime),
|
||||
45.001, 35d, Distance.of(1),
|
||||
null, null,
|
||||
null,
|
||||
Speed.ZERO)) //Sensor data is now outdated, but we do not fall back to GPS.
|
||||
.setSensorDistance(Distance.ZERO)
|
||||
Speed.ZERO), //Sensor data is now outdated, but we do not fall back to GPS.
|
||||
Distance.ZERO,
|
||||
null,
|
||||
Cadence.of(0), //TODO This could be null, right?
|
||||
null,
|
||||
null
|
||||
)
|
||||
), TestDataUtil.getTrackPoints(contentProviderUtils, trackId));
|
||||
}
|
||||
|
||||
private void mockAltitudeChange(TrackPointCreator trackPointCreator, float altitudeGain) {
|
||||
trackPointCreator.getSensorManager().sensorDataSet.barometer = new AggregatorBarometer("", "") {
|
||||
trackPointCreator.getSensorManager().sensorDataAggregator.barometer = new AggregatorBarometer("", "") {
|
||||
@Override
|
||||
public boolean hasReceivedData() {
|
||||
return true;
|
||||
|
||||
+2
-1
@@ -45,6 +45,7 @@ import java.util.List;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import java.util.concurrent.TimeoutException;
|
||||
|
||||
import de.dennisguse.opentracks.TestUtil;
|
||||
import de.dennisguse.opentracks.data.ContentProviderUtils;
|
||||
import de.dennisguse.opentracks.data.models.Track;
|
||||
import de.dennisguse.opentracks.sensors.GpsStatusValue;
|
||||
@@ -60,7 +61,7 @@ public class TrackRecordingServiceStateMachineTest {
|
||||
public final ServiceTestRule mServiceRule = ServiceTestRule.withTimeout(5, TimeUnit.SECONDS);
|
||||
|
||||
@Rule
|
||||
public GrantPermissionRule mRuntimePermissionRule = GrantPermissionRule.grant(android.Manifest.permission.ACCESS_FINE_LOCATION);
|
||||
public GrantPermissionRule mRuntimePermissionRule = TestUtil.createGrantPermissionRule();
|
||||
|
||||
private final Context context = ApplicationProvider.getApplicationContext();
|
||||
private ContentProviderUtils contentProviderUtils;
|
||||
|
||||
+146
-34
@@ -15,7 +15,6 @@ import org.junit.Before;
|
||||
import org.junit.Rule;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
import org.mockito.Mockito;
|
||||
|
||||
import java.time.Duration;
|
||||
import java.time.Instant;
|
||||
@@ -34,10 +33,11 @@ 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.models.TrackPoint;
|
||||
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.PreferencesUtils;
|
||||
import de.dennisguse.opentracks.settings.UnitSystem;
|
||||
import de.dennisguse.opentracks.data.statistics.SensorStatistics;
|
||||
import de.dennisguse.opentracks.ui.intervals.IntervalStatisticsUpdater;
|
||||
|
||||
//Due to DateTimeFormatter using NBSP, these tests only work in SDK34+
|
||||
@@ -84,8 +84,15 @@ public class VoiceAnnouncementUtilsTest {
|
||||
null
|
||||
));
|
||||
|
||||
SensorDataSet dataSet = Mockito.mock(SensorDataSet.class);
|
||||
Mockito.when(dataSet.getHeartRate()).thenReturn(new Pair<>(HeartRate.of(60), "unused"));
|
||||
SensorDataSet dataSet = new SensorDataSet(
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
new SensorData<>(HeartRate.of(60), "unused"),
|
||||
null,
|
||||
null,
|
||||
null
|
||||
);
|
||||
|
||||
// when
|
||||
String announcement = VoiceAnnouncementUtils.createStatistics(context, track, dataSet, UnitSystem.METRIC, true, null, null).toString();
|
||||
@@ -117,8 +124,15 @@ public class VoiceAnnouncementUtilsTest {
|
||||
null
|
||||
));
|
||||
|
||||
SensorDataSet dataSet = Mockito.mock(SensorDataSet.class);
|
||||
Mockito.when(dataSet.getHeartRate()).thenReturn(new Pair<>(HeartRate.of(60), "unused"));
|
||||
SensorDataSet dataSet = new SensorDataSet(
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
new SensorData<>(HeartRate.of(60), "unused"),
|
||||
null,
|
||||
null,
|
||||
null
|
||||
);
|
||||
|
||||
// when
|
||||
String announcement = VoiceAnnouncementUtils.createStatistics(context, track, dataSet, UnitSystem.METRIC, true, null, null).toString();
|
||||
@@ -150,8 +164,15 @@ public class VoiceAnnouncementUtilsTest {
|
||||
null
|
||||
));
|
||||
|
||||
SensorDataSet dataSet = Mockito.mock(SensorDataSet.class);
|
||||
Mockito.when(dataSet.getHeartRate()).thenReturn(new Pair<>(HeartRate.of(60), "unused"));
|
||||
SensorDataSet dataSet = new SensorDataSet(
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
new SensorData<>(HeartRate.of(60), "unused"),
|
||||
null,
|
||||
null,
|
||||
null
|
||||
);
|
||||
|
||||
// when
|
||||
String announcement = VoiceAnnouncementUtils.createStatistics(context, track, dataSet, UnitSystem.METRIC, true, null, null).toString();
|
||||
@@ -183,8 +204,15 @@ public class VoiceAnnouncementUtilsTest {
|
||||
null
|
||||
));
|
||||
|
||||
SensorDataSet dataSet = Mockito.mock(SensorDataSet.class);
|
||||
Mockito.when(dataSet.getHeartRate()).thenReturn(new Pair<>(HeartRate.of(60), "unused"));
|
||||
SensorDataSet dataSet = new SensorDataSet(
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
new SensorData<>(HeartRate.of(60), "unused"),
|
||||
null,
|
||||
null,
|
||||
null
|
||||
);
|
||||
|
||||
// when
|
||||
String announcement = VoiceAnnouncementUtils.createStatistics(context, track, dataSet, UnitSystem.METRIC, true, null, null).toString();
|
||||
@@ -198,8 +226,15 @@ public class VoiceAnnouncementUtilsTest {
|
||||
// given
|
||||
Pair<Track, Statistics> data = buildTrackWithTrackPoints();
|
||||
|
||||
SensorDataSet dataSet = Mockito.mock(SensorDataSet.class);
|
||||
Mockito.when(dataSet.getHeartRate()).thenReturn(new Pair<>(HeartRate.of(60), "unused"));
|
||||
SensorDataSet dataSet = new SensorDataSet(
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
new SensorData<>(HeartRate.of(60), "unused"),
|
||||
null,
|
||||
null,
|
||||
null
|
||||
);
|
||||
|
||||
// when
|
||||
String announcement = VoiceAnnouncementUtils.createStatistics(context, data.first, dataSet, UnitSystem.METRIC, true, data.second, null).toString();
|
||||
@@ -231,8 +266,15 @@ public class VoiceAnnouncementUtilsTest {
|
||||
null
|
||||
));
|
||||
|
||||
SensorDataSet dataSet = Mockito.mock(SensorDataSet.class);
|
||||
Mockito.when(dataSet.getHeartRate()).thenReturn(new Pair<>(HeartRate.of(60), "unused"));
|
||||
SensorDataSet dataSet = new SensorDataSet(
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
new SensorData<>(HeartRate.of(60), "unused"),
|
||||
null,
|
||||
null,
|
||||
null
|
||||
);
|
||||
|
||||
// when
|
||||
String announcement = VoiceAnnouncementUtils.createStatistics(context, track, dataSet, UnitSystem.METRIC, false, null, null).toString();
|
||||
@@ -246,8 +288,15 @@ public class VoiceAnnouncementUtilsTest {
|
||||
// given
|
||||
Pair<Track, Statistics> data = buildTrackWithTrackPoints();
|
||||
|
||||
SensorDataSet dataSet = Mockito.mock(SensorDataSet.class);
|
||||
Mockito.when(dataSet.getHeartRate()).thenReturn(new Pair<>(HeartRate.of(60), "unused"));
|
||||
SensorDataSet dataSet = new SensorDataSet(
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
new SensorData<>(HeartRate.of(60), "unused"),
|
||||
null,
|
||||
null,
|
||||
null
|
||||
);
|
||||
|
||||
// when
|
||||
String announcement = VoiceAnnouncementUtils.createStatistics(context, data.first, dataSet, UnitSystem.METRIC, false, data.second, null).toString();
|
||||
@@ -279,8 +328,15 @@ public class VoiceAnnouncementUtilsTest {
|
||||
null
|
||||
));
|
||||
|
||||
SensorDataSet dataSet = Mockito.mock(SensorDataSet.class);
|
||||
Mockito.when(dataSet.getHeartRate()).thenReturn(new Pair<>(HeartRate.of(60), "unused"));
|
||||
SensorDataSet dataSet = new SensorDataSet(
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
new SensorData<>(HeartRate.of(60), "unused"),
|
||||
null,
|
||||
null,
|
||||
null
|
||||
);
|
||||
|
||||
// when
|
||||
String announcement = VoiceAnnouncementUtils.createStatistics(context, track, dataSet, UnitSystem.IMPERIAL_FEET, true, null, null).toString();
|
||||
@@ -312,8 +368,15 @@ public class VoiceAnnouncementUtilsTest {
|
||||
null
|
||||
));
|
||||
|
||||
SensorDataSet dataSet = Mockito.mock(SensorDataSet.class);
|
||||
Mockito.when(dataSet.getHeartRate()).thenReturn(new Pair<>(HeartRate.of(60), "unused"));
|
||||
SensorDataSet dataSet = new SensorDataSet(
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
new SensorData<>(HeartRate.of(60), "unused"),
|
||||
null,
|
||||
null,
|
||||
null
|
||||
);
|
||||
|
||||
// when
|
||||
String announcement = VoiceAnnouncementUtils.createStatistics(context, track, dataSet, UnitSystem.IMPERIAL_FEET, true, null, null).toString();
|
||||
@@ -345,8 +408,15 @@ public class VoiceAnnouncementUtilsTest {
|
||||
null
|
||||
));
|
||||
|
||||
SensorDataSet dataSet = Mockito.mock(SensorDataSet.class);
|
||||
Mockito.when(dataSet.getHeartRate()).thenReturn(new Pair<>(HeartRate.of(60), "unused"));
|
||||
SensorDataSet dataSet = new SensorDataSet(
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
new SensorData<>(HeartRate.of(60), "unused"),
|
||||
null,
|
||||
null,
|
||||
null
|
||||
);
|
||||
|
||||
// when
|
||||
String announcement = VoiceAnnouncementUtils.createStatistics(context, track, dataSet, UnitSystem.IMPERIAL_METER, true, null, null).toString();
|
||||
@@ -378,8 +448,15 @@ public class VoiceAnnouncementUtilsTest {
|
||||
null
|
||||
));
|
||||
|
||||
SensorDataSet dataSet = Mockito.mock(SensorDataSet.class);
|
||||
Mockito.when(dataSet.getHeartRate()).thenReturn(new Pair<>(HeartRate.of(60), "unused"));
|
||||
SensorDataSet dataSet = new SensorDataSet(
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
new SensorData<>(HeartRate.of(60), "unused"),
|
||||
null,
|
||||
null,
|
||||
null
|
||||
);
|
||||
|
||||
// when
|
||||
String announcement = VoiceAnnouncementUtils.createStatistics(context, track, dataSet, UnitSystem.METRIC, true, null, null).toString();
|
||||
@@ -393,8 +470,15 @@ public class VoiceAnnouncementUtilsTest {
|
||||
// given
|
||||
Pair<Track, Statistics> data = buildTrackWithTrackPoints();
|
||||
|
||||
SensorDataSet dataSet = Mockito.mock(SensorDataSet.class);
|
||||
Mockito.when(dataSet.getHeartRate()).thenReturn(new Pair<>(HeartRate.of(60), "unused"));
|
||||
SensorDataSet dataSet = new SensorDataSet(
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
new SensorData<>(HeartRate.of(60), "unused"),
|
||||
null,
|
||||
null,
|
||||
null
|
||||
);
|
||||
|
||||
// when
|
||||
String announcement = VoiceAnnouncementUtils.createStatistics(context, data.first, dataSet, UnitSystem.IMPERIAL_FEET, true, data.second, null).toString();
|
||||
@@ -426,8 +510,15 @@ public class VoiceAnnouncementUtilsTest {
|
||||
null
|
||||
));
|
||||
|
||||
SensorDataSet dataSet = Mockito.mock(SensorDataSet.class);
|
||||
Mockito.when(dataSet.getHeartRate()).thenReturn(new Pair<>(HeartRate.of(60), "unused"));
|
||||
SensorDataSet dataSet = new SensorDataSet(
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
new SensorData<>(HeartRate.of(60), "unused"),
|
||||
null,
|
||||
null,
|
||||
null
|
||||
);
|
||||
|
||||
// when
|
||||
String announcement = VoiceAnnouncementUtils.createStatistics(context, track, dataSet, UnitSystem.IMPERIAL_FEET, false, null, null).toString();
|
||||
@@ -441,8 +532,15 @@ public class VoiceAnnouncementUtilsTest {
|
||||
// given
|
||||
Pair<Track, Statistics> data = buildTrackWithTrackPoints();
|
||||
|
||||
SensorDataSet dataSet = Mockito.mock(SensorDataSet.class);
|
||||
Mockito.when(dataSet.getHeartRate()).thenReturn(new Pair<>(HeartRate.of(60), "unused"));
|
||||
SensorDataSet dataSet = new SensorDataSet(
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
new SensorData<>(HeartRate.of(60), "unused"),
|
||||
null,
|
||||
null,
|
||||
null
|
||||
);
|
||||
|
||||
// when
|
||||
String announcement = VoiceAnnouncementUtils.createStatistics(context, data.first, dataSet, UnitSystem.IMPERIAL_FEET, false, data.second, null).toString();
|
||||
@@ -461,8 +559,15 @@ public class VoiceAnnouncementUtilsTest {
|
||||
|
||||
SensorStatistics sensorStatistics = new SensorStatistics(HeartRate.of(180f), HeartRate.of(180f), null, null, null, null);
|
||||
|
||||
SensorDataSet dataSet = Mockito.mock(SensorDataSet.class);
|
||||
Mockito.when(dataSet.getHeartRate()).thenReturn(new Pair<>(HeartRate.of(60), "unused"));
|
||||
SensorDataSet dataSet = new SensorDataSet(
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
new SensorData<>(HeartRate.of(60), "unused"),
|
||||
null,
|
||||
null,
|
||||
null
|
||||
);
|
||||
|
||||
// when
|
||||
String announcement = VoiceAnnouncementUtils.createStatistics(context, data.first, dataSet, UnitSystem.METRIC, true, data.second, sensorStatistics).toString();
|
||||
@@ -487,8 +592,15 @@ public class VoiceAnnouncementUtilsTest {
|
||||
|
||||
SensorStatistics sensorStatistics = new SensorStatistics(HeartRate.of(180f), HeartRate.of(180f), null, null, null, null);
|
||||
|
||||
SensorDataSet dataSet = Mockito.mock(SensorDataSet.class);
|
||||
Mockito.when(dataSet.getHeartRate()).thenReturn(new Pair<>(HeartRate.of(60), "unused"));
|
||||
SensorDataSet dataSet = new SensorDataSet(
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
new SensorData<>(HeartRate.of(60), "unused"),
|
||||
null,
|
||||
null,
|
||||
null
|
||||
);
|
||||
|
||||
// when
|
||||
String announcement = VoiceAnnouncementUtils.createStatistics(context, data.first, dataSet, UnitSystem.METRIC, true, data.second, sensorStatistics).toString();
|
||||
|
||||
+130
-67
@@ -89,8 +89,7 @@ public class TrackStatisticsUpdaterTest {
|
||||
// given
|
||||
TrackPoint tp1 = new TrackPoint(TrackPoint.Type.SEGMENT_START_MANUAL, Instant.ofEpochMilli(1000));
|
||||
TrackPoint tp2 = createTrackPoint(0, 0, Altitude.WGS84.of(5.0), Instant.ofEpochMilli(2000));
|
||||
TrackPoint tp3 = createTrackPoint(0.00001, 0, Altitude.WGS84.of(5.0), Instant.ofEpochMilli(3000));
|
||||
tp3.setSpeed(Speed.of(5f));
|
||||
TrackPoint tp3 = createTrackPoint(0.00001, 0, Altitude.WGS84.of(5.0), Instant.ofEpochMilli(3000), Speed.of(5f), null);
|
||||
|
||||
// when
|
||||
TrackStatisticsUpdater subject = new TrackStatisticsUpdater(List.of(tp1, tp2, tp3));
|
||||
@@ -103,15 +102,17 @@ public class TrackStatisticsUpdaterTest {
|
||||
public void addTrackPoint_distance_from_GPS_moving_and_sensor_moving() {
|
||||
// given
|
||||
TrackPoint tp1 = new TrackPoint(TrackPoint.Type.SEGMENT_START_MANUAL, Instant.ofEpochMilli(1000));
|
||||
TrackPoint tp2 = createTrackPoint(0, 0, Altitude.WGS84.of(5.0), Instant.ofEpochMilli(2000));
|
||||
tp2.setSpeed(Speed.of(5f));
|
||||
TrackPoint tp3 = createTrackPoint(0.001, 0, Altitude.WGS84.of(5.0), Instant.ofEpochMilli(3000));
|
||||
tp3.setSpeed(Speed.of(5f));
|
||||
TrackPoint tp4 = createTrackPoint(0.001, 0, Altitude.WGS84.of(5.0), Instant.ofEpochMilli(4000));
|
||||
tp4.setSpeed(Speed.of(5f));
|
||||
tp4.setSensorDistance(Distance.of(5f));
|
||||
TrackPoint tp5 = new TrackPoint(TrackPoint.Type.SEGMENT_END_MANUAL, Instant.ofEpochMilli(5000));
|
||||
tp5.setSensorDistance(Distance.of(10f));
|
||||
TrackPoint tp2 = createTrackPoint(0, 0, Altitude.WGS84.of(5.0), Instant.ofEpochMilli(2000), Speed.of(5f), null);
|
||||
TrackPoint tp3 = createTrackPoint(0.001, 0, Altitude.WGS84.of(5.0), Instant.ofEpochMilli(3000), Speed.of(5f), null);
|
||||
TrackPoint tp4 = createTrackPoint(0.001, 0, Altitude.WGS84.of(5.0), Instant.ofEpochMilli(4000), Speed.of(5f), Distance.of(5));
|
||||
TrackPoint tp5 = new TrackPoint(null,
|
||||
TrackPoint.Type.SEGMENT_END_MANUAL,
|
||||
Position.of(Instant.ofEpochMilli(5000)),
|
||||
Distance.of(10f),
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
null);
|
||||
|
||||
// when
|
||||
TrackStatisticsUpdater subject = new TrackStatisticsUpdater(List.of(tp1, tp2, tp3));
|
||||
@@ -131,13 +132,10 @@ public class TrackStatisticsUpdaterTest {
|
||||
public void addTrackPoint_distance_from_GPS_moving_and_sensor_disconnecting() {
|
||||
// given
|
||||
TrackPoint tp1 = new TrackPoint(TrackPoint.Type.SEGMENT_START_MANUAL, Instant.ofEpochMilli(1000));
|
||||
TrackPoint tp2 = createTrackPoint(0, 0, Altitude.WGS84.of(5.0), Instant.ofEpochMilli(2000));
|
||||
tp2.setSpeed(Speed.of(5f));
|
||||
TrackPoint tp3 = createTrackPoint(0.00001, 0, Altitude.WGS84.of(5.0), Instant.ofEpochMilli(3000));
|
||||
tp3.setSensorDistance(Distance.of(5f));
|
||||
tp3.setSpeed(Speed.of(5f));
|
||||
TrackPoint tp4 = createTrackPoint(0.0005, 0, Altitude.WGS84.of(5.0), Instant.ofEpochMilli(4000));
|
||||
tp4.setSpeed(Speed.of(5f));
|
||||
TrackPoint tp2 = createTrackPoint(0, 0, Altitude.WGS84.of(5.0), Instant.ofEpochMilli(2000), Speed.of(5f), null);
|
||||
|
||||
TrackPoint tp3 = createTrackPoint(0.00001, 0, Altitude.WGS84.of(5.0), Instant.ofEpochMilli(3000), Speed.of(5f), Distance.of(5));
|
||||
TrackPoint tp4 = createTrackPoint(0.0005, 0, Altitude.WGS84.of(5.0), Instant.ofEpochMilli(4000), Speed.of(5f), null);
|
||||
TrackPoint tp5 = new TrackPoint(TrackPoint.Type.SEGMENT_END_MANUAL, Instant.ofEpochMilli(5000));
|
||||
|
||||
// when
|
||||
@@ -158,10 +156,8 @@ public class TrackStatisticsUpdaterTest {
|
||||
public void addTrackPoint_maxSpeed_multiple_segments() {
|
||||
TrackStatisticsUpdater subject = new TrackStatisticsUpdater(List.of(
|
||||
new TrackPoint(TrackPoint.Type.SEGMENT_START_MANUAL, Instant.ofEpochSecond(0)),
|
||||
createTrackPoint(0, 0, Altitude.WGS84.of(0), Instant.ofEpochSecond(1))
|
||||
.setSpeed(Speed.of(2f)),
|
||||
createTrackPoint(0, 0, Altitude.WGS84.of(0), Instant.ofEpochSecond(2))
|
||||
.setSpeed(Speed.of(2f)),
|
||||
createTrackPoint(0, 0, Altitude.WGS84.of(0), Instant.ofEpochSecond(1), Speed.of(2), null),
|
||||
createTrackPoint(0, 0, Altitude.WGS84.of(0), Instant.ofEpochSecond(2), Speed.of(2), null),
|
||||
new TrackPoint(TrackPoint.Type.SEGMENT_END_MANUAL, Instant.ofEpochSecond(4))
|
||||
));
|
||||
assertEquals(Speed.of(2), subject.getTrackStatistics().maxSpeed());
|
||||
@@ -169,10 +165,8 @@ public class TrackStatisticsUpdaterTest {
|
||||
// when
|
||||
List.of(
|
||||
new TrackPoint(TrackPoint.Type.SEGMENT_START_MANUAL, Instant.ofEpochSecond(5)),
|
||||
createTrackPoint(0, 0, Altitude.WGS84.of(0), Instant.ofEpochSecond(6))
|
||||
.setSpeed(Speed.of(1f)),
|
||||
createTrackPoint(0, 0, Altitude.WGS84.of(0), Instant.ofEpochSecond(7))
|
||||
.setSpeed(Speed.of(1f)),
|
||||
createTrackPoint(0, 0, Altitude.WGS84.of(0), Instant.ofEpochSecond(6), Speed.of(1), null),
|
||||
createTrackPoint(0, 0, Altitude.WGS84.of(0), Instant.ofEpochSecond(7), Speed.of(1), null),
|
||||
new TrackPoint(TrackPoint.Type.SEGMENT_END_MANUAL, Instant.ofEpochSecond(8))
|
||||
).forEach(subject::addTrackPoint);
|
||||
|
||||
@@ -185,16 +179,30 @@ public class TrackStatisticsUpdaterTest {
|
||||
// when
|
||||
TrackStatisticsUpdater subject = new TrackStatisticsUpdater(List.of(
|
||||
new TrackPoint(TrackPoint.Type.SEGMENT_START_MANUAL, Instant.ofEpochSecond(0)),
|
||||
createTrackPoint(0, 0, Altitude.WGS84.of(0), Instant.ofEpochSecond(1))
|
||||
.setSpeed(Speed.of(2f)),
|
||||
createTrackPoint(0, 0, Altitude.WGS84.of(0), Instant.ofEpochSecond(2))
|
||||
.setSpeed(Speed.of(2f)),
|
||||
createTrackPoint(0, 0, Altitude.WGS84.of(0), Instant.ofEpochSecond(1), Speed.of(2), null),
|
||||
createTrackPoint(0, 0, Altitude.WGS84.of(0), Instant.ofEpochSecond(2), Speed.of(2), null),
|
||||
|
||||
new TrackPoint(TrackPoint.Type.IDLE, Instant.ofEpochSecond(30)),
|
||||
new TrackPoint(TrackPoint.Type.TRACKPOINT, Instant.ofEpochSecond(40))
|
||||
.setHeartRate(50),
|
||||
new TrackPoint(TrackPoint.Type.TRACKPOINT, Instant.ofEpochSecond(45))
|
||||
.setHeartRate(50),
|
||||
new TrackPoint(
|
||||
null,
|
||||
TrackPoint.Type.TRACKPOINT,
|
||||
Position.of(Instant.ofEpochSecond(40)),
|
||||
null,
|
||||
HeartRate.of(50),
|
||||
null,
|
||||
null,
|
||||
null
|
||||
),
|
||||
new TrackPoint(
|
||||
null,
|
||||
TrackPoint.Type.TRACKPOINT,
|
||||
Position.of(Instant.ofEpochSecond(45)),
|
||||
null,
|
||||
HeartRate.of(50),
|
||||
null,
|
||||
null,
|
||||
null
|
||||
),
|
||||
createTrackPoint(0, 1, Altitude.WGS84.of(0), Instant.ofEpochSecond(50)),
|
||||
createTrackPoint(0, 2, Altitude.WGS84.of(0), Instant.ofEpochSecond(55)),
|
||||
|
||||
@@ -210,21 +218,40 @@ public class TrackStatisticsUpdaterTest {
|
||||
// when
|
||||
TrackStatisticsUpdater subject = new TrackStatisticsUpdater(List.of(
|
||||
new TrackPoint(TrackPoint.Type.SEGMENT_START_MANUAL, Instant.ofEpochSecond(0)),
|
||||
createTrackPoint(0, 0, Altitude.WGS84.of(0), Instant.ofEpochSecond(1))
|
||||
.setSensorDistance(Distance.of(10)),
|
||||
createTrackPoint(0, 0, Altitude.WGS84.of(0), Instant.ofEpochSecond(2))
|
||||
.setSensorDistance(Distance.of(10)),
|
||||
|
||||
new TrackPoint(TrackPoint.Type.IDLE, Instant.ofEpochSecond(30))
|
||||
.setSensorDistance(Distance.ofKilometer(1)),
|
||||
new TrackPoint(TrackPoint.Type.TRACKPOINT, Instant.ofEpochSecond(40))
|
||||
.setHeartRate(50),
|
||||
new TrackPoint(TrackPoint.Type.TRACKPOINT, Instant.ofEpochSecond(45))
|
||||
.setHeartRate(50),
|
||||
createTrackPoint(0, 0, Altitude.WGS84.of(0), Instant.ofEpochSecond(50))
|
||||
.setSensorDistance(Distance.of(10)),
|
||||
createTrackPoint(0, 0, Altitude.WGS84.of(0), Instant.ofEpochSecond(55))
|
||||
.setSensorDistance(Distance.of(10)),
|
||||
createTrackPoint(0, 0, Altitude.WGS84.of(0), Instant.ofEpochSecond(1), null, Distance.of(10)),
|
||||
createTrackPoint(0, 0, Altitude.WGS84.of(0), Instant.ofEpochSecond(2), null, Distance.of(10)),
|
||||
new TrackPoint(
|
||||
null,
|
||||
TrackPoint.Type.IDLE,
|
||||
Position.of(Instant.ofEpochSecond(30)),
|
||||
Distance.ofKilometer(1),
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
null
|
||||
),
|
||||
new TrackPoint(
|
||||
null,
|
||||
TrackPoint.Type.TRACKPOINT,
|
||||
Position.of(Instant.ofEpochSecond(40)),
|
||||
null,
|
||||
HeartRate.of(50),
|
||||
null,
|
||||
null,
|
||||
null
|
||||
),
|
||||
new TrackPoint(
|
||||
null,
|
||||
TrackPoint.Type.TRACKPOINT,
|
||||
Position.of(Instant.ofEpochSecond(45)),
|
||||
null,
|
||||
HeartRate.of(50),
|
||||
null,
|
||||
null,
|
||||
null
|
||||
),
|
||||
createTrackPoint(0, 0, Altitude.WGS84.of(0), Instant.ofEpochSecond(50), null, Distance.of(10)),
|
||||
createTrackPoint(0, 0, Altitude.WGS84.of(0), Instant.ofEpochSecond(55), null, Distance.of(10)),
|
||||
|
||||
new TrackPoint(TrackPoint.Type.SEGMENT_END_MANUAL, Instant.ofEpochSecond(60))
|
||||
));
|
||||
@@ -240,8 +267,8 @@ public class TrackStatisticsUpdaterTest {
|
||||
// when
|
||||
TrackStatisticsUpdater subject = new TrackStatisticsUpdater(new TrackPoint(TrackPoint.Type.SEGMENT_START_MANUAL, Instant.ofEpochSecond(0)));
|
||||
subject.addTrackPoint(
|
||||
createTrackPoint(0, 0, Altitude.WGS84.of(0), Instant.ofEpochSecond(10))
|
||||
.setSensorDistance(Distance.of(10)));
|
||||
createTrackPoint(0, 0, Altitude.WGS84.of(0), Instant.ofEpochSecond(10), null, Distance.of(10))
|
||||
);
|
||||
|
||||
subject.addTrackPoint(new TrackPoint(TrackPoint.Type.IDLE, Instant.ofEpochSecond(30)));
|
||||
// then
|
||||
@@ -252,8 +279,17 @@ public class TrackStatisticsUpdaterTest {
|
||||
|
||||
// when
|
||||
subject.addTrackPoint(
|
||||
new TrackPoint(TrackPoint.Type.TRACKPOINT, Instant.ofEpochSecond(40))
|
||||
.setSensorDistance(Distance.ZERO));
|
||||
new TrackPoint(
|
||||
null,
|
||||
TrackPoint.Type.TRACKPOINT,
|
||||
Position.of(Instant.ofEpochSecond(40)),
|
||||
Distance.ZERO,
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
null
|
||||
)
|
||||
);
|
||||
// then
|
||||
assertTrue(subject.isIdle());
|
||||
assertEquals(Duration.ofSeconds(30), subject.getTrackStatistics().movingDuration());
|
||||
@@ -262,8 +298,17 @@ public class TrackStatisticsUpdaterTest {
|
||||
|
||||
// when
|
||||
subject.addTrackPoint(
|
||||
new TrackPoint(TrackPoint.Type.TRACKPOINT, Instant.ofEpochSecond(45))
|
||||
.setSensorDistance(Distance.of(1)));
|
||||
new TrackPoint(
|
||||
null,
|
||||
TrackPoint.Type.TRACKPOINT,
|
||||
Position.of(Instant.ofEpochSecond(45)),
|
||||
Distance.of(1),
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
null
|
||||
)
|
||||
);
|
||||
// then
|
||||
assertTrue(subject.isIdle());
|
||||
assertEquals(Duration.ofSeconds(30), subject.getTrackStatistics().movingDuration());
|
||||
@@ -272,8 +317,17 @@ public class TrackStatisticsUpdaterTest {
|
||||
|
||||
// when
|
||||
subject.addTrackPoint(
|
||||
new TrackPoint(TrackPoint.Type.TRACKPOINT, Instant.ofEpochSecond(50))
|
||||
.setSensorDistance(Distance.of(10)));
|
||||
new TrackPoint(
|
||||
null,
|
||||
TrackPoint.Type.TRACKPOINT,
|
||||
Position.of(Instant.ofEpochSecond(50)),
|
||||
Distance.of(10),
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
null
|
||||
)
|
||||
);
|
||||
// then
|
||||
assertFalse(subject.isIdle());
|
||||
assertEquals(Duration.ofSeconds(30), subject.getTrackStatistics().movingDuration());
|
||||
@@ -294,15 +348,12 @@ public class TrackStatisticsUpdaterTest {
|
||||
public void copy_constructor() {
|
||||
// given
|
||||
TrackPoint tp1 = new TrackPoint(TrackPoint.Type.SEGMENT_START_MANUAL, Instant.ofEpochMilli(1000));
|
||||
TrackPoint tp2 = createTrackPoint(0, 0, Altitude.WGS84.of(5.0), Instant.ofEpochMilli(2000));
|
||||
tp2.setSpeed(Speed.of(5f));
|
||||
TrackPoint tp3 = createTrackPoint(0.00001, 0, Altitude.WGS84.of(5.0), Instant.ofEpochMilli(3000));
|
||||
tp3.setSpeed(Speed.of(5f));
|
||||
TrackPoint tp4 = createTrackPoint(0.0005, 0, Altitude.WGS84.of(5.0), Instant.ofEpochMilli(4000));
|
||||
tp4.setSpeed(Speed.of(5f));
|
||||
TrackPoint tp2 = createTrackPoint(0, 0, Altitude.WGS84.of(5.0), Instant.ofEpochMilli(2000), Speed.of(5f), null);
|
||||
TrackPoint tp3 = createTrackPoint(0.00001, 0, Altitude.WGS84.of(5.0), Instant.ofEpochMilli(3000), Speed.of(5f), null);
|
||||
TrackPoint tp4 = createTrackPoint(0.0005, 0, Altitude.WGS84.of(5.0), Instant.ofEpochMilli(4000), Speed.of(5f), null);
|
||||
TrackPoint tp5 = new TrackPoint(TrackPoint.Type.SEGMENT_END_MANUAL, Instant.ofEpochMilli(5000));
|
||||
|
||||
TrackStatisticsUpdater initial = new TrackStatisticsUpdater(List.of(tp1, tp2,tp3, tp4));
|
||||
TrackStatisticsUpdater initial = new TrackStatisticsUpdater(List.of(tp1, tp2, tp3, tp4));
|
||||
|
||||
// when
|
||||
TrackStatisticsUpdater subject = new TrackStatisticsUpdater(initial, tp5);
|
||||
@@ -335,7 +386,13 @@ public class TrackStatisticsUpdaterTest {
|
||||
}
|
||||
|
||||
public TrackPoint createTrackPoint(double latitude, double longitude, Altitude altitude, Instant time) {
|
||||
return new TrackPoint(TrackPoint.Type.TRACKPOINT,
|
||||
return createTrackPoint(latitude, longitude, altitude, time, null, null);
|
||||
}
|
||||
|
||||
public TrackPoint createTrackPoint(double latitude, double longitude, Altitude altitude, Instant time, Speed speed, Distance sensorDistance) {
|
||||
return new TrackPoint(
|
||||
null,
|
||||
TrackPoint.Type.TRACKPOINT,
|
||||
new Position(
|
||||
time,
|
||||
latitude,
|
||||
@@ -344,7 +401,13 @@ public class TrackStatisticsUpdaterTest {
|
||||
altitude,
|
||||
null,
|
||||
null,
|
||||
null
|
||||
));
|
||||
speed
|
||||
),
|
||||
sensorDistance,
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
null
|
||||
);
|
||||
}
|
||||
}
|
||||
+2
-1
@@ -30,6 +30,7 @@ import org.junit.runner.RunWith;
|
||||
import java.util.List;
|
||||
|
||||
import de.dennisguse.opentracks.R;
|
||||
import de.dennisguse.opentracks.TestUtil;
|
||||
import de.dennisguse.opentracks.TrackListActivity;
|
||||
import de.dennisguse.opentracks.content.data.TestDataUtil;
|
||||
import de.dennisguse.opentracks.data.ContentProviderUtils;
|
||||
@@ -47,7 +48,7 @@ public class EspressoAggregatedFilterTest {
|
||||
public ActivityScenarioRule<TrackListActivity> mActivityTestRule = new ActivityScenarioRule<>(TrackListActivity.class);
|
||||
|
||||
@Rule
|
||||
public GrantPermissionRule mGrantPermissionRule = GrantPermissionRule.grant(android.Manifest.permission.ACCESS_FINE_LOCATION);
|
||||
public GrantPermissionRule mGrantPermissionRule = TestUtil.createGrantPermissionRule();
|
||||
|
||||
@Before
|
||||
public void setUp() {
|
||||
|
||||
+1
-2
@@ -104,8 +104,7 @@ public class IntervalStatisticsUpdaterTest {
|
||||
|
||||
ArrayList<TrackPoint> trackPoints = new ArrayList<>();
|
||||
for (int i = 0; i < numberOfPoints; i++) {
|
||||
trackPoints.add(TestDataUtil.createTrackPoint(i)
|
||||
.setAltitudeGainLoss(null));
|
||||
trackPoints.add(TestDataUtil.createTrackPoint(i, TrackPoint.Type.TRACKPOINT, null));
|
||||
}
|
||||
TrackStatisticsUpdater trackStatisticsUpdater = new TrackStatisticsUpdater(trackPoints);
|
||||
|
||||
|
||||
@@ -278,7 +278,7 @@ public class TrackRecordingActivity extends AbstractActivity implements ChooseAc
|
||||
Intent intent = IntentUtils
|
||||
.newIntent(this, MarkerEditActivity.class)
|
||||
.putExtra(MarkerEditActivity.EXTRA_TRACK_ID, trackId)
|
||||
.putExtra(MarkerEditActivity.EXTRA_LOCATION, trackPoint.getPosition().toLocation());
|
||||
.putExtra(MarkerEditActivity.EXTRA_LOCATION, trackPoint.position().toLocation());
|
||||
startActivity(intent);
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -35,11 +35,11 @@ import de.dennisguse.opentracks.data.TrackDataHub;
|
||||
import de.dennisguse.opentracks.data.models.Statistics;
|
||||
import de.dennisguse.opentracks.data.models.Track;
|
||||
import de.dennisguse.opentracks.data.models.TrackPoint;
|
||||
import de.dennisguse.opentracks.data.statistics.SegmentStatisticUpdater;
|
||||
import de.dennisguse.opentracks.data.statistics.TrackStatisticsUpdater;
|
||||
import de.dennisguse.opentracks.databinding.ChartBinding;
|
||||
import de.dennisguse.opentracks.settings.PreferencesUtils;
|
||||
import de.dennisguse.opentracks.settings.UnitSystem;
|
||||
import de.dennisguse.opentracks.data.statistics.SegmentStatisticUpdater;
|
||||
import de.dennisguse.opentracks.data.statistics.TrackStatisticsUpdater;
|
||||
|
||||
/**
|
||||
* A fragment to display track chart to the user.
|
||||
@@ -204,7 +204,7 @@ public class ChartFragment extends Fragment implements TrackDataHub.Listener {
|
||||
|
||||
public void onSampledInTrackPoint(@NonNull TrackPoint trackPoint, @NonNull Statistics trackStatistics) {
|
||||
if (isResumed()) {
|
||||
ChartPoint point = ChartPoint.create(trackStatistics, trackPoint, trackPoint.getSpeed(), chartByDistance, viewBinding.chartView.getUnitSystem());
|
||||
ChartPoint point = ChartPoint.create(trackStatistics, trackPoint, trackPoint.position().speed(), chartByDistance, viewBinding.chartView.getUnitSystem());
|
||||
pendingPoints.add(point);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -27,8 +27,8 @@ public record ChartPoint(
|
||||
chartByDistance
|
||||
? trackStatistics.totalDistance().toKM_Miles(unitSystem)
|
||||
: trackStatistics.totalDuration().toMillis(),
|
||||
trackPoint.hasAltitude()
|
||||
? Distance.of(trackPoint.getAltitude().toM()).toM_FT(unitSystem)
|
||||
trackPoint.position().hasAltitude()
|
||||
? Distance.of(trackPoint.position().altitude().toM()).toM_FT(unitSystem)
|
||||
: null,
|
||||
smoothedSpeed != null
|
||||
? smoothedSpeed.to(unitSystem)
|
||||
@@ -36,14 +36,14 @@ public record ChartPoint(
|
||||
smoothedSpeed != null
|
||||
? smoothedSpeed.toPace(unitSystem).toSeconds() / 60d
|
||||
: null,
|
||||
trackPoint.hasHeartRate()
|
||||
? (double) trackPoint.getHeartRate().getBPM()
|
||||
trackPoint.heartRate() != null
|
||||
? (double) trackPoint.heartRate().getBPM()
|
||||
: null,
|
||||
trackPoint.hasCadence()
|
||||
? (double) trackPoint.getCadence().getRPM()
|
||||
trackPoint.cadence() != null
|
||||
? (double) trackPoint.cadence().getRPM()
|
||||
: null,
|
||||
trackPoint.hasPower()
|
||||
? (double) trackPoint.getPower().getW()
|
||||
trackPoint.power() != null
|
||||
? (double) trackPoint.power().getW()
|
||||
: null
|
||||
);
|
||||
}
|
||||
|
||||
@@ -51,10 +51,10 @@ 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.models.TrackPoint;
|
||||
import de.dennisguse.opentracks.data.statistics.SensorStatistics;
|
||||
import de.dennisguse.opentracks.data.tables.MarkerColumns;
|
||||
import de.dennisguse.opentracks.data.tables.TrackPointsColumns;
|
||||
import de.dennisguse.opentracks.data.tables.TracksColumns;
|
||||
import de.dennisguse.opentracks.data.statistics.SensorStatistics;
|
||||
import de.dennisguse.opentracks.ui.markers.MarkerUtils;
|
||||
import de.dennisguse.opentracks.util.FileUtils;
|
||||
|
||||
@@ -544,7 +544,12 @@ public class ContentProviderUtils {
|
||||
int altitudeGainIndex = cursor.getColumnIndexOrThrow(TrackPointsColumns.ALTITUDE_GAIN);
|
||||
int altitudeLossIndex = cursor.getColumnIndexOrThrow(TrackPointsColumns.ALTITUDE_LOSS);
|
||||
|
||||
TrackPoint trackPoint = new TrackPoint(
|
||||
AltitudeGainLoss altitudeGainLoss = null;
|
||||
if (!cursor.isNull(altitudeGainIndex) && !cursor.isNull(altitudeLossIndex)) {
|
||||
altitudeGainLoss = new AltitudeGainLoss(cursor.getFloat(altitudeGainIndex), cursor.getFloat(altitudeLossIndex));
|
||||
}
|
||||
|
||||
return new TrackPoint(
|
||||
new TrackPoint.Id(cursor.getInt(idIndex)),
|
||||
TrackPoint.Type.getById(cursor.getInt(typeIndex)),
|
||||
new Position(
|
||||
@@ -556,26 +561,13 @@ public class ContentProviderUtils {
|
||||
!cursor.isNull(accuracyVerticalIndex) ? Distance.of(cursor.getFloat(accuracyVerticalIndex)) : null,
|
||||
!cursor.isNull(bearingIndex) ? cursor.getFloat(bearingIndex) : null,
|
||||
!cursor.isNull(speedIndex) ? Speed.of(cursor.getFloat(speedIndex)) : null
|
||||
));
|
||||
|
||||
if (!cursor.isNull(sensorHeartRateIndex)) {
|
||||
trackPoint.setHeartRate(cursor.getFloat(sensorHeartRateIndex));
|
||||
}
|
||||
if (!cursor.isNull(sensorCadenceIndex)) {
|
||||
trackPoint.setCadence(cursor.getFloat(sensorCadenceIndex));
|
||||
}
|
||||
if (!cursor.isNull(sensorDistanceIndex)) {
|
||||
trackPoint.setSensorDistance(Distance.of(cursor.getFloat(sensorDistanceIndex)));
|
||||
}
|
||||
if (!cursor.isNull(sensorPowerIndex)) {
|
||||
trackPoint.setPower(cursor.getFloat(sensorPowerIndex));
|
||||
}
|
||||
|
||||
if (!cursor.isNull(altitudeGainIndex) && !cursor.isNull(altitudeLossIndex)) {
|
||||
trackPoint.setAltitudeGainLoss(cursor.getFloat(altitudeGainIndex), cursor.getFloat(altitudeLossIndex));
|
||||
}
|
||||
|
||||
return trackPoint;
|
||||
),
|
||||
!cursor.isNull(sensorDistanceIndex) ? Distance.of(cursor.getFloat(sensorDistanceIndex)) : null,
|
||||
!cursor.isNull(sensorHeartRateIndex) ? HeartRate.of(cursor.getFloat(sensorHeartRateIndex)) : null,
|
||||
!cursor.isNull(sensorCadenceIndex) ? Cadence.of(cursor.getFloat(sensorCadenceIndex)) : null,
|
||||
!cursor.isNull(sensorPowerIndex) ? Power.of(cursor.getFloat(sensorPowerIndex)) : null,
|
||||
altitudeGainLoss
|
||||
);
|
||||
}
|
||||
|
||||
//TODO Only used for file import; might be better to replace it.
|
||||
@@ -637,42 +629,42 @@ public class ContentProviderUtils {
|
||||
private ContentValues createContentValues(TrackPoint trackPoint, Track.Id trackId) {
|
||||
ContentValues values = new ContentValues();
|
||||
values.put(TrackPointsColumns.TRACKID, trackId.id());
|
||||
values.put(TrackPointsColumns.TYPE, trackPoint.getType().type_db);
|
||||
values.put(TrackPointsColumns.TYPE, trackPoint.type().type_db);
|
||||
|
||||
if (trackPoint.hasLocation()) {
|
||||
values.put(TrackPointsColumns.LATITUDE, (int) (trackPoint.getPosition().latitude() * 1E6));
|
||||
values.put(TrackPointsColumns.LONGITUDE, (int) (trackPoint.getPosition().longitude() * 1E6));
|
||||
if (trackPoint.position().hasLocation()) {
|
||||
values.put(TrackPointsColumns.LATITUDE, (int) (trackPoint.position().latitude() * 1E6));
|
||||
values.put(TrackPointsColumns.LONGITUDE, (int) (trackPoint.position().longitude() * 1E6));
|
||||
}
|
||||
values.put(TrackPointsColumns.TIME, trackPoint.getTime().toEpochMilli());
|
||||
if (trackPoint.hasAltitude()) {
|
||||
values.put(TrackPointsColumns.ALTITUDE, trackPoint.getAltitude().toM());
|
||||
if (trackPoint.position().hasAltitude()) {
|
||||
values.put(TrackPointsColumns.ALTITUDE, trackPoint.position().altitude().toM());
|
||||
}
|
||||
if (trackPoint.hasHorizontalAccuracy()) {
|
||||
values.put(TrackPointsColumns.HORIZONTAL_ACCURACY, trackPoint.getHorizontalAccuracy().toM());
|
||||
if (trackPoint.position().hasHorizontalAccuracy()) {
|
||||
values.put(TrackPointsColumns.HORIZONTAL_ACCURACY, trackPoint.position().horizontalAccuracy().toM());
|
||||
}
|
||||
if (trackPoint.hasSpeed()) {
|
||||
values.put(TrackPointsColumns.SPEED, trackPoint.getSpeed().toMPS());
|
||||
if (trackPoint.position().hasSpeed()) {
|
||||
values.put(TrackPointsColumns.SPEED, trackPoint.position().speed().toMPS());
|
||||
}
|
||||
if (trackPoint.hasBearing()) {
|
||||
values.put(TrackPointsColumns.BEARING, trackPoint.getBearing());
|
||||
if (trackPoint.position().hasBearing()) {
|
||||
values.put(TrackPointsColumns.BEARING, trackPoint.position().bearing());
|
||||
}
|
||||
|
||||
if (trackPoint.hasHeartRate()) {
|
||||
values.put(TrackPointsColumns.SENSOR_HEARTRATE, trackPoint.getHeartRate().getBPM());
|
||||
if (trackPoint.heartRate() != null) {
|
||||
values.put(TrackPointsColumns.SENSOR_HEARTRATE, trackPoint.heartRate().getBPM());
|
||||
}
|
||||
if (trackPoint.hasCadence()) {
|
||||
values.put(TrackPointsColumns.SENSOR_CADENCE, trackPoint.getCadence().getRPM());
|
||||
if (trackPoint.cadence() != null) {
|
||||
values.put(TrackPointsColumns.SENSOR_CADENCE, trackPoint.cadence().getRPM());
|
||||
}
|
||||
if (trackPoint.hasSensorDistance()) {
|
||||
values.put(TrackPointsColumns.SENSOR_DISTANCE, trackPoint.getSensorDistance().toM());
|
||||
if (trackPoint.sensorDistance() != null) {
|
||||
values.put(TrackPointsColumns.SENSOR_DISTANCE, trackPoint.sensorDistance().toM());
|
||||
}
|
||||
if (trackPoint.hasPower()) {
|
||||
values.put(TrackPointsColumns.SENSOR_POWER, trackPoint.getPower().getW());
|
||||
if (trackPoint.power() != null) {
|
||||
values.put(TrackPointsColumns.SENSOR_POWER, trackPoint.power().getW());
|
||||
}
|
||||
|
||||
if (trackPoint.hasAltitudeGainLoss()) {
|
||||
values.put(TrackPointsColumns.ALTITUDE_GAIN, trackPoint.getAltitudeGainLoss().gain_m());
|
||||
values.put(TrackPointsColumns.ALTITUDE_LOSS, trackPoint.getAltitudeGainLoss().loss_m());
|
||||
if (trackPoint.altitudeGainLoss() != null) {
|
||||
values.put(TrackPointsColumns.ALTITUDE_GAIN, trackPoint.altitudeGainLoss().gain_m());
|
||||
values.put(TrackPointsColumns.ALTITUDE_LOSS, trackPoint.altitudeGainLoss().loss_m());
|
||||
}
|
||||
return values;
|
||||
}
|
||||
|
||||
@@ -33,12 +33,12 @@ import java.util.Set;
|
||||
import de.dennisguse.opentracks.data.models.Statistics;
|
||||
import de.dennisguse.opentracks.data.models.Track;
|
||||
import de.dennisguse.opentracks.data.models.TrackPoint;
|
||||
import de.dennisguse.opentracks.data.statistics.TrackStatisticsUpdater;
|
||||
import de.dennisguse.opentracks.data.tables.TrackPointsColumns;
|
||||
import de.dennisguse.opentracks.data.tables.TracksColumns;
|
||||
import de.dennisguse.opentracks.services.RecordingStatus;
|
||||
import de.dennisguse.opentracks.services.TrackRecordingService;
|
||||
import de.dennisguse.opentracks.services.handlers.AltitudeCorrectionManager;
|
||||
import de.dennisguse.opentracks.data.statistics.TrackStatisticsUpdater;
|
||||
|
||||
/**
|
||||
* Track data hub.
|
||||
@@ -288,14 +288,14 @@ public class TrackDataHub {
|
||||
}
|
||||
|
||||
trackPoint = trackPointIterator.next();
|
||||
TrackPoint.Id trackPointId = trackPoint.getId();
|
||||
TrackPoint.Id trackPointId = trackPoint.id();
|
||||
|
||||
// Stop if past the last wanted point
|
||||
if (maxPointId != null && trackPointId.id() > maxPointId.id()) {
|
||||
break;
|
||||
}
|
||||
|
||||
egm2008Correction.correctAltitude(context, trackPoint);
|
||||
trackPoint = egm2008Correction.correctAltitude(context, trackPoint);
|
||||
|
||||
if (localFirstSeenTrackPointId == null) {
|
||||
localFirstSeenTrackPointId = trackPointId;
|
||||
@@ -319,7 +319,7 @@ public class TrackDataHub {
|
||||
}
|
||||
|
||||
if (trackPoint != null) {
|
||||
localLastSeenTrackPointIdId = trackPoint.getId();
|
||||
localLastSeenTrackPointIdId = trackPoint.id();
|
||||
}
|
||||
|
||||
if (updateSamplingState) {
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
package de.dennisguse.opentracks.data.models;
|
||||
|
||||
import androidx.annotation.Nullable;
|
||||
|
||||
public record HeartRate(float value) {
|
||||
public static HeartRate INVALID = HeartRate.of(0.0f);
|
||||
|
||||
@@ -7,6 +9,12 @@ public record HeartRate(float value) {
|
||||
return new HeartRate(value);
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public static HeartRate ofOrNull(Float value) {
|
||||
if (value == null) return null;
|
||||
return new HeartRate(value);
|
||||
}
|
||||
|
||||
public float getBPM() {
|
||||
return value;
|
||||
}
|
||||
|
||||
@@ -51,10 +51,10 @@ public final class MarkerBuilder {
|
||||
this.typeLocalized = "";
|
||||
this.photoUrl = null;
|
||||
|
||||
if (!trackPoint.hasLocation())
|
||||
if (!trackPoint.position().hasLocation())
|
||||
throw new RuntimeException("Marker requires a trackpoint with a location.");
|
||||
|
||||
this.position = trackPoint.getPosition();
|
||||
this.position = trackPoint.position();
|
||||
}
|
||||
|
||||
public MarkerBuilder(@Nullable Track.Id trackId, @NonNull TrackPoint trackPoint, String name, String description, String typeLocalized, Uri photoUrl) {
|
||||
|
||||
@@ -17,8 +17,9 @@ public record Position(
|
||||
@Nullable Float bearing,
|
||||
@Nullable Speed speed
|
||||
) {
|
||||
@Deprecated
|
||||
public static Position empty() {
|
||||
return of((Instant) null);
|
||||
return of(null);
|
||||
}
|
||||
|
||||
public static Position of(@NonNull Instant time) {
|
||||
@@ -34,10 +35,6 @@ public record Position(
|
||||
);
|
||||
}
|
||||
|
||||
public static Position of(@NonNull Location location) {
|
||||
return of(location, Instant.ofEpochMilli(location.getTime()));
|
||||
}
|
||||
|
||||
public static Position of(@NonNull Location location, @NonNull Instant time) {
|
||||
return new Position(
|
||||
time,
|
||||
@@ -132,9 +129,8 @@ public record Position(
|
||||
return new Position(time, latitude, longitude, horizontalAccuracy, altitude, verticalAccuracy, bearing, speed);
|
||||
}
|
||||
|
||||
//TODO Use double
|
||||
public Position withCoordinates(Double latitude, Double longitude) {
|
||||
return new Position(time, latitude, longitude, horizontalAccuracy, altitude, verticalAccuracy, bearing, speed);
|
||||
public Position withCoordinates(Position position) {
|
||||
return new Position(time, position.latitude, position.longitude, horizontalAccuracy, altitude, verticalAccuracy, bearing, speed);
|
||||
}
|
||||
|
||||
//TODO Use float
|
||||
|
||||
@@ -22,23 +22,168 @@ import androidx.annotation.NonNull;
|
||||
import androidx.annotation.Nullable;
|
||||
|
||||
import java.time.Instant;
|
||||
import java.util.Optional;
|
||||
|
||||
/**
|
||||
* Sensor and/or location information for a specific point in time.
|
||||
* <p>
|
||||
* Time is created using the {@link de.dennisguse.opentracks.services.handlers.MonotonicClock}, because system time jump backwards.
|
||||
* GPS time is ignored as for non-GPS events, we could not create GPS-based timestamps.
|
||||
* GPS time is ignored as for non-GPS events, we cannot create GPS-based timestamps.
|
||||
*/
|
||||
//TODO Should be a record (with final properties)
|
||||
public class TrackPoint {
|
||||
public record TrackPoint(
|
||||
|
||||
@Nullable
|
||||
private final TrackPoint.Id id;
|
||||
@Nullable
|
||||
TrackPoint.Id id,
|
||||
|
||||
@NonNull
|
||||
TrackPoint.Type type,
|
||||
|
||||
//Requires: position.time must be non-null
|
||||
@NonNull
|
||||
Position position,
|
||||
|
||||
Distance sensorDistance,
|
||||
HeartRate heartRate,
|
||||
Cadence cadence,
|
||||
Power power,
|
||||
AltitudeGainLoss altitudeGainLoss
|
||||
) {
|
||||
|
||||
public TrackPoint(Type type, Position position) {
|
||||
this(
|
||||
null,
|
||||
type,
|
||||
position,
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
null
|
||||
);
|
||||
}
|
||||
|
||||
@Deprecated //TOOD Private?
|
||||
public TrackPoint(Type type, Instant now) {
|
||||
this(
|
||||
null,
|
||||
type,
|
||||
Position.of(now),
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
null
|
||||
);
|
||||
}
|
||||
|
||||
public static TrackPoint createSegmentStartManualWithTime(Instant time) {
|
||||
return new TrackPoint(TrackPoint.Type.SEGMENT_START_MANUAL, time);
|
||||
}
|
||||
|
||||
public static TrackPoint with(TrackPoint trackPoint, Altitude.EGM2008 correctAltitude) {
|
||||
return new TrackPoint(
|
||||
trackPoint.id,
|
||||
trackPoint.type,
|
||||
new Position(
|
||||
trackPoint.position.time(),
|
||||
trackPoint.position.latitude(),
|
||||
trackPoint.position.longitude(),
|
||||
trackPoint.position.horizontalAccuracy(),
|
||||
correctAltitude,
|
||||
trackPoint.position.verticalAccuracy(),
|
||||
trackPoint.position.bearing(),
|
||||
trackPoint.position.speed()
|
||||
),
|
||||
trackPoint.sensorDistance,
|
||||
trackPoint.heartRate,
|
||||
trackPoint.cadence,
|
||||
trackPoint.power,
|
||||
trackPoint.altitudeGainLoss
|
||||
);
|
||||
}
|
||||
|
||||
public TrackPoint with(Type newType) {
|
||||
return new TrackPoint(
|
||||
id,
|
||||
newType,
|
||||
position,
|
||||
sensorDistance,
|
||||
heartRate,
|
||||
cadence,
|
||||
power,
|
||||
altitudeGainLoss
|
||||
);
|
||||
}
|
||||
|
||||
public TrackPoint with(Position newPosition) {
|
||||
return new TrackPoint(
|
||||
id,
|
||||
type,
|
||||
newPosition,
|
||||
sensorDistance,
|
||||
heartRate,
|
||||
cadence,
|
||||
power,
|
||||
altitudeGainLoss
|
||||
);
|
||||
}
|
||||
|
||||
//Requires: position.time must be non-null
|
||||
@NonNull
|
||||
private Position position;
|
||||
public Instant getTime() {
|
||||
return position.time();
|
||||
}
|
||||
|
||||
|
||||
public TrackPoint minusCumulativeSensorData(@NonNull TrackPoint lastTrackPoint) {
|
||||
Distance newSensorDistance = sensorDistance;
|
||||
if (sensorDistance != null && lastTrackPoint.sensorDistance != null) {
|
||||
newSensorDistance = sensorDistance.minus(lastTrackPoint.sensorDistance);
|
||||
}
|
||||
AltitudeGainLoss newAltitudeGainLoss = altitudeGainLoss;
|
||||
if (altitudeGainLoss != null && lastTrackPoint.altitudeGainLoss() != null) {
|
||||
newAltitudeGainLoss = new AltitudeGainLoss(
|
||||
altitudeGainLoss.gain_m() - lastTrackPoint.altitudeGainLoss.gain_m(),
|
||||
altitudeGainLoss.loss_m() - lastTrackPoint.altitudeGainLoss.loss_m());
|
||||
}
|
||||
return new TrackPoint(
|
||||
id,
|
||||
type,
|
||||
position,
|
||||
newSensorDistance,
|
||||
heartRate,
|
||||
cadence,
|
||||
power,
|
||||
newAltitudeGainLoss
|
||||
);
|
||||
}
|
||||
|
||||
@NonNull
|
||||
public Distance distanceToPrevious(@NonNull TrackPoint previous) {
|
||||
if (sensorDistance() != null) {
|
||||
return sensorDistance();
|
||||
}
|
||||
return distanceToPreviousFromLocation(previous);
|
||||
}
|
||||
|
||||
@NonNull
|
||||
public Distance distanceToPreviousFromLocation(@NonNull TrackPoint previous) {
|
||||
if (!position.hasLocation() || !previous.position().hasLocation()) {
|
||||
throw new RuntimeException("Cannot compute distance.");
|
||||
}
|
||||
|
||||
return Distance.of(position().toLocation().distanceTo(previous.position().toLocation()));
|
||||
}
|
||||
|
||||
public boolean isIdleTriggered() {
|
||||
return type == TrackPoint.Type.IDLE;
|
||||
}
|
||||
|
||||
public boolean isSegmentManualStart() {
|
||||
return type == TrackPoint.Type.SEGMENT_START_MANUAL;
|
||||
}
|
||||
|
||||
public boolean isSegmentManualEnd() {
|
||||
return type == TrackPoint.Type.SEGMENT_END_MANUAL;
|
||||
}
|
||||
|
||||
public enum Type {
|
||||
SEGMENT_START_MANUAL(-2), //Start of a segment due to user interaction (start, resume)
|
||||
@@ -75,298 +220,6 @@ public class TrackPoint {
|
||||
}
|
||||
}
|
||||
|
||||
@NonNull
|
||||
private Type type;
|
||||
|
||||
private Distance sensorDistance;
|
||||
private HeartRate heartRate = null;
|
||||
private Cadence cadence = null;
|
||||
private Power power = null;
|
||||
private AltitudeGainLoss altitudeGainLoss = null;
|
||||
|
||||
public TrackPoint(@Nullable TrackPoint.Id id, @NonNull Type type, @NonNull Position position) {
|
||||
this.id = id;
|
||||
this.type = type;
|
||||
this.position = position;
|
||||
}
|
||||
|
||||
public TrackPoint(@NonNull Type type, @NonNull Instant time) {
|
||||
this(null, type, Position.of(time));
|
||||
}
|
||||
|
||||
public TrackPoint(@NonNull Type type, @NonNull Position position) {
|
||||
this(null, type, position);
|
||||
}
|
||||
|
||||
public static TrackPoint createSegmentStartManualWithTime(Instant time) {
|
||||
return new TrackPoint(Type.SEGMENT_START_MANUAL, time);
|
||||
}
|
||||
|
||||
public static TrackPoint createSegmentEndWithTime(Instant time) {
|
||||
return new TrackPoint(Type.SEGMENT_END_MANUAL, time);
|
||||
}
|
||||
|
||||
@NonNull
|
||||
public Type getType() {
|
||||
return type;
|
||||
}
|
||||
|
||||
@Deprecated //Should not be needed.
|
||||
public TrackPoint setType(@NonNull Type type) {
|
||||
this.type = type;
|
||||
return this;
|
||||
}
|
||||
|
||||
public boolean isIdleTriggered() {
|
||||
return type == Type.IDLE;
|
||||
}
|
||||
|
||||
public boolean isSegmentManualStart() {
|
||||
return type == Type.SEGMENT_START_MANUAL;
|
||||
}
|
||||
|
||||
public boolean isSegmentManualEnd() {
|
||||
return type == Type.SEGMENT_END_MANUAL;
|
||||
}
|
||||
|
||||
//TODO Check if naming is correct.
|
||||
public boolean wasCreatedManually() {
|
||||
return hasLocation() || hasSpeed();
|
||||
}
|
||||
|
||||
/**
|
||||
* May be null if the track was not loaded from the database.
|
||||
*/
|
||||
@Nullable
|
||||
public TrackPoint.Id getId() {
|
||||
return id;
|
||||
}
|
||||
|
||||
public boolean hasLocation() {
|
||||
return position.hasLocation();
|
||||
}
|
||||
|
||||
@NonNull
|
||||
public Position getPosition() {
|
||||
return position;
|
||||
}
|
||||
|
||||
public TrackPoint setPosition(Position position) {
|
||||
this.position = position.with(this.position.time());
|
||||
return this;
|
||||
}
|
||||
|
||||
public boolean hasAltitudeGainLoss() {
|
||||
return altitudeGainLoss != null;
|
||||
}
|
||||
|
||||
public AltitudeGainLoss getAltitudeGainLoss() {
|
||||
return altitudeGainLoss;
|
||||
}
|
||||
|
||||
public TrackPoint setAltitudeGainLoss(AltitudeGainLoss altitudeGainLoss) {
|
||||
this.altitudeGainLoss = altitudeGainLoss;
|
||||
return this;
|
||||
}
|
||||
|
||||
public TrackPoint setAltitudeGainLoss(float altitudeGain_m, float altitudeLoss_m) {
|
||||
setAltitudeGainLoss(new AltitudeGainLoss(altitudeGain_m, altitudeLoss_m));
|
||||
return this;
|
||||
}
|
||||
|
||||
@NonNull
|
||||
public Instant getTime() {
|
||||
return position.time();
|
||||
}
|
||||
|
||||
public boolean hasAltitude() {
|
||||
return position.hasAltitude();
|
||||
}
|
||||
|
||||
public Altitude getAltitude() {
|
||||
return position.altitude();
|
||||
}
|
||||
|
||||
@Deprecated
|
||||
public TrackPoint setAltitude(Altitude altitude) {
|
||||
position = position.with(altitude);
|
||||
return this;
|
||||
}
|
||||
|
||||
public boolean hasSpeed() {
|
||||
return position.hasSpeed();
|
||||
}
|
||||
|
||||
public Speed getSpeed() {
|
||||
return position.speed();
|
||||
}
|
||||
|
||||
@Deprecated
|
||||
public TrackPoint setSpeed(Speed speed) {
|
||||
this.position = position.with(speed);
|
||||
return this;
|
||||
}
|
||||
|
||||
public boolean hasBearing() {
|
||||
return position.hasBearing();
|
||||
}
|
||||
|
||||
public float getBearing() {
|
||||
return position.bearing();
|
||||
}
|
||||
|
||||
public TrackPoint setBearing(Float bearing) {
|
||||
this.position = this.position.withBearing(bearing);
|
||||
return this;
|
||||
}
|
||||
|
||||
public boolean hasHorizontalAccuracy() {
|
||||
return position.hasHorizontalAccuracy();
|
||||
}
|
||||
|
||||
public Distance getHorizontalAccuracy() {
|
||||
return position.horizontalAccuracy();
|
||||
}
|
||||
|
||||
@Deprecated
|
||||
public TrackPoint setHorizontalAccuracy(Distance horizontalAccuracy) {
|
||||
this.position = this.position.withHorizontalAccuracy(horizontalAccuracy);
|
||||
return this;
|
||||
}
|
||||
|
||||
public boolean hasVerticalAccuracy() {
|
||||
return position.hasVerticalAccuracy();
|
||||
}
|
||||
|
||||
public Distance getVerticalAccuracy() {
|
||||
return position.verticalAccuracy();
|
||||
}
|
||||
|
||||
public TrackPoint setVerticalAccuracy(Distance verticalAccuracy) {
|
||||
this.position = this.position.withVerticalAccuracy(verticalAccuracy);
|
||||
return this;
|
||||
}
|
||||
|
||||
@NonNull
|
||||
public Distance distanceToPrevious(@NonNull TrackPoint previous) {
|
||||
if (hasSensorDistance()) {
|
||||
return getSensorDistance();
|
||||
}
|
||||
return distanceToPreviousFromLocation(previous);
|
||||
}
|
||||
|
||||
@NonNull
|
||||
public Distance distanceToPreviousFromLocation(@NonNull TrackPoint previous) {
|
||||
if (!hasLocation() || hasLocation() != previous.hasLocation()) {
|
||||
throw new RuntimeException("Cannot compute distance.");
|
||||
}
|
||||
|
||||
return Distance.of(getPosition().toLocation().distanceTo(previous.getPosition().toLocation()));
|
||||
}
|
||||
|
||||
public boolean fulfillsAccuracy(Distance thresholdHorizontalAccuracy) {
|
||||
return position.fulfillsAccuracy(thresholdHorizontalAccuracy);
|
||||
}
|
||||
|
||||
public Optional<Float> bearingTo(@NonNull TrackPoint dest) {
|
||||
if (!dest.hasLocation() || !hasLocation()) {
|
||||
return Optional.empty();
|
||||
}
|
||||
return Optional.of(getPosition().toLocation().bearingTo(dest.getPosition().toLocation()));
|
||||
}
|
||||
|
||||
// Sensor data
|
||||
public boolean hasSensorDistance() {
|
||||
return sensorDistance != null;
|
||||
}
|
||||
|
||||
public Distance getSensorDistance() {
|
||||
return sensorDistance;
|
||||
}
|
||||
|
||||
public TrackPoint setSensorDistance(Distance distance_m) {
|
||||
this.sensorDistance = distance_m;
|
||||
return this;
|
||||
}
|
||||
|
||||
public TrackPoint minusCumulativeSensorData(@NonNull TrackPoint lastTrackPoint) {
|
||||
if (hasSensorDistance() && lastTrackPoint.hasSensorDistance()) {
|
||||
sensorDistance = sensorDistance.minus(lastTrackPoint.getSensorDistance());
|
||||
}
|
||||
if (hasAltitudeGainLoss() && lastTrackPoint.hasAltitudeGainLoss()) {
|
||||
altitudeGainLoss = new AltitudeGainLoss(
|
||||
altitudeGainLoss.gain_m() - lastTrackPoint.altitudeGainLoss.gain_m(),
|
||||
altitudeGainLoss.loss_m() - lastTrackPoint.altitudeGainLoss.loss_m());
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
public boolean hasHeartRate() {
|
||||
return heartRate != null;
|
||||
}
|
||||
|
||||
public HeartRate getHeartRate() {
|
||||
return heartRate;
|
||||
}
|
||||
|
||||
public TrackPoint setHeartRate(HeartRate heartRate) {
|
||||
this.heartRate = heartRate;
|
||||
return this;
|
||||
}
|
||||
|
||||
public TrackPoint setHeartRate(float heartRate) {
|
||||
return setHeartRate(HeartRate.of(heartRate));
|
||||
}
|
||||
|
||||
public boolean hasCadence() {
|
||||
return cadence != null;
|
||||
}
|
||||
|
||||
public Cadence getCadence() {
|
||||
return cadence;
|
||||
}
|
||||
|
||||
public TrackPoint setCadence(Cadence cadence) {
|
||||
this.cadence = cadence;
|
||||
return this;
|
||||
}
|
||||
|
||||
public TrackPoint setCadence(float cadence) {
|
||||
return setCadence(Cadence.of(cadence));
|
||||
}
|
||||
|
||||
public boolean hasPower() {
|
||||
return power != null;
|
||||
}
|
||||
|
||||
public Power getPower() {
|
||||
return power;
|
||||
}
|
||||
|
||||
public TrackPoint setPower(Power power) {
|
||||
this.power = power;
|
||||
return this;
|
||||
}
|
||||
|
||||
public TrackPoint setPower(float power) {
|
||||
return setPower(Power.of(power));
|
||||
}
|
||||
|
||||
@NonNull
|
||||
@Override
|
||||
public String toString() {
|
||||
return "TrackPoint{" +
|
||||
"id=" + id +
|
||||
", type=" + type +
|
||||
", position=" + position +
|
||||
", sensorDistance=" + sensorDistance +
|
||||
", heartRate=" + heartRate +
|
||||
", cadence=" + cadence +
|
||||
", power=" + power +
|
||||
", altitudeGainLoss=" + altitudeGainLoss +
|
||||
'}';
|
||||
}
|
||||
|
||||
public record Id(long id) implements Parcelable {
|
||||
|
||||
@Override
|
||||
|
||||
@@ -154,7 +154,7 @@ public class SegmentStatisticUpdater {
|
||||
Duration movingDuration = Duration.between(lastTrackPoint.getTime(), trackPoint.getTime());
|
||||
|
||||
if (movingDuration.isNegative()) {
|
||||
throw new RuntimeException("Moving time cannot be negative");
|
||||
throw new RuntimeException("Moving time cannot be negative: " + lastTrackPoint.getTime() + " is after " + trackPoint.getTime());
|
||||
}
|
||||
this.movingDuration = this.movingDuration.plus(movingDuration);
|
||||
}
|
||||
|
||||
+17
-17
@@ -111,32 +111,32 @@ public class TrackStatisticsUpdater {
|
||||
currentSegment.updateTotalTime(trackPoint.getTime());
|
||||
|
||||
// Process sensor data: barometer
|
||||
if (trackPoint.hasAltitudeGainLoss()) {
|
||||
currentSegment.addTotalAltitudeGainLoss(trackPoint.getAltitudeGainLoss());
|
||||
if (trackPoint.altitudeGainLoss() != null) {
|
||||
currentSegment.addTotalAltitudeGainLoss(trackPoint.altitudeGainLoss());
|
||||
}
|
||||
|
||||
//Update absolute (GPS-based) altitude
|
||||
if (trackPoint.hasAltitude()) {
|
||||
currentSegment.updateAltitudeExtremities(trackPoint.getAltitude());
|
||||
// Update absolute (GPS-based) altitude
|
||||
if (trackPoint.position().hasAltitude()) {
|
||||
currentSegment.updateAltitudeExtremities(trackPoint.position().altitude());
|
||||
}
|
||||
|
||||
// Update heart rate
|
||||
if (trackPoint.hasHeartRate() && lastTrackPoint != null) {
|
||||
if (trackPoint.heartRate() != null && lastTrackPoint != null) {
|
||||
Duration trackPointDuration = Duration.between(lastTrackPoint.getTime(), trackPoint.getTime());
|
||||
Duration newTotalDuration = totalHeartRateDuration.plus(trackPointDuration);
|
||||
|
||||
averageHeartRateBPM = (totalHeartRateDuration.toMillis() * averageHeartRateBPM + trackPointDuration.toMillis() * trackPoint.getHeartRate().getBPM()) / newTotalDuration.toMillis();
|
||||
averageHeartRateBPM = (totalHeartRateDuration.toMillis() * averageHeartRateBPM + trackPointDuration.toMillis() * trackPoint.heartRate().getBPM()) / newTotalDuration.toMillis();
|
||||
totalHeartRateDuration = newTotalDuration;
|
||||
|
||||
currentSegment.setAverageHeartRate(HeartRate.of(averageHeartRateBPM));
|
||||
}
|
||||
|
||||
// Update power
|
||||
if (trackPoint.hasPower() && lastTrackPoint != null) {
|
||||
if (trackPoint.power() != null && lastTrackPoint != null) {
|
||||
Duration trackPointDuration = Duration.between(lastTrackPoint.getTime(), trackPoint.getTime());
|
||||
Duration newTotalDuration = totalPowerDuration.plus(trackPointDuration);
|
||||
|
||||
averagePowerW = (totalPowerDuration.toMillis() * averagePowerW + trackPointDuration.toMillis() * trackPoint.getPower().getW()) / newTotalDuration.toMillis();
|
||||
averagePowerW = (totalPowerDuration.toMillis() * averagePowerW + trackPointDuration.toMillis() * trackPoint.power().getW()) / newTotalDuration.toMillis();
|
||||
totalPowerDuration = newTotalDuration;
|
||||
|
||||
currentSegment.setAveragePower(Power.of(averagePowerW));
|
||||
@@ -145,11 +145,11 @@ public class TrackStatisticsUpdater {
|
||||
{
|
||||
// Update total distance
|
||||
Distance movingDistance = null;
|
||||
if (trackPoint.hasSensorDistance()) {
|
||||
movingDistance = trackPoint.getSensorDistance();
|
||||
if (trackPoint.sensorDistance() != null) {
|
||||
movingDistance = trackPoint.sensorDistance();
|
||||
} else if (lastTrackPoint != null
|
||||
&& lastTrackPoint.hasLocation()
|
||||
&& trackPoint.hasLocation()) {
|
||||
&& lastTrackPoint.position().hasLocation()
|
||||
&& trackPoint.position().hasLocation()) {
|
||||
// GPS-based distance/speed
|
||||
movingDistance = trackPoint.distanceToPrevious(lastTrackPoint);
|
||||
}
|
||||
@@ -173,7 +173,7 @@ public class TrackStatisticsUpdater {
|
||||
}
|
||||
}
|
||||
|
||||
if (trackPoint.hasSpeed()) {
|
||||
if (trackPoint.position().hasSpeed()) {
|
||||
updateSpeed(trackPoint);
|
||||
}
|
||||
}
|
||||
@@ -205,7 +205,7 @@ public class TrackStatisticsUpdater {
|
||||
* Updates a speed reading while assuming the user is moving.
|
||||
*/
|
||||
private void updateSpeed(@NonNull TrackPoint trackPoint) {
|
||||
Speed currentSpeed = trackPoint.getSpeed();
|
||||
Speed currentSpeed = trackPoint.position().speed();
|
||||
if (currentSpeed.greaterThan(currentSegment.getMaxSpeed())) {
|
||||
currentSegment.setMaxSpeed(currentSpeed);
|
||||
}
|
||||
@@ -215,8 +215,8 @@ public class TrackStatisticsUpdater {
|
||||
@Override
|
||||
public String toString() {
|
||||
return "TrackStatisticsUpdater{" +
|
||||
"segmentStatisticUpdater=" + statisticsWithoutCurrentSegment +
|
||||
", currentSegment=" + currentSegment +
|
||||
"currentSegment=" + currentSegment +
|
||||
", segmentStatisticUpdater=" + statisticsWithoutCurrentSegment +
|
||||
", lastTrackPoint=" + lastTrackPoint +
|
||||
'}';
|
||||
}
|
||||
|
||||
@@ -90,20 +90,20 @@ public class CSVTrackExporter implements TrackExporter {
|
||||
public boolean writeTrack(@NonNull List<Track> tracks, @NonNull OutputStream outputStream) {
|
||||
List<Column> columns = List.of(
|
||||
new Column("time", null),
|
||||
new Column("trackpoint_type", t -> quote(t.getType().name())),
|
||||
new Column("latitude", t -> t.hasLocation() ? COORDINATE_FORMAT.format(t.getPosition().latitude()) : ""),
|
||||
new Column("longitude", t -> t.hasLocation() ? COORDINATE_FORMAT.format(t.getPosition().longitude()) : ""),
|
||||
new Column("altitude", t -> t.hasAltitude() ? ALTITUDE_FORMAT.format(t.getAltitude().toM()) : ""),
|
||||
new Column("accuracy_horizontal", t -> t.hasHorizontalAccuracy() ? DISTANCE_FORMAT.format(t.getHorizontalAccuracy().toM()) : ""),
|
||||
new Column("accuracy_vertical", t -> t.hasVerticalAccuracy() ? DISTANCE_FORMAT.format(t.getVerticalAccuracy().toM()) : ""),
|
||||
new Column("trackpoint_type", t -> quote(t.type().name())),
|
||||
new Column("latitude", t -> t.position().hasLocation() ? COORDINATE_FORMAT.format(t.position().latitude()) : ""),
|
||||
new Column("longitude", t -> t.position().hasLocation() ? COORDINATE_FORMAT.format(t.position().longitude()) : ""),
|
||||
new Column("altitude", t -> t.position().hasAltitude() ? ALTITUDE_FORMAT.format(t.position().altitude().toM()) : ""),
|
||||
new Column("accuracy_horizontal", t -> t.position().hasHorizontalAccuracy() ? DISTANCE_FORMAT.format(t.position().horizontalAccuracy().toM()) : ""),
|
||||
new Column("accuracy_vertical", t -> t.position().hasVerticalAccuracy() ? DISTANCE_FORMAT.format(t.position().verticalAccuracy().toM()) : ""),
|
||||
|
||||
new Column("speed", t -> t.hasSpeed() ? SPEED_FORMAT.format(t.getSpeed().toKMH()) : ""),
|
||||
new Column("altitude_gain", t -> t.hasAltitudeGainLoss() ? ALTITUDE_FORMAT.format(t.getAltitudeGainLoss().gain_m()) : ""),
|
||||
new Column("altitude_loss", t -> t.hasAltitudeGainLoss() ? ALTITUDE_FORMAT.format(t.getAltitudeGainLoss().loss_m()) : ""),
|
||||
new Column("sensor_distance", t -> t.hasSensorDistance() ? DISTANCE_FORMAT.format(t.getSensorDistance().toM()) : ""),
|
||||
new Column("heartrate", t -> t.hasHeartRate() ? HEARTRATE_FORMAT.format(t.getHeartRate().getBPM()) : ""),
|
||||
new Column("cadence", t -> t.hasCadence() ? CADENCE_FORMAT.format(t.getCadence().getRPM()) : ""),
|
||||
new Column("power", t -> t.hasPower() ? POWER_FORMAT.format(t.getPower().getW()) : ""));
|
||||
new Column("speed", t -> t.position().hasSpeed() ? SPEED_FORMAT.format(t.position().speed().toKMH()) : ""),
|
||||
new Column("altitude_gain", t -> t.altitudeGainLoss() != null ? ALTITUDE_FORMAT.format(t.altitudeGainLoss().gain_m()) : ""),
|
||||
new Column("altitude_loss", t -> t.altitudeGainLoss() != null ? ALTITUDE_FORMAT.format(t.altitudeGainLoss().loss_m()) : ""),
|
||||
new Column("sensor_distance", t -> t.sensorDistance() != null ? DISTANCE_FORMAT.format(t.sensorDistance().toM()) : ""),
|
||||
new Column("heartrate", t -> t.heartRate() != null ? HEARTRATE_FORMAT.format(t.heartRate().getBPM()) : ""),
|
||||
new Column("cadence", t -> t.cadence() != null ? CADENCE_FORMAT.format(t.cadence().getRPM()) : ""),
|
||||
new Column("power", t -> t.power() != null ? POWER_FORMAT.format(t.power().getW()) : ""));
|
||||
|
||||
try {
|
||||
prepare(outputStream);
|
||||
|
||||
@@ -140,7 +140,7 @@ public class GPXTrackExporter implements TrackExporter {
|
||||
wroteTrack = true;
|
||||
}
|
||||
|
||||
switch (trackPoint.getType()) {
|
||||
switch (trackPoint.type()) {
|
||||
case SEGMENT_START_MANUAL ->
|
||||
Log.i(TAG, "Exporting " + TrackPoint.Type.SEGMENT_START_MANUAL.name() + " is not supported.");
|
||||
case SEGMENT_END_MANUAL -> {
|
||||
@@ -152,7 +152,7 @@ public class GPXTrackExporter implements TrackExporter {
|
||||
if (wroteSegment) writeCloseSegment();
|
||||
writeOpenSegment();
|
||||
wroteSegment = true;
|
||||
if (trackPoint.hasLocation()) {
|
||||
if (trackPoint.position().hasLocation()) {
|
||||
trackDistance = trackDistance.plus(writeTrackPoint(track.zoneOffset(), trackPoint, sensorPoints, trackDistance));
|
||||
sensorPoints.clear();
|
||||
} else {
|
||||
@@ -165,7 +165,7 @@ public class GPXTrackExporter implements TrackExporter {
|
||||
writeOpenSegment();
|
||||
wroteSegment = true;
|
||||
}
|
||||
if (trackPoint.hasLocation()) {
|
||||
if (trackPoint.position().hasLocation()) {
|
||||
trackDistance = trackDistance.plus(writeTrackPoint(track.zoneOffset(), trackPoint, sensorPoints, trackDistance));
|
||||
sensorPoints.clear();
|
||||
} else {
|
||||
@@ -176,7 +176,7 @@ public class GPXTrackExporter implements TrackExporter {
|
||||
// Not supported as IDLE-TrackPoints have no location.
|
||||
}
|
||||
default ->
|
||||
throw new RuntimeException("Exporting this TrackPoint type is not implemented: " + trackPoint.getType());
|
||||
throw new RuntimeException("Exporting this TrackPoint type is not implemented: " + trackPoint.type());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -306,10 +306,10 @@ public class GPXTrackExporter implements TrackExporter {
|
||||
private Distance writeTrackPoint(ZoneOffset zoneOffset, TrackPoint trackPoint, List<TrackPoint> sensorPoints, Distance trackDistance) {
|
||||
Distance cumulativeDistance;
|
||||
|
||||
printWriter.println("<trkpt " + formatLocation(trackPoint.getPosition()) + ">");
|
||||
printWriter.println("<trkpt " + formatLocation(trackPoint.position()) + ">");
|
||||
|
||||
if (trackPoint.hasAltitude()) {
|
||||
printWriter.println("<ele>" + ALTITUDE_FORMAT.format(trackPoint.getAltitude().toM()) + "</ele>");
|
||||
if (trackPoint.position().hasAltitude()) {
|
||||
printWriter.println("<ele>" + ALTITUDE_FORMAT.format(trackPoint.position().altitude().toM()) + "</ele>");
|
||||
}
|
||||
|
||||
printWriter.println("<time>" + StringUtils.formatDateTimeIso8601(trackPoint.getTime(), zoneOffset) + "</time>");
|
||||
@@ -317,41 +317,41 @@ public class GPXTrackExporter implements TrackExporter {
|
||||
{
|
||||
String trackPointExtensionV2Content = "";
|
||||
|
||||
if (trackPoint.hasHeartRate()) {
|
||||
trackPointExtensionV2Content += "<gpxtpx:hr>" + HEARTRATE_FORMAT.format(trackPoint.getHeartRate().getBPM()) + "</gpxtpx:hr>\n";
|
||||
if (trackPoint.heartRate() != null) {
|
||||
trackPointExtensionV2Content += "<gpxtpx:hr>" + HEARTRATE_FORMAT.format(trackPoint.heartRate().getBPM()) + "</gpxtpx:hr>\n";
|
||||
}
|
||||
|
||||
if (trackPoint.hasCadence()) {
|
||||
trackPointExtensionV2Content += "<gpxtpx:cad>" + CADENCE_FORMAT.format(trackPoint.getCadence().getRPM()) + "</gpxtpx:cad>\n";
|
||||
if (trackPoint.cadence() != null) {
|
||||
trackPointExtensionV2Content += "<gpxtpx:cad>" + CADENCE_FORMAT.format(trackPoint.cadence().getRPM()) + "</gpxtpx:cad>\n";
|
||||
}
|
||||
|
||||
if (trackPoint.hasSpeed()) {
|
||||
trackPointExtensionV2Content += "<gpxtpx:speed>" + SPEED_FORMAT.format(trackPoint.getSpeed().toMPS()) + "</gpxtpx:speed>\n";
|
||||
if (trackPoint.position().hasSpeed()) {
|
||||
trackPointExtensionV2Content += "<gpxtpx:speed>" + SPEED_FORMAT.format(trackPoint.position().speed().toMPS()) + "</gpxtpx:speed>\n";
|
||||
}
|
||||
|
||||
String extensionContent = "";
|
||||
if (trackPoint.hasPower()) {
|
||||
extensionContent += "<pwr:PowerInWatts>" + POWER_FORMAT.format(trackPoint.getPower().getW()) + "</pwr:PowerInWatts>\n";
|
||||
if (trackPoint.power() != null) {
|
||||
extensionContent += "<pwr:PowerInWatts>" + POWER_FORMAT.format(trackPoint.power().getW()) + "</pwr:PowerInWatts>\n";
|
||||
}
|
||||
|
||||
Double cumulativeGain = cumulateSensorData(trackPoint, sensorPoints, (tp) -> tp.hasAltitudeGainLoss() ? (double) tp.getAltitudeGainLoss().gain_m() : null);
|
||||
Double cumulativeGain = cumulateSensorData(trackPoint, sensorPoints, (tp) -> tp.altitudeGainLoss() != null ? (double) tp.altitudeGainLoss().gain_m() : null);
|
||||
if (cumulativeGain != null) {
|
||||
extensionContent += ("<opentracks:gain>" + ALTITUDE_FORMAT.format(cumulativeGain) + "</opentracks:gain>\n");
|
||||
}
|
||||
|
||||
Double cumulativeLoss = cumulateSensorData(trackPoint, sensorPoints, (tp) -> tp.hasAltitudeGainLoss() ? (double) tp.getAltitudeGainLoss().loss_m() : null);
|
||||
Double cumulativeLoss = cumulateSensorData(trackPoint, sensorPoints, (tp) -> tp.altitudeGainLoss() != null ? (double) tp.altitudeGainLoss().loss_m() : null);
|
||||
if (cumulativeLoss != null) {
|
||||
extensionContent += ("<opentracks:loss>" + ALTITUDE_FORMAT.format(cumulativeLoss) + "</opentracks:loss>\n");
|
||||
}
|
||||
|
||||
if (trackPoint.hasHorizontalAccuracy()) {
|
||||
extensionContent += ("<opentracks:accuracy_horizontal>" + DISTANCE_FORMAT.format(trackPoint.getHorizontalAccuracy().toM()) + "</opentracks:accuracy_horizontal>");
|
||||
if (trackPoint.position().hasHorizontalAccuracy()) {
|
||||
extensionContent += ("<opentracks:accuracy_horizontal>" + DISTANCE_FORMAT.format(trackPoint.position().horizontalAccuracy().toM()) + "</opentracks:accuracy_horizontal>");
|
||||
}
|
||||
if (trackPoint.hasVerticalAccuracy()) {
|
||||
extensionContent += ("<opentracks:accuracy_vertical>" + DISTANCE_FORMAT.format(trackPoint.getVerticalAccuracy().toM()) + "</opentracks:accuracy_vertical>");
|
||||
if (trackPoint.position().hasVerticalAccuracy()) {
|
||||
extensionContent += ("<opentracks:accuracy_vertical>" + DISTANCE_FORMAT.format(trackPoint.position().verticalAccuracy().toM()) + "</opentracks:accuracy_vertical>");
|
||||
}
|
||||
|
||||
cumulativeDistance = Distance.ofOrNull(cumulateSensorData(trackPoint, sensorPoints, (tp) -> tp.hasSensorDistance() ? tp.getSensorDistance().toM() : null));
|
||||
cumulativeDistance = Distance.ofOrNull(cumulateSensorData(trackPoint, sensorPoints, (tp) -> tp.sensorDistance() != null ? tp.sensorDistance().toM() : null));
|
||||
if (cumulativeDistance != null) {
|
||||
extensionContent += ("<opentracks:distance>" + DISTANCE_FORMAT.format(cumulativeDistance.toM()) + "</opentracks:distance>\n");
|
||||
extensionContent += ("<cluetrust:distance>" + DISTANCE_FORMAT.format(trackDistance.plus(cumulativeDistance).toM()) + "</cluetrust:distance>\n");
|
||||
|
||||
@@ -95,7 +95,7 @@ public class KMLTrackExporter implements TrackExporter {
|
||||
private final List<Float> powerList = new ArrayList<>();
|
||||
private final List<Float> cadenceList = new ArrayList<>();
|
||||
private final List<Float> heartRateList = new ArrayList<>();
|
||||
private final List<Float> altitudeGainList = new ArrayList<>();
|
||||
private final List<Float> altitudeGainList = new ArrayList<>(); //TODO Join with altitudeLossList
|
||||
private final List<Float> altitudeLossList = new ArrayList<>();
|
||||
private final List<Float> accuracyHorizontal = new ArrayList<>();
|
||||
private final List<Float> accuracyVertical = new ArrayList<>();
|
||||
@@ -167,7 +167,7 @@ public class KMLTrackExporter implements TrackExporter {
|
||||
wroteTrack = true;
|
||||
}
|
||||
|
||||
switch (trackPoint.getType()) {
|
||||
switch (trackPoint.type()) {
|
||||
case SEGMENT_START_MANUAL, SEGMENT_START_AUTOMATIC -> {
|
||||
if (wroteSegment) writeCloseSegment();
|
||||
writeOpenSegment();
|
||||
@@ -190,7 +190,7 @@ public class KMLTrackExporter implements TrackExporter {
|
||||
writeTrackPoint(track.zoneOffset(), trackPoint);
|
||||
}
|
||||
default ->
|
||||
throw new RuntimeException("Exporting this TrackPoint type is not implemented: " + trackPoint.getType());
|
||||
throw new RuntimeException("Exporting this TrackPoint type is not implemented: " + trackPoint.type());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -370,28 +370,28 @@ public class KMLTrackExporter implements TrackExporter {
|
||||
void writeTrackPoint(ZoneOffset zoneOffset, TrackPoint trackPoint) {
|
||||
printWriter.println("<when>" + getTime(zoneOffset, trackPoint.getTime()) + "</when>");
|
||||
|
||||
trackpointTypeList.add(trackPoint.getType());
|
||||
trackpointTypeList.add(trackPoint.type());
|
||||
|
||||
if (trackPoint.hasLocation()) {
|
||||
printWriter.println("<coord>" + getCoordinates(trackPoint.getPosition(), " ") + "</coord>");
|
||||
if (trackPoint.position().hasLocation()) {
|
||||
printWriter.println("<coord>" + getCoordinates(trackPoint.position(), " ") + "</coord>");
|
||||
} else {
|
||||
printWriter.println("<coord/>");
|
||||
}
|
||||
speedList.add(trackPoint.hasSpeed() ? (float) trackPoint.getSpeed().toMPS() : null);
|
||||
speedList.add(trackPoint.position().hasSpeed() ? (float) trackPoint.position().speed().toMPS() : null);
|
||||
|
||||
distanceList.add(trackPoint.hasSensorDistance() ? (float) trackPoint.getSensorDistance().toM() : null);
|
||||
heartRateList.add(trackPoint.hasHeartRate() ? trackPoint.getHeartRate().getBPM() : null);
|
||||
cadenceList.add(trackPoint.hasCadence() ? trackPoint.getCadence().getRPM() : null);
|
||||
powerList.add(trackPoint.hasPower() ? trackPoint.getPower().getW() : null);
|
||||
if (trackPoint.hasAltitudeGainLoss()) {
|
||||
altitudeGainList.add(trackPoint.getAltitudeGainLoss().loss_m());
|
||||
altitudeLossList.add(trackPoint.getAltitudeGainLoss().loss_m());
|
||||
distanceList.add(trackPoint.sensorDistance() != null ? (float) trackPoint.sensorDistance().toM() : null);
|
||||
heartRateList.add(trackPoint.heartRate() != null ? trackPoint.heartRate().getBPM() : null);
|
||||
cadenceList.add(trackPoint.cadence() != null ? trackPoint.cadence().getRPM() : null);
|
||||
powerList.add(trackPoint.power() != null ? trackPoint.power().getW() : null);
|
||||
if (trackPoint.altitudeGainLoss() != null) {
|
||||
altitudeGainList.add(trackPoint.altitudeGainLoss().loss_m());
|
||||
altitudeLossList.add(trackPoint.altitudeGainLoss().loss_m());
|
||||
} else {
|
||||
altitudeGainList.add(null);
|
||||
altitudeLossList.add(null);
|
||||
}
|
||||
accuracyHorizontal.add(trackPoint.hasHorizontalAccuracy() ? (float) trackPoint.getHorizontalAccuracy().toM() : null);
|
||||
accuracyVertical.add(trackPoint.hasVerticalAccuracy() ? (float) trackPoint.getVerticalAccuracy().toM() : null);
|
||||
accuracyHorizontal.add(trackPoint.position().hasHorizontalAccuracy() ? (float) trackPoint.position().horizontalAccuracy().toM() : null);
|
||||
accuracyVertical.add(trackPoint.position().hasVerticalAccuracy() ? (float) trackPoint.position().verticalAccuracy().toM() : null);
|
||||
}
|
||||
|
||||
private void writeSimpleArraySensorData(List<Float> list, String name) {
|
||||
|
||||
@@ -32,9 +32,13 @@ import java.util.List;
|
||||
import java.util.Locale;
|
||||
|
||||
import de.dennisguse.opentracks.data.models.Altitude;
|
||||
import de.dennisguse.opentracks.data.models.AltitudeGainLoss;
|
||||
import de.dennisguse.opentracks.data.models.Cadence;
|
||||
import de.dennisguse.opentracks.data.models.Distance;
|
||||
import de.dennisguse.opentracks.data.models.HeartRate;
|
||||
import de.dennisguse.opentracks.data.models.Marker;
|
||||
import de.dennisguse.opentracks.data.models.Position;
|
||||
import de.dennisguse.opentracks.data.models.Power;
|
||||
import de.dennisguse.opentracks.data.models.Speed;
|
||||
import de.dennisguse.opentracks.data.models.Track;
|
||||
import de.dennisguse.opentracks.data.models.TrackPoint;
|
||||
@@ -265,8 +269,8 @@ public class GPXTrackImporter extends DefaultHandler implements XMLImporter.Trac
|
||||
return;
|
||||
}
|
||||
|
||||
TrackPoint first = currentSegment.getFirst();
|
||||
first.setType(TrackPoint.Type.SEGMENT_START_AUTOMATIC);
|
||||
TrackPoint first = currentSegment.removeFirst();
|
||||
currentSegment.addFirst(first.with(TrackPoint.Type.SEGMENT_START_AUTOMATIC));
|
||||
|
||||
trackImporter.addTrackPoints(currentSegment);
|
||||
currentSegment.clear();
|
||||
@@ -332,59 +336,70 @@ public class GPXTrackImporter extends DefaultHandler implements XMLImporter.Trac
|
||||
}
|
||||
}
|
||||
|
||||
TrackPoint trackPoint = new TrackPoint(
|
||||
TrackPoint.Type.TRACKPOINT,
|
||||
new Position(
|
||||
parsedTime.toInstant(),
|
||||
latitudeParsed,
|
||||
longitudeParsed,
|
||||
accuracyHorizontalParsed,
|
||||
altitudeParsed,
|
||||
accuracyVerticalParsed,
|
||||
null,
|
||||
speedParsed
|
||||
));
|
||||
|
||||
if (heartrate != null) {
|
||||
try {
|
||||
trackPoint.setHeartRate(Float.parseFloat(heartrate));
|
||||
} catch (NumberFormatException e) {
|
||||
throw new ParsingException(createErrorMessage(String.format(Locale.US, "Unable to parse heart rate: %s", heartrate)), e);
|
||||
}
|
||||
}
|
||||
|
||||
if (cadence != null) {
|
||||
try {
|
||||
trackPoint.setCadence(Float.parseFloat(cadence));
|
||||
} catch (Exception e) {
|
||||
throw new ParsingException(createErrorMessage(String.format(Locale.US, "Unable to parse cadence: %s", cadence)), e);
|
||||
}
|
||||
}
|
||||
|
||||
if (power != null) {
|
||||
try {
|
||||
trackPoint.setPower(Float.parseFloat(power));
|
||||
} catch (NumberFormatException e) {
|
||||
throw new ParsingException(createErrorMessage(String.format(Locale.US, "Unable to parse power: %s", power)), e);
|
||||
}
|
||||
}
|
||||
|
||||
if (gain != null && loss != null) {
|
||||
try {
|
||||
trackPoint.setAltitudeGainLoss(Float.parseFloat(gain), Float.parseFloat(loss));
|
||||
} catch (NumberFormatException e) {
|
||||
throw new ParsingException(createErrorMessage(String.format(Locale.US, "Unable to parse altitude gain: %s or los: %s", gain, loss)), e);
|
||||
}
|
||||
}
|
||||
Distance parsedSensorDistance = null;
|
||||
if (sensorDistance != null) {
|
||||
try {
|
||||
trackPoint.setSensorDistance(Distance.of(Float.parseFloat(sensorDistance)));
|
||||
parsedSensorDistance = Distance.of(Float.parseFloat(sensorDistance));
|
||||
} catch (NumberFormatException e) {
|
||||
throw new ParsingException(createErrorMessage(String.format(Locale.US, "Unable to parse distance: %s", sensorDistance)), e);
|
||||
}
|
||||
}
|
||||
|
||||
return trackPoint;
|
||||
HeartRate parsedHeartRate = null;
|
||||
if (heartrate != null) {
|
||||
try {
|
||||
parsedHeartRate = HeartRate.of(Float.parseFloat(heartrate));
|
||||
} catch (NumberFormatException e) {
|
||||
throw new ParsingException(createErrorMessage(String.format(Locale.US, "Unable to parse heart rate: %s", heartrate)), e);
|
||||
}
|
||||
}
|
||||
|
||||
Cadence parsedCadence = null;
|
||||
if (cadence != null) {
|
||||
try {
|
||||
parsedCadence = Cadence.of(Float.parseFloat(cadence));
|
||||
} catch (Exception e) {
|
||||
throw new ParsingException(createErrorMessage(String.format(Locale.US, "Unable to parse cadence: %s", cadence)), e);
|
||||
}
|
||||
}
|
||||
|
||||
Power parsedPower = null;
|
||||
if (power != null) {
|
||||
try {
|
||||
parsedPower = Power.of(Float.parseFloat(power));
|
||||
} catch (NumberFormatException e) {
|
||||
throw new ParsingException(createErrorMessage(String.format(Locale.US, "Unable to parse power: %s", power)), e);
|
||||
}
|
||||
}
|
||||
|
||||
AltitudeGainLoss altitudeGainLoss = null;
|
||||
if (gain != null && loss != null) {
|
||||
try {
|
||||
altitudeGainLoss = new AltitudeGainLoss(Float.parseFloat(gain), Float.parseFloat(loss));
|
||||
} catch (NumberFormatException e) {
|
||||
throw new ParsingException(createErrorMessage(String.format(Locale.US, "Unable to parse altitude gain: %s or los: %s", gain, loss)), e);
|
||||
}
|
||||
}
|
||||
|
||||
return new TrackPoint(
|
||||
null,
|
||||
TrackPoint.Type.TRACKPOINT,
|
||||
new Position(
|
||||
parsedTime.toInstant(),
|
||||
latitudeParsed,
|
||||
longitudeParsed,
|
||||
accuracyHorizontalParsed,
|
||||
altitudeParsed,
|
||||
accuracyVerticalParsed,
|
||||
null,
|
||||
speedParsed
|
||||
),
|
||||
parsedSensorDistance,
|
||||
parsedHeartRate,
|
||||
parsedCadence,
|
||||
parsedPower,
|
||||
altitudeGainLoss
|
||||
);
|
||||
}
|
||||
|
||||
private void onTrackPointStart(Attributes attributes) {
|
||||
@@ -425,8 +440,8 @@ public class GPXTrackImporter extends DefaultHandler implements XMLImporter.Trac
|
||||
|
||||
TrackPoint trackPoint = createTrackPoint();
|
||||
|
||||
if (!trackPoint.hasLocation()) {
|
||||
Log.w(TAG, "Marker with invalid coordinates ignored: " + trackPoint.getPosition());
|
||||
if (!trackPoint.position().hasLocation()) {
|
||||
Log.w(TAG, "Marker with invalid coordinates ignored: " + trackPoint.position());
|
||||
return;
|
||||
}
|
||||
markers.add(new Marker(
|
||||
@@ -435,7 +450,7 @@ public class GPXTrackImporter extends DefaultHandler implements XMLImporter.Trac
|
||||
name != null ? name : "",
|
||||
description != null ? description : "",
|
||||
markerType != null ? markerType : "",
|
||||
trackPoint.getPosition(),
|
||||
trackPoint.position(),
|
||||
photoUrl
|
||||
));
|
||||
}
|
||||
|
||||
@@ -33,9 +33,13 @@ import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Locale;
|
||||
|
||||
import de.dennisguse.opentracks.data.models.AltitudeGainLoss;
|
||||
import de.dennisguse.opentracks.data.models.Cadence;
|
||||
import de.dennisguse.opentracks.data.models.Distance;
|
||||
import de.dennisguse.opentracks.data.models.HeartRate;
|
||||
import de.dennisguse.opentracks.data.models.Marker;
|
||||
import de.dennisguse.opentracks.data.models.Position;
|
||||
import de.dennisguse.opentracks.data.models.Power;
|
||||
import de.dennisguse.opentracks.data.models.Speed;
|
||||
import de.dennisguse.opentracks.data.models.Track;
|
||||
import de.dennisguse.opentracks.data.models.TrackPoint;
|
||||
@@ -95,10 +99,11 @@ public class KMLTrackImporter extends DefaultHandler implements XMLImporter.Trac
|
||||
private ZoneOffset zoneOffset;
|
||||
|
||||
private final ArrayList<Instant> whenList = new ArrayList<>();
|
||||
private final ArrayList<Position> positionList = new ArrayList<>();
|
||||
private final ArrayList<Position> positionList = new ArrayList<>(); //TODO This is only latitude, longitude, and altitude
|
||||
|
||||
private String dataType;
|
||||
|
||||
//TODO For ArrayList use specific data models; and why Float? Double would be easier.
|
||||
private final ArrayList<String> trackpointTypeList = new ArrayList<>();
|
||||
private final ArrayList<Float> sensorSpeedList = new ArrayList<>();
|
||||
private final ArrayList<Float> sensorDistanceList = new ArrayList<>();
|
||||
@@ -151,8 +156,7 @@ public class KMLTrackImporter extends DefaultHandler implements XMLImporter.Trac
|
||||
}
|
||||
onTrackSegmentStart();
|
||||
}
|
||||
case TAG_EXTENDED_DATA, TAG_SIMPLE_ARRAY_DATA, TAG_KML22_SIMPLE_ARRAY_DATA ->
|
||||
dataType = attributes.getValue(ATTRIBUTE_NAME);
|
||||
case TAG_EXTENDED_DATA, TAG_SIMPLE_ARRAY_DATA, TAG_KML22_SIMPLE_ARRAY_DATA -> dataType = attributes.getValue(ATTRIBUTE_NAME);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -317,62 +321,77 @@ public class KMLTrackImporter extends DefaultHandler implements XMLImporter.Trac
|
||||
for (int i = 0; i < positionList.size(); i++) {
|
||||
Instant time = whenList.get(i);
|
||||
Position position = positionList.get(i);
|
||||
|
||||
TrackPoint trackPoint;
|
||||
if (position == null) {
|
||||
trackPoint = new TrackPoint(TrackPoint.Type.TRACKPOINT, Position.of(time));
|
||||
position = Position.of(time);
|
||||
} else {
|
||||
trackPoint = new TrackPoint(TrackPoint.Type.TRACKPOINT, position.with(time));
|
||||
position = position.with(time);
|
||||
}
|
||||
|
||||
TrackPoint.Type type = null;
|
||||
Speed speed = null;
|
||||
Distance sensorDistance = null;
|
||||
HeartRate heartRate = null;
|
||||
Cadence cadence = null;
|
||||
Power power = null;
|
||||
AltitudeGainLoss altitudeGainLoss = null;
|
||||
Distance horizontalAccuracy = null;
|
||||
Distance verticalAccuracy = null;
|
||||
|
||||
if (i < trackpointTypeList.size() && trackpointTypeList.get(i) != null) {
|
||||
TrackPoint.Type type = TrackPoint.Type.valueOf(trackpointTypeList.get(i));
|
||||
trackPoint.setType(type);
|
||||
type = TrackPoint.Type.valueOf(trackpointTypeList.get(i));
|
||||
}
|
||||
|
||||
if (i < sensorSpeedList.size() && sensorSpeedList.get(i) != null) {
|
||||
trackPoint.setSpeed(Speed.of(sensorSpeedList.get(i)));
|
||||
speed = Speed.of(sensorSpeedList.get(i));
|
||||
}
|
||||
if (i < sensorDistanceList.size() && sensorDistanceList.get(i) != null) {
|
||||
trackPoint.setSensorDistance(Distance.of(sensorDistanceList.get(i)));
|
||||
sensorDistance = Distance.of(sensorDistanceList.get(i));
|
||||
}
|
||||
if (i < sensorHeartRateList.size() && sensorHeartRateList.get(i) != null) {
|
||||
trackPoint.setHeartRate(sensorHeartRateList.get(i));
|
||||
if (i < sensorHeartRateList.size()) {
|
||||
heartRate = HeartRate.ofOrNull(sensorHeartRateList.get(i));
|
||||
}
|
||||
if (i < sensorCadenceList.size() && sensorCadenceList.get(i) != null) {
|
||||
trackPoint.setCadence(sensorCadenceList.get(i));
|
||||
cadence = Cadence.of(sensorCadenceList.get(i));
|
||||
}
|
||||
if (i < sensorPowerList.size() && sensorPowerList.get(i) != null) {
|
||||
trackPoint.setPower(sensorPowerList.get(i));
|
||||
power = Power.of(sensorPowerList.get(i));
|
||||
}
|
||||
if (i < altitudeGainList.size() && i < altitudeLossList.size()) {
|
||||
if (altitudeGainList.get(i) != null && altitudeLossList.get(i) != null) {
|
||||
trackPoint.setAltitudeGainLoss(altitudeGainList.get(i), altitudeLossList.get(i));
|
||||
}
|
||||
if ((i < altitudeGainList.size() && altitudeGainList.get(i) != null)
|
||||
&& (i < altitudeLossList.size() && altitudeLossList.get(i) != null)) {
|
||||
altitudeGainLoss = new AltitudeGainLoss(altitudeGainList.get(i), altitudeLossList.get(i));
|
||||
}
|
||||
if (i < accuracyHorizontal.size() && accuracyHorizontal.get(i) != null) {
|
||||
trackPoint.setHorizontalAccuracy(Distance.of(accuracyHorizontal.get(i)));
|
||||
horizontalAccuracy = Distance.of(accuracyHorizontal.get(i));
|
||||
}
|
||||
if (i < accuracyVertical.size() && accuracyVertical.get(i) != null) {
|
||||
trackPoint.setVerticalAccuracy(Distance.of(accuracyVertical.get(i)));
|
||||
verticalAccuracy = Distance.of(accuracyVertical.get(i));
|
||||
}
|
||||
|
||||
// Update TrackPoint type for START / STOP.
|
||||
TrackPoint.Type type = trackPoint.getType();
|
||||
if (i == 0) {
|
||||
//first
|
||||
if (!trackPoint.wasCreatedManually()) {
|
||||
type = TrackPoint.Type.SEGMENT_START_MANUAL;
|
||||
} else {
|
||||
if (wasCreatedAutomatically(position)) {
|
||||
type = TrackPoint.Type.SEGMENT_START_AUTOMATIC;
|
||||
} else {
|
||||
type = TrackPoint.Type.SEGMENT_START_MANUAL;
|
||||
}
|
||||
} else if (i == positionList.size() - 1 && !trackPoint.wasCreatedManually()) {
|
||||
} else if (i == positionList.size() - 1 && !wasCreatedAutomatically(position)) {
|
||||
//last
|
||||
type = TrackPoint.Type.SEGMENT_END_MANUAL;
|
||||
}
|
||||
trackPoint.setType(type);
|
||||
|
||||
trackImporter.addTrackPoint(trackPoint);
|
||||
trackImporter.addTrackPoint(new TrackPoint(
|
||||
null,
|
||||
type != null ? type : TrackPoint.Type.TRACKPOINT,
|
||||
position
|
||||
.with(speed)
|
||||
.withHorizontalAccuracy(horizontalAccuracy)
|
||||
.withVerticalAccuracy(verticalAccuracy),
|
||||
sensorDistance,
|
||||
heartRate,
|
||||
cadence,
|
||||
power,
|
||||
altitudeGainLoss
|
||||
));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -392,6 +411,7 @@ public class KMLTrackImporter extends DefaultHandler implements XMLImporter.Trac
|
||||
}
|
||||
|
||||
private Position createPosition(String latitude, String longitude, String altitude) {
|
||||
//TODO Do not use Location.
|
||||
if (longitude != null && latitude != null) {
|
||||
Location location = new Location("import");
|
||||
try {
|
||||
@@ -439,12 +459,9 @@ public class KMLTrackImporter extends DefaultHandler implements XMLImporter.Trac
|
||||
case KMLTrackExporter.EXTENDED_DATA_TYPE_CADENCE -> sensorCadenceList.add(value);
|
||||
case KMLTrackExporter.EXTENDED_DATA_TYPE_ALTITUDE_GAIN -> altitudeGainList.add(value);
|
||||
case KMLTrackExporter.EXTENDED_DATA_TYPE_ALTITUDE_LOSS -> altitudeLossList.add(value);
|
||||
case KMLTrackExporter.EXTENDED_DATA_TYPE_ACCURACY_HORIZONTAL ->
|
||||
accuracyHorizontal.add(value);
|
||||
case KMLTrackExporter.EXTENDED_DATA_TYPE_ACCURACY_VERTICAL ->
|
||||
accuracyVertical.add(value);
|
||||
default ->
|
||||
Log.w(TAG, "Data from extended data " + dataType + " is not (yet) supported.");
|
||||
case KMLTrackExporter.EXTENDED_DATA_TYPE_ACCURACY_HORIZONTAL -> accuracyHorizontal.add(value);
|
||||
case KMLTrackExporter.EXTENDED_DATA_TYPE_ACCURACY_VERTICAL -> accuracyVertical.add(value);
|
||||
default -> Log.w(TAG, "Data from extended data " + dataType + " is not (yet) supported.");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -457,6 +474,10 @@ public class KMLTrackImporter extends DefaultHandler implements XMLImporter.Trac
|
||||
trackImporter.finish();
|
||||
}
|
||||
|
||||
public boolean wasCreatedAutomatically(Position position) {
|
||||
return position.hasLocation() || position.hasSpeed();
|
||||
}
|
||||
|
||||
@Override
|
||||
public DefaultHandler getHandler() {
|
||||
return this;
|
||||
|
||||
@@ -176,21 +176,19 @@ public class TrackImporter {
|
||||
TrackPoint previous = trackPoints.get(i - 1);
|
||||
TrackPoint current = trackPoints.get(i);
|
||||
|
||||
if (current.hasSensorDistance() || (previous.hasLocation() && current.hasLocation())) {
|
||||
if (current.sensorDistance() != null || (previous.position().hasLocation() && current.position().hasLocation())) {
|
||||
TrackPoint newCurrent = current;
|
||||
|
||||
Distance distanceToPrevious = current.distanceToPrevious(previous);
|
||||
if (!current.hasSpeed()) {
|
||||
if (!current.position().hasSpeed()) {
|
||||
Duration timeDifference = Duration.between(previous.getTime(), current.getTime());
|
||||
current.setSpeed(Speed.of(distanceToPrevious, timeDifference));
|
||||
newCurrent = newCurrent.with(newCurrent.position().with(Speed.of(distanceToPrevious, timeDifference)));
|
||||
}
|
||||
|
||||
if (!current.hasBearing()) {
|
||||
previous.bearingTo(current)
|
||||
.ifPresent(current::setBearing);
|
||||
}
|
||||
|
||||
if (current.getType().equals(TrackPoint.Type.TRACKPOINT) && distanceToPrevious.greaterThan(maxRecordingDistance)) {
|
||||
current.setType(TrackPoint.Type.SEGMENT_START_AUTOMATIC);
|
||||
if (current.type().equals(TrackPoint.Type.TRACKPOINT) && distanceToPrevious.greaterThan(maxRecordingDistance)) {
|
||||
newCurrent = newCurrent.with(TrackPoint.Type.SEGMENT_START_AUTOMATIC);
|
||||
}
|
||||
trackPoints.set(i, newCurrent);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -124,20 +124,20 @@ public class GpsManager implements SensorConnector, LocationListenerCompat, GpsS
|
||||
@Override
|
||||
public void onLocationChanged(@NonNull Location location) {
|
||||
// Send each update to the status; please note that this TrackPoint is not stored.
|
||||
TrackPoint trackPoint = new TrackPoint(TrackPoint.Type.TRACKPOINT, Position.of(location, trackPointCreator.createNow()));
|
||||
gpsStatusManager.onNewTrackPoint(trackPoint);
|
||||
Position position = Position.of(location, trackPointCreator.createNow());
|
||||
gpsStatusManager.onNewTrackPoint(new TrackPoint(TrackPoint.Type.TRACKPOINT, position));
|
||||
|
||||
if (!trackPoint.getPosition().hasValidLocation()) {
|
||||
Log.w(TAG, "Ignore newTrackPoint. location is invalid.");
|
||||
if (!position.hasValidLocation()) {
|
||||
Log.w(TAG, "Ignore newTrackPoint. Location is invalid.");
|
||||
return;
|
||||
}
|
||||
|
||||
if (!trackPoint.getPosition().fulfillsAccuracy(thresholdHorizontalAccuracy)) {
|
||||
if (!position.fulfillsAccuracy(thresholdHorizontalAccuracy)) {
|
||||
Log.d(TAG, "Ignore newTrackPoint. Poor accuracy.");
|
||||
return;
|
||||
}
|
||||
|
||||
observer.onChange(new Raw<>(observer.getNow(), Position.of(location)));
|
||||
observer.onChange(new Raw<>(observer.getNow(), position));
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -18,6 +18,7 @@ import de.dennisguse.opentracks.settings.PreferencesUtils;
|
||||
/**
|
||||
* This class handle GPS status according to received locations and some thresholds.
|
||||
*/
|
||||
//TODO Use Position instead of TrackPoint.
|
||||
class GpsStatusManager {
|
||||
|
||||
private static final String TAG = GpsStatusManager.class.getSimpleName();
|
||||
@@ -100,7 +101,7 @@ class GpsStatusManager {
|
||||
//TODO use MonotonicClock instead of Instant.now()
|
||||
@VisibleForTesting
|
||||
void determineGpsStatusOnTrackpoint(@NonNull TrackPoint lastTrackPoint) {
|
||||
if (lastTrackPoint.fulfillsAccuracy(horizontalAccuracyThreshold)) {
|
||||
if (lastTrackPoint.position().fulfillsAccuracy(horizontalAccuracyThreshold)) {
|
||||
if (gpsStatus != GpsStatusValue.GPS_SIGNAL_FIX) {
|
||||
setGpsStatus(GpsStatusValue.GPS_SIGNAL_FIX);
|
||||
scheduleTimer(); //TODO
|
||||
|
||||
@@ -11,9 +11,9 @@ import androidx.annotation.VisibleForTesting;
|
||||
|
||||
import java.time.Instant;
|
||||
|
||||
import de.dennisguse.opentracks.data.models.TrackPoint;
|
||||
import de.dennisguse.opentracks.sensors.sensorData.Aggregator;
|
||||
import de.dennisguse.opentracks.sensors.sensorData.Raw;
|
||||
import de.dennisguse.opentracks.sensors.sensorData.SensorDataAggregator;
|
||||
import de.dennisguse.opentracks.sensors.sensorData.SensorDataSet;
|
||||
import de.dennisguse.opentracks.services.handlers.TrackPointCreator;
|
||||
import de.dennisguse.opentracks.util.SystemUtils;
|
||||
@@ -24,7 +24,7 @@ public class SensorManager implements SharedPreferences.OnSharedPreferenceChange
|
||||
|
||||
//TODO Should be final and not be visible for testing
|
||||
@VisibleForTesting
|
||||
public SensorDataSet sensorDataSet;
|
||||
public SensorDataAggregator sensorDataAggregator;
|
||||
|
||||
private PowerManager.WakeLock wakeLock;
|
||||
|
||||
@@ -34,23 +34,23 @@ public class SensorManager implements SharedPreferences.OnSharedPreferenceChange
|
||||
|
||||
@Override
|
||||
public void onConnect(Aggregator<?, ?> aggregator) {
|
||||
sensorDataSet.add(aggregator);
|
||||
sensorDataAggregator.add(aggregator);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onChange(Raw<?> data) {
|
||||
sensorDataSet.update(data);
|
||||
observer.onChange(new SensorDataSet(sensorDataSet));
|
||||
sensorDataAggregator.update(data);
|
||||
observer.onChange();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onDisconnect(Aggregator<?, ?> aggregator) {
|
||||
sensorDataSet.add(aggregator);
|
||||
sensorDataAggregator.add(aggregator);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onRemove(Aggregator<?, ?> aggregator) {
|
||||
sensorDataSet.remove(aggregator);
|
||||
sensorDataAggregator.remove(aggregator);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -67,7 +67,7 @@ public class SensorManager implements SharedPreferences.OnSharedPreferenceChange
|
||||
|
||||
public SensorManager(TrackPointCreator observer) {
|
||||
this.observer = observer;
|
||||
this.sensorDataSet = new SensorDataSet(observer);
|
||||
this.sensorDataAggregator = new SensorDataAggregator();
|
||||
}
|
||||
|
||||
public void start(Context context, Handler handler) {
|
||||
@@ -100,12 +100,11 @@ public class SensorManager implements SharedPreferences.OnSharedPreferenceChange
|
||||
gpsManager.stop(context);
|
||||
gpsManager = null;
|
||||
|
||||
sensorDataSet.clear();
|
||||
sensorDataAggregator.clear();
|
||||
}
|
||||
|
||||
public SensorDataSet fill(TrackPoint trackPoint) {
|
||||
sensorDataSet.fillTrackPoint(trackPoint);
|
||||
return new SensorDataSet(sensorDataSet);
|
||||
public SensorDataSet getSensorDataSet(Instant now) {
|
||||
return sensorDataAggregator.getSensorData(now);
|
||||
}
|
||||
|
||||
public void reset() {
|
||||
@@ -113,7 +112,7 @@ public class SensorManager implements SharedPreferences.OnSharedPreferenceChange
|
||||
Log.d(TAG, "No recording running and no reset necessary.");
|
||||
return;
|
||||
}
|
||||
sensorDataSet.reset();
|
||||
sensorDataAggregator.reset();
|
||||
}
|
||||
|
||||
@VisibleForTesting
|
||||
|
||||
@@ -1,7 +1,5 @@
|
||||
package de.dennisguse.opentracks.sensors.sensorData;
|
||||
|
||||
import android.util.Pair;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
|
||||
import java.time.Instant;
|
||||
@@ -61,8 +59,8 @@ public abstract class Aggregator<Input, Output> {
|
||||
}
|
||||
|
||||
@NonNull
|
||||
public Pair<Output, String> getAggregatedValueWithSensorName(Instant now) {
|
||||
return new Pair<>(getAggregatedValue(now), getSensorNameOrAddress());
|
||||
public SensorData<Output> getAggregatedValueWithSensorName(Instant now) {
|
||||
return new SensorData<>(getAggregatedValue(now), getSensorNameOrAddress());
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -2,6 +2,8 @@ package de.dennisguse.opentracks.sensors.sensorData;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
|
||||
import java.time.Instant;
|
||||
|
||||
import de.dennisguse.opentracks.data.models.Position;
|
||||
|
||||
public class AggregatorGPS extends Aggregator<Position, Position> {
|
||||
@@ -36,4 +38,11 @@ public class AggregatorGPS extends Aggregator<Position, Position> {
|
||||
protected Position getNoneValue() {
|
||||
return Position.empty();
|
||||
}
|
||||
|
||||
@NonNull
|
||||
@Override
|
||||
public Position getAggregatedValue(Instant now) {
|
||||
return super.getAggregatedValue(now)
|
||||
.with(now);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,9 @@
|
||||
package de.dennisguse.opentracks.sensors.sensorData;
|
||||
|
||||
public record SensorData<T>(
|
||||
|
||||
T data,
|
||||
|
||||
String sensorNameOrAddress
|
||||
) {
|
||||
}
|
||||
@@ -0,0 +1,212 @@
|
||||
package de.dennisguse.opentracks.sensors.sensorData;
|
||||
|
||||
import android.util.Log;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.annotation.Nullable;
|
||||
import androidx.annotation.VisibleForTesting;
|
||||
|
||||
import java.time.Instant;
|
||||
|
||||
import de.dennisguse.opentracks.data.models.AtmosphericPressure;
|
||||
import de.dennisguse.opentracks.data.models.Cadence;
|
||||
import de.dennisguse.opentracks.data.models.Distance;
|
||||
import de.dennisguse.opentracks.data.models.HeartRate;
|
||||
import de.dennisguse.opentracks.data.models.Position;
|
||||
import de.dennisguse.opentracks.data.models.Speed;
|
||||
import de.dennisguse.opentracks.sensors.BluetoothHandlerCyclingCadence;
|
||||
import de.dennisguse.opentracks.sensors.BluetoothHandlerCyclingDistanceSpeed;
|
||||
import de.dennisguse.opentracks.sensors.BluetoothHandlerManagerCyclingPower;
|
||||
import de.dennisguse.opentracks.sensors.BluetoothHandlerRunningSpeedAndCadence;
|
||||
import de.dennisguse.opentracks.settings.PreferencesUtils;
|
||||
|
||||
public class SensorDataAggregator {
|
||||
|
||||
private static final String TAG = SensorDataAggregator.class.getSimpleName();
|
||||
|
||||
@VisibleForTesting
|
||||
public AggregatorHeartRate heartRate;
|
||||
|
||||
@VisibleForTesting
|
||||
public AggregatorCyclingCadence cyclingCadence;
|
||||
|
||||
@VisibleForTesting
|
||||
public AggregatorCyclingDistanceSpeed cyclingDistanceSpeed;
|
||||
|
||||
@VisibleForTesting
|
||||
public AggregatorCyclingPower cyclingPower;
|
||||
|
||||
@VisibleForTesting
|
||||
public AggregatorRunning runningDistanceSpeedCadence;
|
||||
|
||||
@VisibleForTesting
|
||||
public AggregatorBarometer barometer;
|
||||
|
||||
public AggregatorGPS gps;
|
||||
|
||||
@VisibleForTesting
|
||||
public SensorData<HeartRate> getHeartRate(Instant now) {
|
||||
if (heartRate != null) {
|
||||
SensorData<HeartRate> value = heartRate.getAggregatedValueWithSensorName(now);
|
||||
if (value.data().isValid()) return value;
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
private SensorData<Cadence> getCadence(Instant now) {
|
||||
if (cyclingCadence != null) {
|
||||
return cyclingCadence.getAggregatedValueWithSensorName(now);
|
||||
}
|
||||
|
||||
if (runningDistanceSpeedCadence != null && runningDistanceSpeedCadence.hasReceivedData() && runningDistanceSpeedCadence.getAggregatedValue(now).cadence() != null) {
|
||||
return new SensorData<>(runningDistanceSpeedCadence.aggregatedValue.cadence(), runningDistanceSpeedCadence.getSensorNameOrAddress());
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
private SensorData<Distance> getDistance(Instant now) {
|
||||
if (cyclingDistanceSpeed != null && cyclingDistanceSpeed.hasReceivedData()) {
|
||||
return new SensorData<>(cyclingDistanceSpeed.getAggregatedValue(now).distanceOverall(), cyclingDistanceSpeed.getSensorNameOrAddress());
|
||||
}
|
||||
|
||||
if (runningDistanceSpeedCadence != null && runningDistanceSpeedCadence.hasReceivedData()) {
|
||||
return new SensorData<>(runningDistanceSpeedCadence.getAggregatedValue(now).distance(), runningDistanceSpeedCadence.getSensorNameOrAddress());
|
||||
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
//TOOD simplify?
|
||||
private SensorData<Speed> getSpeed(Instant now) {
|
||||
|
||||
if (cyclingDistanceSpeed != null && cyclingDistanceSpeed.hasReceivedData() && cyclingDistanceSpeed.getAggregatedValue(now).speed() != null) {
|
||||
return new SensorData<>(cyclingDistanceSpeed.getAggregatedValue(now).speed(), cyclingDistanceSpeed.getSensorNameOrAddress());
|
||||
}
|
||||
|
||||
if (runningDistanceSpeedCadence != null && runningDistanceSpeedCadence.hasReceivedData() && runningDistanceSpeedCadence.getAggregatedValue(now).speed() != null) {
|
||||
return new SensorData<>(runningDistanceSpeedCadence.aggregatedValue.speed(), runningDistanceSpeedCadence.getSensorNameOrAddress());
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
public void add(@NonNull Aggregator<?, ?> data) {
|
||||
set(data, data);
|
||||
}
|
||||
|
||||
public void update(@NonNull Raw<?> data) {
|
||||
Object value = data.value();
|
||||
|
||||
if (value instanceof HeartRate) {
|
||||
this.heartRate.add((Raw<HeartRate>) data);
|
||||
return;
|
||||
}
|
||||
|
||||
if (value instanceof BluetoothHandlerCyclingCadence.CrankData) {
|
||||
this.cyclingCadence.add((Raw<BluetoothHandlerCyclingCadence.CrankData>) data);
|
||||
return;
|
||||
}
|
||||
if (value instanceof BluetoothHandlerCyclingDistanceSpeed.WheelData) {
|
||||
this.cyclingDistanceSpeed.setWheelCircumference(PreferencesUtils.getWheelCircumference()); //TODO Fetch once and then listen for changes.
|
||||
this.cyclingDistanceSpeed.add((Raw<BluetoothHandlerCyclingDistanceSpeed.WheelData>) data);
|
||||
return;
|
||||
}
|
||||
if (value instanceof BluetoothHandlerRunningSpeedAndCadence.Data) {
|
||||
this.runningDistanceSpeedCadence.add((Raw<BluetoothHandlerRunningSpeedAndCadence.Data>) data);
|
||||
|
||||
return;
|
||||
}
|
||||
if (value instanceof BluetoothHandlerManagerCyclingPower.Data) {
|
||||
this.cyclingPower.add((Raw<BluetoothHandlerManagerCyclingPower.Data>) data);
|
||||
return;
|
||||
}
|
||||
if (value instanceof AtmosphericPressure) {
|
||||
this.barometer.add((Raw<AtmosphericPressure>) data);
|
||||
return;
|
||||
}
|
||||
if (value instanceof Position) {
|
||||
this.gps.add((Raw<Position>) data);
|
||||
return;
|
||||
}
|
||||
|
||||
throw new UnsupportedOperationException(data.getClass().getCanonicalName() + " " + data.value().getClass().getCanonicalName());
|
||||
}
|
||||
|
||||
public void remove(@NonNull Aggregator<?, ?> type) {
|
||||
set(type, null);
|
||||
}
|
||||
|
||||
public void clear() {
|
||||
Log.i(TAG, "Removing all aggregators");
|
||||
this.heartRate = null;
|
||||
this.cyclingCadence = null;
|
||||
this.cyclingDistanceSpeed = null;
|
||||
this.cyclingPower = null;
|
||||
this.runningDistanceSpeedCadence = null;
|
||||
this.barometer = null;
|
||||
this.gps = null;
|
||||
}
|
||||
|
||||
public SensorDataSet getSensorData(Instant now) {
|
||||
return new SensorDataSet(
|
||||
gps != null && gps.hasReceivedData() ? gps.getAggregatedValueWithSensorName(now) : new SensorData<>(Position.of(now), ""),
|
||||
getSpeed(now),
|
||||
getDistance(now),
|
||||
getHeartRate(now),
|
||||
getCadence(now),
|
||||
cyclingPower != null && cyclingPower.hasReceivedData() ? cyclingPower.getAggregatedValueWithSensorName(now) : null,
|
||||
barometer != null && barometer.hasReceivedData() ? barometer.getAggregatedValueWithSensorName(now) : null
|
||||
);
|
||||
}
|
||||
|
||||
public void reset() {
|
||||
Log.i(TAG, "Resetting data");
|
||||
|
||||
if (heartRate != null) heartRate.resetAggregated();
|
||||
if (cyclingCadence != null) cyclingCadence.resetAggregated();
|
||||
if (cyclingDistanceSpeed != null) cyclingDistanceSpeed.resetAggregated();
|
||||
if (cyclingPower != null) cyclingPower.resetAggregated();
|
||||
if (runningDistanceSpeedCadence != null) runningDistanceSpeedCadence.resetAggregated();
|
||||
if (barometer != null) barometer.resetAggregated();
|
||||
if (gps != null) gps.resetAggregated();
|
||||
}
|
||||
|
||||
//TODO Refactor generic "fun" here: go explicit.
|
||||
private void set(@NonNull Aggregator<?, ?> type, @Nullable Aggregator<?, ?> sensorData) {
|
||||
Log.i(TAG, "Setting aggregator " + type.getClass().getCanonicalName() + " to " + sensorData);
|
||||
|
||||
if (type instanceof AggregatorHeartRate) {
|
||||
heartRate = (AggregatorHeartRate) sensorData;
|
||||
return;
|
||||
}
|
||||
if (type instanceof AggregatorCyclingCadence) {
|
||||
cyclingCadence = (AggregatorCyclingCadence) sensorData;
|
||||
return;
|
||||
}
|
||||
if (type instanceof AggregatorCyclingDistanceSpeed) {
|
||||
cyclingDistanceSpeed = (AggregatorCyclingDistanceSpeed) sensorData;
|
||||
return;
|
||||
}
|
||||
if (type instanceof AggregatorCyclingPower) {
|
||||
cyclingPower = (AggregatorCyclingPower) sensorData;
|
||||
return;
|
||||
}
|
||||
if (type instanceof AggregatorRunning) {
|
||||
runningDistanceSpeedCadence = (AggregatorRunning) sensorData;
|
||||
return;
|
||||
}
|
||||
if (type instanceof AggregatorBarometer) {
|
||||
barometer = (AggregatorBarometer) sensorData;
|
||||
return;
|
||||
}
|
||||
if (type instanceof AggregatorGPS) {
|
||||
gps = (AggregatorGPS) sensorData;
|
||||
return;
|
||||
}
|
||||
|
||||
throw new UnsupportedOperationException(type.getClass().getCanonicalName());
|
||||
}
|
||||
}
|
||||
@@ -1,240 +1,30 @@
|
||||
package de.dennisguse.opentracks.sensors.sensorData;
|
||||
|
||||
import android.util.Log;
|
||||
import android.util.Pair;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.annotation.Nullable;
|
||||
import androidx.annotation.VisibleForTesting;
|
||||
|
||||
import de.dennisguse.opentracks.data.models.AtmosphericPressure;
|
||||
import de.dennisguse.opentracks.data.models.AltitudeGainLoss;
|
||||
import de.dennisguse.opentracks.data.models.Cadence;
|
||||
import de.dennisguse.opentracks.data.models.Distance;
|
||||
import de.dennisguse.opentracks.data.models.HeartRate;
|
||||
import de.dennisguse.opentracks.data.models.Position;
|
||||
import de.dennisguse.opentracks.data.models.Power;
|
||||
import de.dennisguse.opentracks.data.models.Speed;
|
||||
import de.dennisguse.opentracks.data.models.TrackPoint;
|
||||
import de.dennisguse.opentracks.sensors.BluetoothHandlerCyclingCadence;
|
||||
import de.dennisguse.opentracks.sensors.BluetoothHandlerCyclingDistanceSpeed;
|
||||
import de.dennisguse.opentracks.sensors.BluetoothHandlerManagerCyclingPower;
|
||||
import de.dennisguse.opentracks.sensors.BluetoothHandlerRunningSpeedAndCadence;
|
||||
import de.dennisguse.opentracks.services.handlers.TrackPointCreator;
|
||||
import de.dennisguse.opentracks.settings.PreferencesUtils;
|
||||
|
||||
public class SensorDataSet {
|
||||
|
||||
private static final String TAG = SensorDataSet.class.getSimpleName();
|
||||
|
||||
@VisibleForTesting
|
||||
public AggregatorHeartRate heartRate;
|
||||
|
||||
@VisibleForTesting
|
||||
public AggregatorCyclingCadence cyclingCadence;
|
||||
|
||||
@VisibleForTesting
|
||||
public AggregatorCyclingDistanceSpeed cyclingDistanceSpeed;
|
||||
|
||||
@VisibleForTesting
|
||||
public AggregatorCyclingPower cyclingPower;
|
||||
|
||||
@VisibleForTesting
|
||||
public AggregatorRunning runningDistanceSpeedCadence;
|
||||
|
||||
@VisibleForTesting
|
||||
public AggregatorBarometer barometer;
|
||||
|
||||
public AggregatorGPS gps;
|
||||
|
||||
private final TrackPointCreator trackPointCreator;
|
||||
|
||||
public SensorDataSet(TrackPointCreator trackPointCreator) {
|
||||
this.trackPointCreator = trackPointCreator;
|
||||
}
|
||||
|
||||
@Deprecated //TODO This is not a copy constructor anymore, but it should be - aggregators are no value objects; best guess: can be removed
|
||||
public SensorDataSet(SensorDataSet toCopy) {
|
||||
this.trackPointCreator = toCopy.trackPointCreator;
|
||||
this.heartRate = toCopy.heartRate;
|
||||
this.cyclingCadence = toCopy.cyclingCadence;
|
||||
this.cyclingDistanceSpeed = toCopy.cyclingDistanceSpeed;
|
||||
this.cyclingPower = toCopy.cyclingPower;
|
||||
this.runningDistanceSpeedCadence = toCopy.runningDistanceSpeedCadence;
|
||||
this.barometer = toCopy.barometer;
|
||||
this.gps = toCopy.gps;
|
||||
}
|
||||
|
||||
public Pair<HeartRate, String> getHeartRate() {
|
||||
if (heartRate != null) {
|
||||
Pair<HeartRate, String> value = heartRate.getAggregatedValueWithSensorName(trackPointCreator.createNow());
|
||||
if (value.first.isValid()) return value;
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
public Pair<Cadence, String> getCadence() {
|
||||
if (cyclingCadence != null) {
|
||||
return cyclingCadence.getAggregatedValueWithSensorName(trackPointCreator.createNow());
|
||||
}
|
||||
|
||||
if (runningDistanceSpeedCadence != null && runningDistanceSpeedCadence.hasReceivedData() && runningDistanceSpeedCadence.aggregatedValue.cadence() != null) {
|
||||
return new Pair<>(runningDistanceSpeedCadence.aggregatedValue.cadence(), runningDistanceSpeedCadence.getSensorNameOrAddress());
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
public Pair<Speed, String> getSpeed() {
|
||||
if (cyclingDistanceSpeed != null && cyclingDistanceSpeed.hasReceivedData() && cyclingDistanceSpeed.getAggregatedValue(trackPointCreator.createNow()).speed() != null) {
|
||||
return new Pair<>(cyclingDistanceSpeed.getAggregatedValue(trackPointCreator.createNow()).speed(), cyclingDistanceSpeed.getSensorNameOrAddress());
|
||||
}
|
||||
|
||||
if (runningDistanceSpeedCadence != null && runningDistanceSpeedCadence.hasReceivedData() && runningDistanceSpeedCadence.getAggregatedValue(trackPointCreator.createNow()).speed() != null) {
|
||||
return new Pair<>(runningDistanceSpeedCadence.aggregatedValue.speed(), runningDistanceSpeedCadence.getSensorNameOrAddress());
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
public AggregatorCyclingPower getCyclingPower() {
|
||||
return cyclingPower;
|
||||
}
|
||||
|
||||
public void add(@NonNull Aggregator<?, ?> data) {
|
||||
set(data, data);
|
||||
}
|
||||
|
||||
public void update(@NonNull Raw<?> data) {
|
||||
Object value = data.value();
|
||||
|
||||
if (value instanceof HeartRate) {
|
||||
this.heartRate.add((Raw<HeartRate>) data);
|
||||
return;
|
||||
}
|
||||
|
||||
if (value instanceof BluetoothHandlerCyclingCadence.CrankData) {
|
||||
this.cyclingCadence.add((Raw<BluetoothHandlerCyclingCadence.CrankData>) data);
|
||||
return;
|
||||
}
|
||||
if (value instanceof BluetoothHandlerCyclingDistanceSpeed.WheelData ) {
|
||||
this.cyclingDistanceSpeed.setWheelCircumference(PreferencesUtils.getWheelCircumference()); //TODO Fetch once and then listen for changes.
|
||||
this.cyclingDistanceSpeed.add((Raw<BluetoothHandlerCyclingDistanceSpeed.WheelData>) data);
|
||||
return;
|
||||
}
|
||||
if (value instanceof BluetoothHandlerRunningSpeedAndCadence.Data) {
|
||||
this.runningDistanceSpeedCadence.add((Raw<BluetoothHandlerRunningSpeedAndCadence.Data>) data);
|
||||
|
||||
return;
|
||||
}
|
||||
if (value instanceof BluetoothHandlerManagerCyclingPower.Data) {
|
||||
this.cyclingPower.add((Raw<BluetoothHandlerManagerCyclingPower.Data>) data);
|
||||
return;
|
||||
}
|
||||
if (value instanceof AtmosphericPressure) {
|
||||
this.barometer.add((Raw<AtmosphericPressure>) data);
|
||||
return;
|
||||
}
|
||||
if (value instanceof Position) {
|
||||
this.gps.add((Raw<Position>) data);
|
||||
return;
|
||||
}
|
||||
|
||||
throw new UnsupportedOperationException(data.getClass().getCanonicalName() + " " + data.value().getClass().getCanonicalName());
|
||||
}
|
||||
|
||||
public void remove(@NonNull Aggregator<?, ?> type) {
|
||||
set(type, null);
|
||||
}
|
||||
|
||||
public void clear() {
|
||||
Log.i(TAG, "Removing all aggregators");
|
||||
this.heartRate = null;
|
||||
this.cyclingCadence = null;
|
||||
this.cyclingDistanceSpeed = null;
|
||||
this.cyclingPower = null;
|
||||
this.runningDistanceSpeedCadence = null;
|
||||
this.barometer = null;
|
||||
this.gps = null;
|
||||
}
|
||||
|
||||
public void fillTrackPoint(TrackPoint trackPoint) {
|
||||
if (gps != null && gps.hasReceivedData()) {
|
||||
trackPoint.setPosition(gps.getAggregatedValue(trackPointCreator.createNow()));
|
||||
}
|
||||
|
||||
Pair<HeartRate, String> heartRate = getHeartRate();
|
||||
if (heartRate != null) {
|
||||
trackPoint.setHeartRate(heartRate.first);
|
||||
}
|
||||
|
||||
if (getCadence() != null) {
|
||||
trackPoint.setCadence(getCadence().first);
|
||||
}
|
||||
|
||||
if (getSpeed() != null) {
|
||||
trackPoint.setSpeed(getSpeed().first);
|
||||
}
|
||||
|
||||
if (cyclingDistanceSpeed != null && cyclingDistanceSpeed.hasReceivedData()) {
|
||||
trackPoint.setSensorDistance(cyclingDistanceSpeed.getAggregatedValue(trackPointCreator.createNow()).distanceOverall());
|
||||
}
|
||||
|
||||
if (cyclingPower != null && cyclingPower.hasReceivedData()) {
|
||||
trackPoint.setPower(cyclingPower.getAggregatedValue(trackPointCreator.createNow()));
|
||||
}
|
||||
|
||||
if (runningDistanceSpeedCadence != null && runningDistanceSpeedCadence.hasReceivedData()) {
|
||||
trackPoint.setSensorDistance(runningDistanceSpeedCadence.getAggregatedValue(trackPointCreator.createNow()).distance());
|
||||
}
|
||||
|
||||
if (barometer != null && barometer.hasReceivedData()) {
|
||||
trackPoint.setAltitudeGainLoss(barometer.getAggregatedValue(trackPointCreator.createNow()).gain_m(), barometer.getAggregatedValue(trackPointCreator.createNow()).loss_m());
|
||||
}
|
||||
}
|
||||
|
||||
public void reset() {
|
||||
Log.i(TAG, "Resetting data");
|
||||
|
||||
if (heartRate != null) heartRate.resetAggregated();
|
||||
if (cyclingCadence != null) cyclingCadence.resetAggregated();
|
||||
if (cyclingDistanceSpeed != null) cyclingDistanceSpeed.resetAggregated();
|
||||
if (cyclingPower != null) cyclingPower.resetAggregated();
|
||||
if (runningDistanceSpeedCadence != null) runningDistanceSpeedCadence.resetAggregated();
|
||||
if (barometer != null) barometer.resetAggregated();
|
||||
if (gps != null) gps.resetAggregated();
|
||||
}
|
||||
|
||||
private void set(@NonNull Aggregator<?, ?> type, @Nullable Aggregator<?, ?> sensorData) {
|
||||
Log.i(TAG, "Setting aggregator " + type.getClass().getCanonicalName() + " to " + sensorData);
|
||||
|
||||
if (type instanceof AggregatorHeartRate) {
|
||||
heartRate = (AggregatorHeartRate) sensorData;
|
||||
return;
|
||||
}
|
||||
if (type instanceof AggregatorCyclingCadence) {
|
||||
cyclingCadence = (AggregatorCyclingCadence) sensorData;
|
||||
return;
|
||||
}
|
||||
if (type instanceof AggregatorCyclingDistanceSpeed) {
|
||||
cyclingDistanceSpeed = (AggregatorCyclingDistanceSpeed) sensorData;
|
||||
return;
|
||||
}
|
||||
if (type instanceof AggregatorCyclingPower) {
|
||||
cyclingPower = (AggregatorCyclingPower) sensorData;
|
||||
return;
|
||||
}
|
||||
if (type instanceof AggregatorRunning) {
|
||||
runningDistanceSpeedCadence = (AggregatorRunning) sensorData;
|
||||
return;
|
||||
}
|
||||
if (type instanceof AggregatorBarometer) {
|
||||
barometer = (AggregatorBarometer) sensorData;
|
||||
return;
|
||||
}
|
||||
if (type instanceof AggregatorGPS) {
|
||||
gps = (AggregatorGPS) sensorData;
|
||||
return;
|
||||
}
|
||||
|
||||
throw new UnsupportedOperationException(type.getClass().getCanonicalName());
|
||||
}
|
||||
public record SensorDataSet(
|
||||
@NonNull
|
||||
SensorData<Position> position,
|
||||
@Nullable
|
||||
SensorData<Speed> speed,
|
||||
@Nullable
|
||||
SensorData<Distance> distance,
|
||||
@Nullable
|
||||
SensorData<HeartRate> heartRate,
|
||||
@Nullable
|
||||
SensorData<Cadence> cadence,
|
||||
@Nullable
|
||||
SensorData<Power> power,
|
||||
@Nullable
|
||||
SensorData<AltitudeGainLoss> altitudeGainLoss
|
||||
) {
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -76,7 +76,7 @@ public class IntervalStatisticsUpdater {
|
||||
}
|
||||
}
|
||||
|
||||
return trackPoint != null ? trackPoint.getId() : null;
|
||||
return trackPoint != null ? trackPoint.id() : null;
|
||||
}
|
||||
|
||||
public List<Statistics> getIntervalList() {
|
||||
@@ -152,9 +152,9 @@ public class IntervalStatisticsUpdater {
|
||||
if (lastTrackPoint == null) {
|
||||
return;
|
||||
}
|
||||
if (gain_m != null && lastTrackPoint.hasAltitudeGainLoss()) {
|
||||
gain_m = gain_m - lastTrackPoint.getAltitudeGainLoss().gain_m();
|
||||
loss_m = loss_m - lastTrackPoint.getAltitudeGainLoss().loss_m();
|
||||
if (gain_m != null && lastTrackPoint.altitudeGainLoss() != null) {
|
||||
gain_m = gain_m - lastTrackPoint.altitudeGainLoss().gain_m();
|
||||
loss_m = loss_m - lastTrackPoint.altitudeGainLoss().loss_m();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -150,7 +150,7 @@ public class MarkerEditViewModel extends AndroidViewModel {
|
||||
name,
|
||||
"",
|
||||
"",
|
||||
trackPoint.getPosition(),
|
||||
trackPoint.position(),
|
||||
null);
|
||||
|
||||
if (markerData == null) {
|
||||
|
||||
@@ -84,11 +84,11 @@ public abstract class GenericStatisticsViewHolder extends StatisticViewHolder<St
|
||||
|
||||
SensorDataSet sensorDataSet = data.sensorDataSet();
|
||||
final TrackPoint latestTrackPoint = data.latestTrackPoint();
|
||||
if (sensorDataSet != null && sensorDataSet.getSpeed() != null) {
|
||||
valueAndUnit = localSpeedFormatter.getSpeedParts(sensorDataSet.getSpeed().first);
|
||||
getBinding().statsDescriptionMain.setText(sensorDataSet.getSpeed().second);
|
||||
if (sensorDataSet != null && sensorDataSet.speed() != null) {
|
||||
valueAndUnit = localSpeedFormatter.getSpeedParts(sensorDataSet.speed().data());
|
||||
getBinding().statsDescriptionMain.setText(sensorDataSet.speed().sensorNameOrAddress());
|
||||
} else {
|
||||
Speed speed = latestTrackPoint != null && latestTrackPoint.hasSpeed() ? latestTrackPoint.getSpeed() : null;
|
||||
Speed speed = latestTrackPoint != null && latestTrackPoint.position().hasSpeed() ? latestTrackPoint.position().speed() : null;
|
||||
valueAndUnit = localSpeedFormatter.getSpeedParts(speed);
|
||||
|
||||
String title = reportSpeed ? getContext().getString(R.string.stats_speed) : getContext().getString(R.string.stats_pace);
|
||||
@@ -249,8 +249,8 @@ public abstract class GenericStatisticsViewHolder extends StatisticViewHolder<St
|
||||
@Override
|
||||
public void onChanged(UnitSystem unitSystem, RecordingData data) {
|
||||
TrackPoint latestTrackPoint = data.latestTrackPoint();
|
||||
Float altitude = latestTrackPoint != null && latestTrackPoint.hasAltitude() ? (float) latestTrackPoint.getAltitude().toM() : null;
|
||||
String altitudeReference = latestTrackPoint != null && latestTrackPoint.hasAltitude() ? getContext().getString(latestTrackPoint.getAltitude().getLabelId()) : null;
|
||||
Float altitude = latestTrackPoint != null && latestTrackPoint.position().hasAltitude() ? (float) latestTrackPoint.position().altitude().toM() : null;
|
||||
String altitudeReference = latestTrackPoint != null && latestTrackPoint.position().hasAltitude() ? getContext().getString(latestTrackPoint.position().altitude().getLabelId()) : null;
|
||||
Pair<String, String> valueAndUnit = StringUtils.getAltitudeParts(getContext(), altitude, unitSystem);
|
||||
|
||||
getBinding().statsValue.setText(valueAndUnit.first);
|
||||
@@ -294,8 +294,8 @@ public abstract class GenericStatisticsViewHolder extends StatisticViewHolder<St
|
||||
public void onChanged(UnitSystem unitSystem, RecordingData data) {
|
||||
TrackPoint latestTrackPoint = data.latestTrackPoint();
|
||||
String value;
|
||||
if (latestTrackPoint != null && latestTrackPoint.hasLocation()) {
|
||||
value = StringUtils.formatCoordinate(getContext(), latestTrackPoint.getPosition());
|
||||
if (latestTrackPoint != null && latestTrackPoint.position().hasLocation()) {
|
||||
value = StringUtils.formatCoordinate(getContext(), latestTrackPoint.position());
|
||||
} else {
|
||||
value = getContext().getString(R.string.value_unknown);
|
||||
}
|
||||
|
||||
@@ -3,10 +3,7 @@ package de.dennisguse.opentracks.viewmodels;
|
||||
import android.util.Pair;
|
||||
import android.view.LayoutInflater;
|
||||
|
||||
import java.time.Instant;
|
||||
|
||||
import de.dennisguse.opentracks.R;
|
||||
import de.dennisguse.opentracks.data.models.HeartRate;
|
||||
import de.dennisguse.opentracks.data.models.HeartRateZones;
|
||||
import de.dennisguse.opentracks.databinding.StatsSensorItemBinding;
|
||||
import de.dennisguse.opentracks.sensors.sensorData.SensorDataSet;
|
||||
@@ -37,10 +34,9 @@ public abstract class SensorStatisticsViewHolder extends StatisticViewHolder<Sta
|
||||
String sensorName = getContext().getString(R.string.value_unknown);
|
||||
|
||||
Pair<String, String> valueAndUnit;
|
||||
Pair<HeartRate, String> heartRate;
|
||||
if (sensorDataSet != null && (heartRate = sensorDataSet.getHeartRate()) != null) {
|
||||
valueAndUnit = StringUtils.getHeartRateParts(getContext(), heartRate.first);
|
||||
sensorName = heartRate.second;
|
||||
if (sensorDataSet != null && sensorDataSet.heartRate() != null) {
|
||||
valueAndUnit = StringUtils.getHeartRateParts(getContext(), sensorDataSet.heartRate().data());
|
||||
sensorName = sensorDataSet.heartRate().sensorNameOrAddress();
|
||||
} else {
|
||||
valueAndUnit = StringUtils.getHeartRateParts(getContext(), null);
|
||||
}
|
||||
@@ -48,8 +44,8 @@ public abstract class SensorStatisticsViewHolder extends StatisticViewHolder<Sta
|
||||
//TODO Loads preference every time
|
||||
HeartRateZones zones = PreferencesUtils.getHeartRateZones();
|
||||
int textColor;
|
||||
if (sensorDataSet != null && (heartRate = sensorDataSet.getHeartRate()) != null) {
|
||||
textColor = zones.getTextColorForZone(getContext(), heartRate.first);
|
||||
if (sensorDataSet != null && sensorDataSet.heartRate() != null) {
|
||||
textColor = zones.getTextColorForZone(getContext(), sensorDataSet.heartRate().data());
|
||||
} else {
|
||||
textColor = zones.getTextColorForZone(getContext(), null);
|
||||
}
|
||||
@@ -72,9 +68,9 @@ public abstract class SensorStatisticsViewHolder extends StatisticViewHolder<Sta
|
||||
String sensorName = getContext().getString(R.string.value_unknown);
|
||||
|
||||
Pair<String, String> valueAndUnit;
|
||||
if (sensorDataSet != null && sensorDataSet.getCadence() != null) {
|
||||
valueAndUnit = StringUtils.getCadenceParts(getContext(), sensorDataSet.getCadence().first);
|
||||
sensorName = sensorDataSet.getCadence().second;
|
||||
if (sensorDataSet != null && sensorDataSet.cadence() != null) {
|
||||
valueAndUnit = StringUtils.getCadenceParts(getContext(), sensorDataSet.cadence().data());
|
||||
sensorName = sensorDataSet.cadence().sensorNameOrAddress();
|
||||
} else {
|
||||
valueAndUnit = StringUtils.getCadenceParts(getContext(), null);
|
||||
}
|
||||
@@ -95,9 +91,9 @@ public abstract class SensorStatisticsViewHolder extends StatisticViewHolder<Sta
|
||||
String sensorName = getContext().getString(R.string.value_unknown);
|
||||
|
||||
Pair<String, String> valueAndUnit;
|
||||
if (sensorDataSet != null && sensorDataSet.getCyclingPower() != null) {
|
||||
valueAndUnit = StringUtils.getPowerParts(getContext(), sensorDataSet.getCyclingPower().getAggregatedValue(Instant.now())); //TODO Use MonotonicClock
|
||||
sensorName = sensorDataSet.getCyclingPower().getSensorNameOrAddress();
|
||||
if (sensorDataSet != null && sensorDataSet.power() != null) {
|
||||
valueAndUnit = StringUtils.getPowerParts(getContext(), sensorDataSet.power().data());
|
||||
sensorName = sensorDataSet.power().sensorNameOrAddress();
|
||||
} else {
|
||||
valueAndUnit = StringUtils.getCadenceParts(getContext(), null);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user