forked from upstream-mirrors/OpenTracks
Compare commits
69 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 3db0a53377 | |||
| 58dac8b706 | |||
| f29b6d4088 | |||
| 80fb4f9998 | |||
| 6340e55fff | |||
| 625549d381 | |||
| 7d9878b32e | |||
| a5553211af | |||
| 044e84154c | |||
| f61d718b2d | |||
| c7a54dcee8 | |||
| 3dc2ee3462 | |||
| 6817ff3234 | |||
| fb0400001a | |||
| 16591b3e80 | |||
| 28da32f573 | |||
| c687308d97 | |||
| 65c8e8542e | |||
| ca4cb648e7 | |||
| 53240c3d1a | |||
| 95b27f66fb | |||
| f31e146cda | |||
| 696939112a | |||
| 73ce97a63e | |||
| 5988ae3d3f | |||
| 58655814b2 | |||
| 11f1b6e72d | |||
| 8f537f33a7 | |||
| 4db72230ff | |||
| fd92793c2a | |||
| 05e45b6870 | |||
| 7521f906ed | |||
| 3222a0c497 | |||
| caf3d957cd | |||
| 005d00cfaa | |||
| c227d54f26 | |||
| bc62320b9f | |||
| 0ad32f676c | |||
| fff5f2d406 | |||
| be653b73ee | |||
| 4580f2a7bb | |||
| 5dd56892fb | |||
| e3460d94ed | |||
| 920ad5129a | |||
| fba08fe8dd | |||
| 5746a41905 | |||
| 361ae909e7 | |||
| 4b59660ea1 | |||
| cabf5d238e | |||
| 9c902b89ed | |||
| c31b8a5802 | |||
| 901c548d13 | |||
| b77de1e10d | |||
| b8c7fba7d8 | |||
| 466719fbc8 | |||
| 4f0ab71503 | |||
| daa4689f44 | |||
| 023a5aa221 | |||
| 291633c1d5 | |||
| 00990076fb | |||
| e672df7a60 | |||
| 82eb237117 | |||
| 832005565a | |||
| 894699f009 | |||
| f45dba258b | |||
| 17be49af70 | |||
| e91d36fc16 | |||
| 56f333180b | |||
| 9af8b0d353 |
@@ -1,5 +1,9 @@
|
||||
name: Android Test
|
||||
on: [push]
|
||||
on:
|
||||
push:
|
||||
branches:
|
||||
- main
|
||||
pull_request:
|
||||
|
||||
jobs:
|
||||
lint:
|
||||
|
||||
Generated
+6
@@ -47,6 +47,12 @@
|
||||
<MarkdownNavigatorCodeStyleSettings>
|
||||
<option name="RIGHT_MARGIN" value="72" />
|
||||
</MarkdownNavigatorCodeStyleSettings>
|
||||
<editorconfig>
|
||||
<option name="ENABLED" value="false" />
|
||||
</editorconfig>
|
||||
<codeStyleSettings language="JAVA">
|
||||
<option name="RIGHT_MARGIN" value="200" />
|
||||
</codeStyleSettings>
|
||||
<codeStyleSettings language="XML">
|
||||
<indentOptions>
|
||||
<option name="CONTINUATION_INDENT_SIZE" value="4" />
|
||||
|
||||
+6
-6
@@ -46,8 +46,8 @@ android {
|
||||
|
||||
defaultConfig {
|
||||
applicationId 'de.dennisguse.opentracks'
|
||||
versionCode 6338
|
||||
versionName "v4.23.0"
|
||||
versionCode 6407
|
||||
versionName "v4.23.2"
|
||||
|
||||
buildConfigField "String", "VERSION_NAME_FULL", "\"${getVersionName()}\""
|
||||
|
||||
@@ -149,17 +149,17 @@ dependencies {
|
||||
implementation 'com.google.android.material:material:1.13.0'
|
||||
implementation 'androidx.constraintlayout:constraintlayout:2.2.1'
|
||||
implementation 'androidx.core:core:1.17.0'
|
||||
implementation 'androidx.core:core-splashscreen:1.0.1'
|
||||
implementation 'androidx.core:core-splashscreen:1.2.0'
|
||||
implementation 'androidx.mediarouter:mediarouter:1.8.1'
|
||||
implementation 'androidx.core:core-location-altitude:1.0.0-alpha03'
|
||||
implementation 'androidx.work:work-runtime:2.10.5'
|
||||
implementation 'androidx.core:core-location-altitude:1.0.0-beta01'
|
||||
implementation 'androidx.work:work-runtime:2.11.0'
|
||||
|
||||
androidTestImplementation 'androidx.test:core:1.7.0'
|
||||
androidTestImplementation 'androidx.test.ext:junit:1.3.0'
|
||||
androidTestImplementation 'androidx.test:rules:1.7.0'
|
||||
androidTestImplementation 'androidx.test:runner:1.7.0'
|
||||
androidTestImplementation 'androidx.test.espresso:espresso-core:3.7.0'
|
||||
androidTestImplementation 'org.mockito:mockito-android:5.19.0'
|
||||
androidTestImplementation 'org.mockito:mockito-android:5.20.0'
|
||||
|
||||
androidTestUtil 'androidx.test:orchestrator:1.6.1'
|
||||
}
|
||||
|
||||
@@ -0,0 +1,10 @@
|
||||
v4.23.0: OpenTracks
|
||||
|
||||
Änderungen:
|
||||
- Benutzeroberfläche: Durchschnittsgeschwindigkeit des aktuellen Segments hinzugefügt
|
||||
(derzeit nur in neuen Layouts verfügbar)
|
||||
- Durch Klicken auf die Benachrichtigung wird die eigentliche Aufzeichnungs-Benutzeroberfläche geöffnet
|
||||
|
||||
Entwickler:
|
||||
- Veralteter JobIntentService durch WorkManager ersetzt
|
||||
- Verwende .isEmpty() außer für CharSequence und Stream.toList()
|
||||
@@ -0,0 +1,8 @@
|
||||
v4.23.1: OpenTracks
|
||||
|
||||
Änderungen:
|
||||
|
||||
- Neue UI-Komponente zur Anzeige der verstrichenen Zeit im aktuellen Segment
|
||||
|
||||
Fehlerbehebung:
|
||||
- Sofortiger Export nach Workout abgestürzt (eingeführt in v4.23.0)
|
||||
@@ -0,0 +1,7 @@
|
||||
v4.23.1: OpenTracks
|
||||
|
||||
Changes:
|
||||
- New UI component to show elapsed time in the current segment
|
||||
|
||||
Bugfix:
|
||||
- Instant post-workout export crashed (introduced in v4.23.0)
|
||||
@@ -0,0 +1,10 @@
|
||||
v4.23.2: OpenTracks
|
||||
|
||||
Changes:
|
||||
- Predictive back works completely
|
||||
|
||||
Bugfix:
|
||||
- Fixed in crash related to the track filename in the settings
|
||||
|
||||
Developer:
|
||||
- Introduced data class for Statistics
|
||||
@@ -13,18 +13,20 @@ import java.time.Duration;
|
||||
import de.dennisguse.opentracks.data.models.Altitude;
|
||||
import de.dennisguse.opentracks.data.models.Distance;
|
||||
import de.dennisguse.opentracks.data.models.Speed;
|
||||
import de.dennisguse.opentracks.data.models.Statistics;
|
||||
import de.dennisguse.opentracks.data.models.TrackPoint;
|
||||
import de.dennisguse.opentracks.settings.UnitSystem;
|
||||
import de.dennisguse.opentracks.stats.TrackStatistics;
|
||||
|
||||
//TODO assertEquals with ChartPoint (instead of individual comparisons)
|
||||
@RunWith(AndroidJUnit4.class)
|
||||
public class ChartPointTest {
|
||||
|
||||
private static final Statistics STATISTIC_NULL = new Statistics(null, null, Duration.ZERO, null, null, null, null, null, null, null);
|
||||
|
||||
@Test
|
||||
public void create_by_time() {
|
||||
// given
|
||||
TrackStatistics statistics = new TrackStatistics();
|
||||
statistics.setTotalTime(Duration.ofSeconds(1000));
|
||||
Statistics statistics = new Statistics(null, null, Duration.ofSeconds(1000), null, null, null, null, null, null, null);
|
||||
|
||||
// when
|
||||
ChartPoint point = ChartPoint.create(statistics, TrackStubUtils.createDefaultTrackPoint(), Speed.of(0), false, UnitSystem.IMPERIAL_FEET);
|
||||
@@ -36,8 +38,7 @@ public class ChartPointTest {
|
||||
@Test
|
||||
public void create_by_distance() {
|
||||
// given
|
||||
TrackStatistics statistics = new TrackStatistics();
|
||||
statistics.setTotalDistance(Distance.of(1000));
|
||||
Statistics statistics = new Statistics(null, null, null, null, Distance.of(1000), null, null, null, null, null);
|
||||
|
||||
// when
|
||||
ChartPoint point = ChartPoint.create(statistics, TrackStubUtils.createDefaultTrackPoint(), Speed.of(0), true, UnitSystem.METRIC);
|
||||
@@ -49,12 +50,11 @@ public class ChartPointTest {
|
||||
@Test
|
||||
public void create_get_altitude_speed_and_pace() {
|
||||
// given
|
||||
TrackStatistics statistics = new TrackStatistics();
|
||||
TrackPoint trackPoint = TrackStubUtils.createDefaultTrackPoint()
|
||||
.setAltitude(Altitude.EGM2008.of(50));
|
||||
|
||||
// when
|
||||
ChartPoint point = ChartPoint.create(statistics, trackPoint, Speed.of(10), false, UnitSystem.METRIC);
|
||||
ChartPoint point = ChartPoint.create(STATISTIC_NULL, trackPoint, Speed.of(10), false, UnitSystem.METRIC);
|
||||
|
||||
// then
|
||||
assertEquals(50, point.altitude(), 0.01);
|
||||
@@ -65,11 +65,10 @@ public class ChartPointTest {
|
||||
@Test
|
||||
public void create_sensorNotAvailable() {
|
||||
// given
|
||||
TrackStatistics statistics = new TrackStatistics();
|
||||
TrackPoint trackPoint = TrackStubUtils.createDefaultTrackPoint()
|
||||
.setAltitude(Altitude.EGM2008.of(50));
|
||||
// when
|
||||
ChartPoint point = ChartPoint.create(statistics, trackPoint, Speed.of(10), false, UnitSystem.METRIC);
|
||||
ChartPoint point = ChartPoint.create(STATISTIC_NULL, trackPoint, Speed.of(10), false, UnitSystem.METRIC);
|
||||
|
||||
// then
|
||||
assertNull(point.heartRate());
|
||||
@@ -86,10 +85,8 @@ public class ChartPointTest {
|
||||
.setCadence(101f)
|
||||
.setPower(102f);
|
||||
|
||||
TrackStatistics statistics = new TrackStatistics();
|
||||
|
||||
// when
|
||||
ChartPoint point = ChartPoint.create(statistics, trackPoint, Speed.of(10), false, UnitSystem.METRIC);
|
||||
ChartPoint point = ChartPoint.create(STATISTIC_NULL, trackPoint, Speed.of(10), false, UnitSystem.METRIC);
|
||||
|
||||
// then
|
||||
assertEquals(100.0, point.heartRate(), 0.01);
|
||||
|
||||
@@ -6,13 +6,11 @@ import android.util.Pair;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.time.Duration;
|
||||
import java.time.Instant;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import de.dennisguse.opentracks.data.ContentProviderUtils;
|
||||
import de.dennisguse.opentracks.data.TrackPointIterator;
|
||||
import de.dennisguse.opentracks.data.models.Altitude;
|
||||
import de.dennisguse.opentracks.data.models.Distance;
|
||||
import de.dennisguse.opentracks.data.models.Marker;
|
||||
@@ -20,9 +18,8 @@ import de.dennisguse.opentracks.data.models.Position;
|
||||
import de.dennisguse.opentracks.data.models.Speed;
|
||||
import de.dennisguse.opentracks.data.models.Track;
|
||||
import de.dennisguse.opentracks.data.models.TrackPoint;
|
||||
import de.dennisguse.opentracks.stats.TrackStatistics;
|
||||
import de.dennisguse.opentracks.stats.TrackStatisticsUpdater;
|
||||
import de.dennisguse.opentracks.ui.markers.MarkerUtils;
|
||||
import de.dennisguse.opentracks.util.AutoCloseableIterator;
|
||||
import de.dennisguse.opentracks.util.FileUtils;
|
||||
|
||||
public class TestDataUtil {
|
||||
@@ -85,10 +82,6 @@ public class TestDataUtil {
|
||||
createTrackPoint(i, TrackPoint.Type.SEGMENT_END_MANUAL)
|
||||
);
|
||||
|
||||
//TODO Use TrackStatisticsUpdater
|
||||
TrackStatistics stats = new TrackStatistics();
|
||||
stats.setTotalDistance(Distance.of(0));
|
||||
stats.setTotalTime(Duration.ofMillis(0));
|
||||
List<Marker> markers = List.of(
|
||||
new Marker(trackId, trackPoints.get(1), "Marker 1", "Marker description 1", "Marker category 3", "", null),
|
||||
new Marker(trackId, trackPoints.get(4), "Marker 2", "Marker description 2", "Marker category 3", "", null),
|
||||
@@ -126,8 +119,7 @@ public class TestDataUtil {
|
||||
trackPoint.setHeartRate(100f + i % 80);
|
||||
trackPoint.setCadence(300f + i);
|
||||
trackPoint.setPower(400f + i);
|
||||
trackPoint.setAltitudeGain(ALTITUDE_GAIN);
|
||||
trackPoint.setAltitudeLoss(ALTITUDE_LOSS);
|
||||
trackPoint.setAltitudeGainLoss(ALTITUDE_GAIN, ALTITUDE_LOSS);
|
||||
return trackPoint;
|
||||
}
|
||||
|
||||
@@ -153,16 +145,11 @@ public class TestDataUtil {
|
||||
dstFile.createNewFile();
|
||||
Uri photoUri = FileUtils.getUriForFile(context, dstFile);
|
||||
|
||||
//TODO Use TrackStatisticsUpdater
|
||||
TrackStatistics stats = new TrackStatistics();
|
||||
stats.setTotalDistance(Distance.of(0));
|
||||
stats.setTotalTime(Duration.ofMillis(0));
|
||||
|
||||
return new Marker(trackId, trackPoint, "Marker name", "Marker description", "Marker category", "", photoUri);
|
||||
}
|
||||
|
||||
public static List<TrackPoint> getTrackPoints(ContentProviderUtils contentProviderUtils, Track.Id trackId) {
|
||||
try (TrackPointIterator trackPointIterator = contentProviderUtils.getTrackPointLocationIterator(trackId, null)) {
|
||||
try (AutoCloseableIterator<TrackPoint> trackPointIterator = contentProviderUtils.getTrackPointLocationIterator(trackId, null)) {
|
||||
ArrayList<TrackPoint> trackPoints = new ArrayList<>();
|
||||
while (trackPointIterator.hasNext()) {
|
||||
trackPoints.add(trackPointIterator.next());
|
||||
@@ -170,20 +157,4 @@ public class TestDataUtil {
|
||||
return trackPoints;
|
||||
}
|
||||
}
|
||||
|
||||
public static Pair<Track.Id, TrackStatistics> buildTrackWithTrackPoints(ContentProviderUtils contentProviderUtils, int numberOfPoints) {
|
||||
Track dummyTrack = new Track();
|
||||
dummyTrack.setId(new Track.Id(System.currentTimeMillis()));
|
||||
dummyTrack.setName("Dummy Track");
|
||||
contentProviderUtils.insertTrack(dummyTrack);
|
||||
TrackStatisticsUpdater trackStatisticsUpdater = new TrackStatisticsUpdater();
|
||||
for (int i = 0; i < numberOfPoints; i++) {
|
||||
TrackPoint tp = TestDataUtil.createTrackPoint(i);
|
||||
contentProviderUtils.insertTrackPoint(tp, dummyTrack.getId());
|
||||
trackStatisticsUpdater.addTrackPoint(tp);
|
||||
}
|
||||
dummyTrack.setTrackStatistics(trackStatisticsUpdater.getTrackStatistics());
|
||||
contentProviderUtils.updateTrack(dummyTrack);
|
||||
return new Pair<>(dummyTrack.getId(), trackStatisticsUpdater.getTrackStatistics());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -41,8 +41,7 @@ public class TestSensorDataUtil {
|
||||
if (power != null) {
|
||||
tp.setPower(power);
|
||||
}
|
||||
tp.setAltitudeGain(3f);
|
||||
tp.setAltitudeLoss(3f);
|
||||
tp.setAltitudeGainLoss(3, 3);
|
||||
trackPointList.add(tp);
|
||||
}
|
||||
|
||||
|
||||
+19
-16
@@ -51,18 +51,21 @@ import java.util.UUID;
|
||||
import de.dennisguse.opentracks.content.data.TestDataUtil;
|
||||
import de.dennisguse.opentracks.content.data.TestSensorDataUtil;
|
||||
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.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.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.tables.MarkerColumns;
|
||||
import de.dennisguse.opentracks.data.tables.TrackPointsColumns;
|
||||
import de.dennisguse.opentracks.data.tables.TracksColumns;
|
||||
import de.dennisguse.opentracks.stats.SensorStatistics;
|
||||
import de.dennisguse.opentracks.stats.TrackStatistics;
|
||||
import de.dennisguse.opentracks.util.AutoCloseableIterator;
|
||||
import de.dennisguse.opentracks.util.FileUtils;
|
||||
|
||||
/**
|
||||
@@ -114,7 +117,7 @@ public class CustomContentProviderUtilsTest {
|
||||
private void testIterator(Track.Id trackId, int numPoints) {
|
||||
TrackPoint.Id lastPointId = initializeTrack(trackId, numPoints);
|
||||
List<TrackPoint> locations = new ArrayList<>(numPoints);
|
||||
try (TrackPointIterator it = contentProviderUtils.getTrackPointLocationIterator(trackId, null)) {
|
||||
try (AutoCloseableIterator<TrackPoint> it = contentProviderUtils.getTrackPointLocationIterator(trackId, null)) {
|
||||
while (it.hasNext()) {
|
||||
TrackPoint trackPoint = it.next();
|
||||
assertNotNull(trackPoint);
|
||||
@@ -152,7 +155,7 @@ public class CustomContentProviderUtilsTest {
|
||||
// Load all inserted trackPoints.
|
||||
TrackPoint.Id lastPointId = null;
|
||||
int counter = 0;
|
||||
try (TrackPointIterator it = contentProviderUtils.getTrackPointLocationIterator(id, null)) {
|
||||
try (AutoCloseableIterator<TrackPoint> it = contentProviderUtils.getTrackPointLocationIterator(id, null)) {
|
||||
while (it.hasNext()) {
|
||||
TrackPoint trackPoint = it.next();
|
||||
lastPointId = trackPoint.getId();
|
||||
@@ -406,18 +409,18 @@ public class CustomContentProviderUtilsTest {
|
||||
Pair<Track, List<TrackPoint>> track = TestDataUtil.createTrack(trackId, 10);
|
||||
|
||||
// AverageSpeed
|
||||
TrackStatistics statistics = new TrackStatistics();
|
||||
statistics.setStartTime(Instant.ofEpochMilli(1000));
|
||||
statistics.setStopTime(Instant.ofEpochMilli(2500));
|
||||
statistics.setTotalTime(Duration.ofMillis(1500));
|
||||
statistics.setMovingTime(Duration.ofMillis(700));
|
||||
statistics.setTotalDistance(Distance.of(750.0));
|
||||
statistics.setTotalAltitudeGain(50.0f);
|
||||
statistics.setMaxSpeed(Speed.of(60.0));
|
||||
statistics.setMaxAltitude(1250.0);
|
||||
statistics.setMinAltitude(1200.0);
|
||||
|
||||
track.first.setTrackStatistics(statistics);
|
||||
track.first.setStatistics(new Statistics(
|
||||
Instant.ofEpochMilli(1000),
|
||||
Instant.ofEpochMilli(2500),
|
||||
Duration.ofMillis(1500),
|
||||
Duration.ofMillis(700),
|
||||
Distance.of(750),
|
||||
Speed.of(60),
|
||||
new AltitudeExtremities(1250, 1200),
|
||||
new AltitudeGainLoss(50, 50),
|
||||
null,
|
||||
null
|
||||
));
|
||||
contentProviderUtils.insertTrack(track.first);
|
||||
|
||||
Marker marker = new Marker(trackId, track.second.get(0));
|
||||
@@ -854,7 +857,7 @@ public class CustomContentProviderUtilsTest {
|
||||
TrackPoint.Id startTrackPointId = trackpointIds.get(0);
|
||||
|
||||
// when
|
||||
TrackPointIterator trackPointIterator = contentProviderUtils.getTrackPointLocationIterator(trackId, trackpointIds.get(0));
|
||||
AutoCloseableIterator<TrackPoint> trackPointIterator = contentProviderUtils.getTrackPointLocationIterator(trackId, trackpointIds.get(0));
|
||||
|
||||
// then
|
||||
for (int i = 0; i < trackpointIds.size(); i++) {
|
||||
|
||||
+3
-2
@@ -9,6 +9,7 @@ import java.util.Collection;
|
||||
import java.util.List;
|
||||
import java.util.UUID;
|
||||
|
||||
import de.dennisguse.opentracks.data.models.Statistics;
|
||||
import de.dennisguse.opentracks.data.models.Track;
|
||||
|
||||
//TODO Merge with TrackFilenameGeneratorTest whenever Junit5 gets available.
|
||||
@@ -36,9 +37,9 @@ public class TrackFilenameGenerator2Test {
|
||||
Track track = new Track();
|
||||
track.setName("Best Track");
|
||||
track.setUuid(UUID.fromString("f81d4fae-7dec-11d0-a765-00a0c91e6bf6"));
|
||||
track.getTrackStatistics().setStartTime(Instant.parse("2020-02-02T02:02:02Z"));
|
||||
track.setStatistics(new Statistics(Instant.parse("2020-02-02T02:02:02Z"), null, null, null, null, null, null, null, null, null));
|
||||
|
||||
// when
|
||||
String filename = subject.format(track, TrackFileFormat.GPX);
|
||||
subject.format(track, TrackFileFormat.GPX);
|
||||
}
|
||||
}
|
||||
+2
-1
@@ -17,6 +17,7 @@ import java.util.UUID;
|
||||
|
||||
import de.dennisguse.opentracks.R;
|
||||
import de.dennisguse.opentracks.TimezoneRule;
|
||||
import de.dennisguse.opentracks.data.models.Statistics;
|
||||
import de.dennisguse.opentracks.data.models.Track;
|
||||
|
||||
@RunWith(Parameterized.class)
|
||||
@@ -50,7 +51,7 @@ public class TrackFilenameGeneratorTest {
|
||||
Track track = new Track();
|
||||
track.setName("Best Track");
|
||||
track.setUuid(UUID.fromString("0000fee0-0000-1000-8000-00805f9b34fb"));
|
||||
track.getTrackStatistics().setStartTime(Instant.parse("2020-02-02T02:02:02Z"));
|
||||
track.setStatistics(new Statistics(Instant.parse("2020-02-02T02:02:02Z"), null, null, null, null, null, null, null, null, null));
|
||||
|
||||
// when
|
||||
String filename = subject.format(track, TrackFileFormat.GPX);
|
||||
|
||||
+40
-53
@@ -60,6 +60,7 @@ 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;
|
||||
import de.dennisguse.opentracks.data.models.TrackPoint;
|
||||
import de.dennisguse.opentracks.io.file.TrackFileFormat;
|
||||
@@ -75,7 +76,6 @@ import de.dennisguse.opentracks.sensors.sensorData.Raw;
|
||||
import de.dennisguse.opentracks.sensors.sensorData.SensorDataSet;
|
||||
import de.dennisguse.opentracks.services.TrackRecordingService;
|
||||
import de.dennisguse.opentracks.services.handlers.TrackPointCreator;
|
||||
import de.dennisguse.opentracks.stats.TrackStatistics;
|
||||
|
||||
/**
|
||||
* Export a track to {@link TrackFileFormat} and verify that the import is identical.
|
||||
@@ -203,20 +203,20 @@ public class ExportImportTest {
|
||||
setUp();
|
||||
|
||||
Track track = contentProviderUtils.getTrack(trackId);
|
||||
TrackStatistics trackStatistics = track.getTrackStatistics();
|
||||
Statistics trackStatistics = track.getStatistics();
|
||||
|
||||
assertEquals(ZoneOffset.of("+01:00"), track.getZoneOffset());
|
||||
assertEquals(Instant.parse("2020-02-02T02:02:02Z"), trackStatistics.getStartTime());
|
||||
assertEquals(Instant.parse("2020-02-02T02:04:00Z"), trackStatistics.getStopTime());
|
||||
assertEquals(Instant.parse("2020-02-02T02:02:02Z"), trackStatistics.startTime());
|
||||
assertEquals(Instant.parse("2020-02-02T02:04:00Z"), trackStatistics.stopTime());
|
||||
|
||||
assertEquals(Duration.ofSeconds(56), trackStatistics.getTotalTime());
|
||||
assertEquals(Duration.ofSeconds(26), trackStatistics.getMovingTime()); //TODO Likely too low
|
||||
assertEquals(Duration.ofSeconds(56), trackStatistics.totalTime());
|
||||
assertEquals(Duration.ofSeconds(26), trackStatistics.movingTime()); //TODO Likely too low
|
||||
|
||||
// Distance
|
||||
assertEquals(222049.34375, trackStatistics.getTotalDistance().toM(), 0.01); //TODO Too low
|
||||
assertEquals(222049.34375, trackStatistics.totalDistance().toM(), 0.01); //TODO Too low
|
||||
|
||||
// Speed
|
||||
assertEquals(8540.359, trackStatistics.getMaxSpeed().toMPS(), 0.01);
|
||||
assertEquals(8540.359, trackStatistics.maxSpeed().toMPS(), 0.01);
|
||||
assertEquals(3965.166, trackStatistics.getAverageSpeed().toMPS(), 0.01);
|
||||
assertEquals(8540.359, trackStatistics.getAverageMovingSpeed().toMPS(), 0.01);
|
||||
|
||||
@@ -224,8 +224,8 @@ public class ExportImportTest {
|
||||
assertEquals(999.122, trackStatistics.getMinAltitude(), 0.01);
|
||||
assertEquals(1020.25, trackStatistics.getMaxAltitude(), 0.01);
|
||||
|
||||
assertEquals(2, trackStatistics.getTotalAltitudeGain(), 0.01);
|
||||
assertEquals(2, trackStatistics.getTotalAltitudeLoss(), 0.01);
|
||||
assertEquals(2, trackStatistics.altitudeGainLoss().gain_m(), 0.01);
|
||||
assertEquals(2, trackStatistics.altitudeGainLoss().loss_m(), 0.01);
|
||||
|
||||
List<TrackPoint> actual = TestDataUtil.getTrackPoints(contentProviderUtils, trackId);
|
||||
new TrackPointAssert().assertEquals(List.of(
|
||||
@@ -237,17 +237,14 @@ public class ExportImportTest {
|
||||
Altitude.WGS84.of(1020.25), null,
|
||||
null,
|
||||
Speed.of(15)))
|
||||
.setAltitudeLoss(1f)
|
||||
.setAltitudeGain(1f),
|
||||
.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)
|
||||
.setAltitudeLoss(1f)
|
||||
.setAltitudeGain(1f),
|
||||
.setPower(50).setAltitudeGainLoss(1, 1),
|
||||
new TrackPoint(TrackPoint.Type.TRACKPOINT, Instant.parse("2020-02-02T02:02:15Z"))
|
||||
.setHeartRate(HeartRate.of(68))
|
||||
.setCadence(3)
|
||||
@@ -260,8 +257,7 @@ public class ExportImportTest {
|
||||
null,
|
||||
Speed.of(5)))
|
||||
.setSensorDistance(Distance.of(2))
|
||||
.setAltitudeLoss(0f)
|
||||
.setAltitudeGain(0f)
|
||||
.setAltitudeGainLoss(0, 0)
|
||||
.setHeartRate(HeartRate.of(69))
|
||||
.setCadence(3)
|
||||
.setPower(50),
|
||||
@@ -274,27 +270,23 @@ public class ExportImportTest {
|
||||
Altitude.WGS84.of(999.1229858398438), null,
|
||||
null,
|
||||
Speed.of(15)))
|
||||
.setAltitudeLoss(0f)
|
||||
.setAltitudeGain(0f),
|
||||
.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)))
|
||||
.setAltitudeLoss(0f)
|
||||
.setAltitudeGain(0f),
|
||||
.setAltitudeGainLoss(0, 0),
|
||||
new TrackPoint(TrackPoint.Type.IDLE, Instant.parse("2020-02-02T02:03:30Z"))
|
||||
.setAltitudeLoss(0f)
|
||||
.setAltitudeGain(0f),
|
||||
.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)))
|
||||
.setAltitudeLoss(0f)
|
||||
.setAltitudeGain(0f),
|
||||
.setAltitudeGainLoss(0, 0),
|
||||
new TrackPoint(TrackPoint.Type.SEGMENT_END_MANUAL, Instant.parse("2020-02-02T02:04:00Z"))
|
||||
), actual);
|
||||
}
|
||||
@@ -334,29 +326,29 @@ public class ExportImportTest {
|
||||
a.assertEquals(trackPoints, actual);
|
||||
|
||||
// 3. trackstatistics
|
||||
TrackStatistics importedTrackStatistics = importedTrack.getTrackStatistics();
|
||||
Statistics importedTrackStatistics = importedTrack.getStatistics();
|
||||
|
||||
// Time
|
||||
assertEquals(track.getZoneOffset(), importedTrack.getZoneOffset());
|
||||
assertEquals(Instant.parse("2020-02-02T02:02:02Z"), importedTrackStatistics.getStartTime());
|
||||
assertEquals(Instant.parse("2020-02-02T02:04:00Z"), importedTrackStatistics.getStopTime());
|
||||
assertEquals(Instant.parse("2020-02-02T02:02:02Z"), importedTrackStatistics.startTime());
|
||||
assertEquals(Instant.parse("2020-02-02T02:04:00Z"), importedTrackStatistics.stopTime());
|
||||
|
||||
assertEquals(Duration.ofSeconds(56), importedTrackStatistics.getTotalTime());
|
||||
assertEquals(Duration.ofSeconds(26), importedTrackStatistics.getMovingTime());
|
||||
assertEquals(Duration.ofSeconds(56), importedTrackStatistics.totalTime());
|
||||
assertEquals(Duration.ofSeconds(26), importedTrackStatistics.movingTime());
|
||||
|
||||
// Distance
|
||||
assertEquals(222049.421, importedTrackStatistics.getTotalDistance().toM(), 0.01);
|
||||
assertEquals(222049.421, importedTrackStatistics.totalDistance().toM(), 0.01);
|
||||
|
||||
// Speed
|
||||
assertEquals(8540.362, importedTrackStatistics.getMaxSpeed().toMPS(), 0.01);
|
||||
assertEquals(8540.362, importedTrackStatistics.maxSpeed().toMPS(), 0.01);
|
||||
assertEquals(3965.168, importedTrackStatistics.getAverageSpeed().toMPS(), 0.01);
|
||||
assertEquals(8540.362, importedTrackStatistics.getAverageMovingSpeed().toMPS(), 0.01);
|
||||
|
||||
// Altitude
|
||||
assertEquals(999.122, importedTrackStatistics.getMinAltitude(), 0.01);
|
||||
assertEquals(1020.25, importedTrackStatistics.getMaxAltitude(), 0.01);
|
||||
assertEquals(2, importedTrackStatistics.getTotalAltitudeGain(), 0.01);
|
||||
assertEquals(2, importedTrackStatistics.getTotalAltitudeLoss(), 0.01);
|
||||
assertEquals(2, importedTrackStatistics.altitudeGainLoss().gain_m(), 0.01);
|
||||
assertEquals(2, importedTrackStatistics.altitudeGainLoss().loss_m(), 0.01);
|
||||
|
||||
|
||||
// 4. markers
|
||||
@@ -433,8 +425,7 @@ public class ExportImportTest {
|
||||
Altitude.WGS84.of(1020.2), null,
|
||||
null,
|
||||
Speed.of(15)))
|
||||
.setAltitudeLoss(1f)
|
||||
.setAltitudeGain(1f),
|
||||
.setAltitudeGainLoss(1, 1),
|
||||
new TrackPoint(TrackPoint.Type.TRACKPOINT,
|
||||
new Position(
|
||||
Instant.parse("2020-02-02T02:02:17Z"),
|
||||
@@ -442,8 +433,7 @@ public class ExportImportTest {
|
||||
Altitude.WGS84.of(1020.2), null,
|
||||
null,
|
||||
Speed.of(5)))
|
||||
.setAltitudeLoss(1f)
|
||||
.setAltitudeGain(1f)
|
||||
.setAltitudeGainLoss(1, 1)
|
||||
.setSensorDistance(Distance.of(12))
|
||||
.setHeartRate(69)
|
||||
.setPower(50f)
|
||||
@@ -455,8 +445,7 @@ public class ExportImportTest {
|
||||
Altitude.WGS84.of(999.0999755859375), null,
|
||||
null,
|
||||
Speed.of(15)))
|
||||
.setAltitudeLoss(0f)
|
||||
.setAltitudeGain(0f),
|
||||
.setAltitudeGainLoss(0, 0),
|
||||
new TrackPoint(TrackPoint.Type.SEGMENT_START_AUTOMATIC,
|
||||
new Position(
|
||||
Instant.parse("2020-02-02T02:03:22Z"),
|
||||
@@ -464,8 +453,7 @@ public class ExportImportTest {
|
||||
Altitude.WGS84.of(999.0999755859375), null,
|
||||
null,
|
||||
Speed.of(15)))
|
||||
.setAltitudeLoss(0f)
|
||||
.setAltitudeGain(0f),
|
||||
.setAltitudeGainLoss(0, 0),
|
||||
new TrackPoint(TrackPoint.Type.TRACKPOINT,
|
||||
new Position(
|
||||
Instant.parse("2020-02-02T02:03:50Z"),
|
||||
@@ -473,35 +461,34 @@ public class ExportImportTest {
|
||||
Altitude.WGS84.of(999.0999755859375), null,
|
||||
null,
|
||||
Speed.of(10)))
|
||||
.setAltitudeLoss(0f)
|
||||
.setAltitudeGain(0f)
|
||||
.setAltitudeGainLoss(0, 0)
|
||||
.setSpeed(Speed.of(15))
|
||||
), actual);
|
||||
|
||||
// 3. trackstatistics
|
||||
TrackStatistics importedTrackStatistics = importedTrack.getTrackStatistics();
|
||||
Statistics importedTrackStatistics = importedTrack.getStatistics();
|
||||
|
||||
// Time
|
||||
assertEquals(track.getZoneOffset(), importedTrack.getZoneOffset());
|
||||
assertEquals(Instant.parse("2020-02-02T02:02:03Z"), importedTrackStatistics.getStartTime());
|
||||
assertEquals(Instant.parse("2020-02-02T02:03:50Z"), importedTrackStatistics.getStopTime());
|
||||
assertEquals(Instant.parse("2020-02-02T02:02:03Z"), importedTrackStatistics.startTime());
|
||||
assertEquals(Instant.parse("2020-02-02T02:03:50Z"), importedTrackStatistics.stopTime());
|
||||
|
||||
assertEquals(Duration.ofSeconds(107), importedTrackStatistics.getTotalTime());
|
||||
assertEquals(Duration.ofSeconds(107), importedTrackStatistics.getMovingTime());
|
||||
assertEquals(Duration.ofSeconds(107), importedTrackStatistics.totalTime());
|
||||
assertEquals(Duration.ofSeconds(107), importedTrackStatistics.movingTime());
|
||||
|
||||
// Distance
|
||||
assertEquals(222271.734, importedTrackStatistics.getTotalDistance().toM(), 0.01);
|
||||
assertEquals(222271.734, importedTrackStatistics.totalDistance().toM(), 0.01);
|
||||
|
||||
// Speed
|
||||
assertEquals(2077.305, importedTrackStatistics.getMaxSpeed().toMPS(), 0.01);
|
||||
assertEquals(2077.305, importedTrackStatistics.maxSpeed().toMPS(), 0.01);
|
||||
assertEquals(2077.305, importedTrackStatistics.getAverageSpeed().toMPS(), 0.01);
|
||||
assertEquals(2077.305, importedTrackStatistics.getAverageMovingSpeed().toMPS(), 0.01);
|
||||
|
||||
// Altitude
|
||||
assertEquals(999.099, importedTrackStatistics.getMinAltitude(), 0.01);
|
||||
assertEquals(1020.2, importedTrackStatistics.getMaxAltitude(), 0.01);
|
||||
assertEquals(2, importedTrackStatistics.getTotalAltitudeGain(), 0.01);
|
||||
assertEquals(2, importedTrackStatistics.getTotalAltitudeLoss(), 0.01);
|
||||
assertEquals(2, importedTrackStatistics.altitudeGainLoss().gain_m(), 0.01);
|
||||
assertEquals(2, importedTrackStatistics.altitudeGainLoss().loss_m(), 0.01);
|
||||
|
||||
// 4. markers
|
||||
assertMarkers();
|
||||
|
||||
+12
-17
@@ -30,11 +30,11 @@ import de.dennisguse.opentracks.data.models.Altitude;
|
||||
import de.dennisguse.opentracks.data.models.Distance;
|
||||
import de.dennisguse.opentracks.data.models.Position;
|
||||
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.io.file.TrackFileFormat;
|
||||
import de.dennisguse.opentracks.io.file.exporter.TrackExporter;
|
||||
import de.dennisguse.opentracks.stats.TrackStatistics;
|
||||
|
||||
/**
|
||||
* Test that legacy KML/GPX formats can still be imported.
|
||||
@@ -149,9 +149,9 @@ public class GPXTrackImporterTest {
|
||||
assertEquals(ActivityType.UNKNOWN, importedTrack.getActivityType());
|
||||
|
||||
// 3. trackstatistics
|
||||
TrackStatistics trackStatistics = importedTrack.getTrackStatistics();
|
||||
assertEquals(0.75, trackStatistics.getMaxSpeed().toMPS(), 0.01);
|
||||
assertEquals(Duration.ofSeconds(101), trackStatistics.getMovingTime());
|
||||
Statistics trackStatistics = importedTrack.getStatistics();
|
||||
assertEquals(0.75, trackStatistics.maxSpeed().toMPS(), 0.01);
|
||||
assertEquals(Duration.ofSeconds(101), trackStatistics.movingTime());
|
||||
|
||||
// 4. trackpoints
|
||||
List<TrackPoint> importedTrackPoints = TestDataUtil.getTrackPoints(contentProviderUtils, importTrackId);
|
||||
@@ -204,9 +204,9 @@ public class GPXTrackImporterTest {
|
||||
assertEquals(ActivityType.UNKNOWN, importedTrack.getActivityType());
|
||||
|
||||
// 3. trackstatistics
|
||||
TrackStatistics trackStatistics = importedTrack.getTrackStatistics();
|
||||
assertEquals(5.0, trackStatistics.getMaxSpeed().toMPS(), 0.01);
|
||||
assertEquals(Duration.ofSeconds(101), trackStatistics.getMovingTime());
|
||||
Statistics trackStatistics = importedTrack.getStatistics();
|
||||
assertEquals(5.0, trackStatistics.maxSpeed().toMPS(), 0.01);
|
||||
assertEquals(Duration.ofSeconds(101), trackStatistics.movingTime());
|
||||
|
||||
// 4. trackpoints
|
||||
List<TrackPoint> importedTrackPoints = TestDataUtil.getTrackPoints(contentProviderUtils, importTrackId);
|
||||
@@ -267,8 +267,7 @@ public class GPXTrackImporterTest {
|
||||
Altitude.WGS84.of(10), null,
|
||||
null,
|
||||
Speed.of(15)))
|
||||
.setAltitudeLoss(1f)
|
||||
.setAltitudeGain(1f),
|
||||
.setAltitudeGainLoss(1, 1),
|
||||
new TrackPoint(TrackPoint.Type.TRACKPOINT,
|
||||
new Position(
|
||||
Instant.parse("2020-02-02T02:02:17Z"),
|
||||
@@ -276,8 +275,7 @@ public class GPXTrackImporterTest {
|
||||
Altitude.WGS84.of(10), null,
|
||||
null,
|
||||
Speed.of(5)))
|
||||
.setAltitudeLoss(1f)
|
||||
.setAltitudeGain(1f)
|
||||
.setAltitudeGainLoss(1, 1)
|
||||
.setSensorDistance(Distance.of(12))
|
||||
.setHeartRate(69)
|
||||
.setPower(50f)
|
||||
@@ -289,8 +287,7 @@ public class GPXTrackImporterTest {
|
||||
Altitude.WGS84.of(10), null,
|
||||
null,
|
||||
Speed.of(15)))
|
||||
.setAltitudeLoss(0f)
|
||||
.setAltitudeGain(0f),
|
||||
.setAltitudeGainLoss(0, 0),
|
||||
new TrackPoint(TrackPoint.Type.SEGMENT_START_AUTOMATIC,
|
||||
new Position(
|
||||
Instant.parse("2020-02-02T02:03:22Z"),
|
||||
@@ -298,8 +295,7 @@ public class GPXTrackImporterTest {
|
||||
Altitude.WGS84.of(10), null,
|
||||
null,
|
||||
Speed.of(15)))
|
||||
.setAltitudeLoss(0f)
|
||||
.setAltitudeGain(0f),
|
||||
.setAltitudeGainLoss(0, 0),
|
||||
new TrackPoint(TrackPoint.Type.TRACKPOINT,
|
||||
new Position(
|
||||
Instant.parse("2020-02-02T02:03:50Z"),
|
||||
@@ -307,8 +303,7 @@ public class GPXTrackImporterTest {
|
||||
Altitude.WGS84.of(10), null,
|
||||
null,
|
||||
Speed.of(10)))
|
||||
.setAltitudeLoss(0f)
|
||||
.setAltitudeGain(0f)
|
||||
.setAltitudeGainLoss(0, 0)
|
||||
.setSpeed(Speed.of(15))
|
||||
), actual);
|
||||
}
|
||||
|
||||
+5
-11
@@ -189,24 +189,21 @@ public class KMLTrackImporterTest {
|
||||
12.340097, 1.234156, null,
|
||||
Altitude.WGS84.of(469.286376953125), null,
|
||||
null,
|
||||
Speed.of(0.539)))
|
||||
.setAltitudeGain(0f),
|
||||
Speed.of(0.539))),
|
||||
new TrackPoint(TrackPoint.Type.TRACKPOINT,
|
||||
new Position(
|
||||
Instant.parse("2020-11-28T17:06:25.448Z"),
|
||||
12.340036, 1.23415, null,
|
||||
Altitude.WGS84.of(439.1626281738281), null,
|
||||
null,
|
||||
Speed.of(0.1577)))
|
||||
.setAltitudeGain(0f),
|
||||
Speed.of(0.1577))),
|
||||
new TrackPoint(TrackPoint.Type.TRACKPOINT,
|
||||
new Position(
|
||||
Instant.parse("2020-11-28T17:06:47.888Z"),
|
||||
12.340057, 1.23405, null,
|
||||
Altitude.WGS84.of(421.8070983886719), null,
|
||||
null,
|
||||
Speed.of(0)))
|
||||
.setAltitudeGain(0f),
|
||||
Speed.of(0))),
|
||||
// created resume trackpoint with time of next valid trackpoint
|
||||
new TrackPoint(TrackPoint.Type.SEGMENT_START_AUTOMATIC,
|
||||
new Position(
|
||||
@@ -214,16 +211,14 @@ public class KMLTrackImporterTest {
|
||||
12.340057, 1.23405, null,
|
||||
Altitude.WGS84.of(419.93902587890625), null,
|
||||
null,
|
||||
Speed.of(0)))
|
||||
.setAltitudeGain(0f),
|
||||
Speed.of(0))),
|
||||
new TrackPoint(TrackPoint.Type.TRACKPOINT,
|
||||
new Position(
|
||||
Instant.parse("2020-11-28T17:06:56.905Z"),
|
||||
12.340057, 1.23405, null,
|
||||
Altitude.WGS84.of(419.9036560058594), null,
|
||||
null,
|
||||
Speed.of(0)))
|
||||
.setAltitudeGain(0f),
|
||||
Speed.of(0))),
|
||||
new TrackPoint(TrackPoint.Type.TRACKPOINT,
|
||||
new Position(
|
||||
Instant.parse("2020-11-28T17:07:20.870Z"),
|
||||
@@ -231,7 +226,6 @@ public class KMLTrackImporterTest {
|
||||
Altitude.WGS84.of(417.99432373046875), null,
|
||||
null,
|
||||
Speed.of(0)))
|
||||
.setAltitudeGain(0f)
|
||||
), importedTrackPoints);
|
||||
}
|
||||
|
||||
|
||||
+3
-7
@@ -29,13 +29,9 @@ public class TrackPointAssert {
|
||||
Assert.assertEquals("altitude", expected.getAltitude().toM(), actual.getAltitude().toM(), delta);
|
||||
}
|
||||
|
||||
Assert.assertEquals("has altitudeGain", expected.hasAltitudeGain(), actual.hasAltitudeGain());
|
||||
if (expected.hasAltitudeGain()) {
|
||||
Assert.assertEquals("altitudeGain", expected.getAltitudeGain(), actual.getAltitudeGain(), delta);
|
||||
}
|
||||
Assert.assertEquals("has altitudeLoss", expected.hasAltitudeLoss(), actual.hasAltitudeLoss());
|
||||
if (expected.hasAltitudeLoss()) {
|
||||
Assert.assertEquals("altitudeLoss", expected.getAltitudeLoss(), actual.getAltitudeLoss(), delta);
|
||||
Assert.assertEquals("has altitudeGainLoss", expected.hasAltitudeGainLoss(), actual.hasAltitudeGainLoss());
|
||||
if (expected.hasAltitudeGainLoss()) {
|
||||
Assert.assertEquals("altitudeGainLoss", expected.getAltitudeGainLoss(), actual.getAltitudeGainLoss());
|
||||
}
|
||||
|
||||
Assert.assertEquals("has speed", expected.hasSpeed(), actual.hasSpeed());
|
||||
|
||||
+24
-13
@@ -16,24 +16,20 @@ import org.junit.runner.RunWith;
|
||||
import org.mockito.Mock;
|
||||
import org.mockito.junit.MockitoJUnitRunner;
|
||||
|
||||
import de.dennisguse.opentracks.data.models.Altitude;
|
||||
import de.dennisguse.opentracks.data.models.Distance;
|
||||
import de.dennisguse.opentracks.data.models.Position;
|
||||
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.settings.UnitSystem;
|
||||
import de.dennisguse.opentracks.stats.TrackStatistics;
|
||||
|
||||
@RunWith(MockitoJUnitRunner.class)
|
||||
public class TrackRecordingServiceNotificationManagerTest {
|
||||
|
||||
private final Context context = ApplicationProvider.getApplicationContext();
|
||||
|
||||
@Mock
|
||||
private TrackPoint trackPointMock;
|
||||
|
||||
@Mock
|
||||
private TrackStatistics trackStatisticsMock;
|
||||
|
||||
@Mock
|
||||
private NotificationCompat.Builder notificationCompatBuilder;
|
||||
|
||||
@@ -42,18 +38,33 @@ public class TrackRecordingServiceNotificationManagerTest {
|
||||
|
||||
@Test
|
||||
public void updateLocation_triggersAlertOnlyOnFirstInaccurateLocation() {
|
||||
when(trackPointMock.hasHorizontalAccuracy()).thenReturn(true);
|
||||
when(trackPointMock.getHorizontalAccuracy()).thenReturn(Distance.of(999f));
|
||||
when(trackPointMock.getSpeed()).thenReturn(Speed.of(0));
|
||||
when(trackStatisticsMock.getTotalDistance()).thenReturn(Distance.of(0));
|
||||
TrackPoint trackPointMock = new TrackPoint(TrackPoint.Type.TRACKPOINT,
|
||||
new Position(
|
||||
null,
|
||||
null, null, Distance.of(999f),
|
||||
Altitude.WGS84.of(10), null,
|
||||
null,
|
||||
Speed.of(0))
|
||||
);
|
||||
|
||||
when(notificationCompatBuilder.setOnlyAlertOnce(anyBoolean()))
|
||||
.thenReturn(notificationCompatBuilder);
|
||||
|
||||
TrackRecordingServiceNotificationManager subject = new TrackRecordingServiceNotificationManager(notificationManager, notificationCompatBuilder);
|
||||
subject.setUnitSystem(UnitSystem.METRIC);
|
||||
|
||||
Track track = new Track();
|
||||
track.setTrackStatistics(trackStatisticsMock);
|
||||
Track track = new Track(new Statistics(
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
Distance.of(0),
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
null
|
||||
));
|
||||
RecordingData data = new RecordingData(track, trackPointMock, null, null);
|
||||
|
||||
// when
|
||||
|
||||
+94
-95
@@ -5,8 +5,11 @@ import static org.junit.Assert.assertFalse;
|
||||
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.location.Location;
|
||||
import android.location.LocationManager;
|
||||
import android.os.Looper;
|
||||
|
||||
import androidx.core.location.LocationManagerCompat;
|
||||
import androidx.test.core.app.ApplicationProvider;
|
||||
import androidx.test.ext.junit.runners.AndroidJUnit4;
|
||||
import androidx.test.filters.MediumTest;
|
||||
@@ -30,11 +33,13 @@ import de.dennisguse.opentracks.R;
|
||||
import de.dennisguse.opentracks.TestUtil;
|
||||
import de.dennisguse.opentracks.content.data.TestDataUtil;
|
||||
import de.dennisguse.opentracks.data.ContentProviderUtils;
|
||||
import de.dennisguse.opentracks.data.models.AltitudeExtremities;
|
||||
import de.dennisguse.opentracks.data.models.AltitudeGainLoss;
|
||||
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.data.models.Statistics;
|
||||
import de.dennisguse.opentracks.data.models.Track;
|
||||
import de.dennisguse.opentracks.data.models.TrackPoint;
|
||||
import de.dennisguse.opentracks.io.file.importer.TrackPointAssert;
|
||||
@@ -46,7 +51,6 @@ import de.dennisguse.opentracks.sensors.sensorData.AggregatorRunning;
|
||||
import de.dennisguse.opentracks.sensors.sensorData.Raw;
|
||||
import de.dennisguse.opentracks.services.handlers.TrackPointCreator;
|
||||
import de.dennisguse.opentracks.settings.PreferencesUtils;
|
||||
import de.dennisguse.opentracks.stats.TrackStatistics;
|
||||
|
||||
/**
|
||||
* Tests insert location.
|
||||
@@ -106,8 +110,8 @@ public class TrackRecordingServiceRecordingTest {
|
||||
|
||||
|
||||
// then
|
||||
assertEquals(new TrackStatistics(startTime, startTime, 0, 0, 0, 0, null, null)
|
||||
, contentProviderUtils.getTrack(trackId).getTrackStatistics());
|
||||
assertEquals(new Statistics(Instant.parse(startTime), Instant.parse(startTime), Duration.ZERO, Duration.ZERO, Distance.of(0), Speed.zero(), new AltitudeExtremities(0.0, 0.0), null, null, null)
|
||||
, contentProviderUtils.getTrack(trackId).getStatistics());
|
||||
|
||||
new TrackPointAssert().assertEquals(List.of(
|
||||
new TrackPoint(TrackPoint.Type.SEGMENT_START_MANUAL, Instant.parse(startTime))
|
||||
@@ -120,14 +124,13 @@ public class TrackRecordingServiceRecordingTest {
|
||||
service.endCurrentTrack();
|
||||
|
||||
// then
|
||||
assertEquals(new TrackStatistics(startTime, stopTime, 0, 1, 1, 0, 0f, 0f)
|
||||
, contentProviderUtils.getTrack(trackId).getTrackStatistics());
|
||||
assertEquals(new Statistics(Instant.parse(startTime), Instant.parse(stopTime), Duration.ofSeconds(1), Duration.ofSeconds(1), Distance.of(0), Speed.zero(), new AltitudeExtremities(0.0, 0.0), new AltitudeGainLoss(0, 0), null, null)
|
||||
, contentProviderUtils.getTrack(trackId).getStatistics());
|
||||
|
||||
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))
|
||||
.setAltitudeGain(0f)
|
||||
.setAltitudeLoss(0f)
|
||||
.setAltitudeGainLoss(0, 0)
|
||||
), TestDataUtil.getTrackPoints(contentProviderUtils, trackId));
|
||||
}
|
||||
|
||||
@@ -143,10 +146,14 @@ public class TrackRecordingServiceRecordingTest {
|
||||
String startTime = "2020-02-02T02:02:02Z";
|
||||
trackPointCreator.setClock(startTime);
|
||||
Track.Id trackId = service.startNewTrack();
|
||||
|
||||
//We do not want the real GPS to interfere.
|
||||
LocationManagerCompat.removeUpdates((LocationManager) context.getSystemService(Context.LOCATION_SERVICE), trackPointCreator.getSensorManager().getGpsManager());
|
||||
|
||||
String gps1 = "2020-02-02T02:02:03Z";
|
||||
TrackRecordingServiceTestUtils.sendGPSLocation(trackPointCreator, gps1, 45.0, 35.0, 1, 15);
|
||||
sendGPSLocation(trackPointCreator, gps1, 45.0, 35.0, 1, 15);
|
||||
String gps2 = "2020-02-02T02:02:04Z";
|
||||
TrackRecordingServiceTestUtils.sendGPSLocation(trackPointCreator, gps2, 45.0, 35.0, 1, 15);
|
||||
sendGPSLocation(trackPointCreator, gps2, 45.0, 35.0, 1, 15);
|
||||
|
||||
// when
|
||||
String idleTime = "2020-02-02T02:02:17Z";
|
||||
@@ -192,14 +199,13 @@ public class TrackRecordingServiceRecordingTest {
|
||||
service.endCurrentTrack();
|
||||
|
||||
// then
|
||||
assertEquals(new TrackStatistics(startTime, pauseTime, 0, 1, 1, 0, 0f, 0f)
|
||||
, contentProviderUtils.getTrack(trackId).getTrackStatistics());
|
||||
assertEquals(new Statistics(Instant.parse(startTime), Instant.parse(pauseTime), Duration.ofSeconds(1), Duration.ofSeconds(1), Distance.of(0), Speed.zero(), new AltitudeExtremities(0.0, 0.0), new AltitudeGainLoss(0, 0), null, null)
|
||||
, contentProviderUtils.getTrack(trackId).getStatistics());
|
||||
|
||||
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))
|
||||
.setAltitudeGain(0f)
|
||||
.setAltitudeLoss(0f)
|
||||
.setAltitudeGainLoss(0, 0)
|
||||
), TestDataUtil.getTrackPoints(contentProviderUtils, trackId));
|
||||
|
||||
//when
|
||||
@@ -208,14 +214,13 @@ public class TrackRecordingServiceRecordingTest {
|
||||
service.resumeTrack(trackId);
|
||||
|
||||
// then
|
||||
assertEquals(new TrackStatistics(startTime, resumeTime, 0, 1, 1, 0, 0f, 0f)
|
||||
, contentProviderUtils.getTrack(trackId).getTrackStatistics());
|
||||
assertEquals(new Statistics(Instant.parse(startTime), Instant.parse(resumeTime), Duration.ofSeconds(1), Duration.ofSeconds(1), Distance.of(0), Speed.zero(), new AltitudeExtremities(0.0, 0.0), new AltitudeGainLoss(0, 0), null, null)
|
||||
, contentProviderUtils.getTrack(trackId).getStatistics());
|
||||
|
||||
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))
|
||||
.setAltitudeGain(0f)
|
||||
.setAltitudeLoss(0f),
|
||||
.setAltitudeGainLoss(0, 0),
|
||||
new TrackPoint(TrackPoint.Type.SEGMENT_START_MANUAL, Instant.parse(resumeTime))
|
||||
), TestDataUtil.getTrackPoints(contentProviderUtils, trackId));
|
||||
}
|
||||
@@ -248,8 +253,7 @@ public class TrackRecordingServiceRecordingTest {
|
||||
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))
|
||||
.setAltitudeGain(0f)
|
||||
.setAltitudeLoss(0f)
|
||||
.setAltitudeGainLoss(0, 0)
|
||||
), trackPoints);
|
||||
}
|
||||
|
||||
@@ -278,8 +282,7 @@ 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))
|
||||
.setAltitudeGain(0f)
|
||||
.setAltitudeLoss(0f),
|
||||
.setAltitudeGainLoss(0, 0),
|
||||
new TrackPoint(TrackPoint.Type.SEGMENT_START_MANUAL, Instant.parse(resumeTime))
|
||||
), TestDataUtil.getTrackPoints(contentProviderUtils, trackId));
|
||||
}
|
||||
@@ -315,12 +318,10 @@ public class TrackRecordingServiceRecordingTest {
|
||||
new TrackPointAssert().assertEquals(List.of(
|
||||
new TrackPoint(TrackPoint.Type.SEGMENT_START_MANUAL, Instant.parse(startTime)),
|
||||
new TrackPoint(TrackPoint.Type.TRACKPOINT, Instant.parse(sensor3))
|
||||
.setAltitudeGain(0f)
|
||||
.setAltitudeLoss(0f)
|
||||
.setAltitudeGainLoss(0, 0)
|
||||
.setHeartRate(HeartRate.of(7)),
|
||||
new TrackPoint(TrackPoint.Type.SEGMENT_END_MANUAL, Instant.parse(stopTime))
|
||||
.setAltitudeGain(0f)
|
||||
.setAltitudeLoss(0f)
|
||||
.setAltitudeGainLoss(0, 0)
|
||||
.setHeartRate(HeartRate.of(7))
|
||||
), TestDataUtil.getTrackPoints(contentProviderUtils, trackId));
|
||||
}
|
||||
@@ -338,28 +339,27 @@ public class TrackRecordingServiceRecordingTest {
|
||||
|
||||
// when
|
||||
String gps1 = "2020-02-02T02:02:03Z";
|
||||
TrackRecordingServiceTestUtils.sendGPSLocation(trackPointCreator, gps1, 45.0, 35.0, 1, 15);
|
||||
sendGPSLocation(trackPointCreator, gps1, 45.0, 35.0, 1, 15);
|
||||
|
||||
// then
|
||||
assertEquals(new TrackStatistics(startTime, gps1, 0, 1, 1, 15, 0f, 0f)
|
||||
, contentProviderUtils.getTrack(trackId).getTrackStatistics());
|
||||
assertEquals(new Statistics(Instant.parse(startTime), Instant.parse(gps1), Duration.ofSeconds(1), Duration.ofSeconds(1), Distance.of(0), Speed.of(15), new AltitudeExtremities(0.0, 0.0), new AltitudeGainLoss(0, 0), null, null)
|
||||
, contentProviderUtils.getTrack(trackId).getStatistics());
|
||||
|
||||
// when
|
||||
String gps2 = "2020-02-02T02:02:06Z";
|
||||
TrackRecordingServiceTestUtils.sendGPSLocation(trackPointCreator, gps2, 45.0001, 35.0, 1, 15);
|
||||
sendGPSLocation(trackPointCreator, gps2, 45.0001, 35.0, 1, 15);
|
||||
|
||||
// then
|
||||
assertEquals(new TrackStatistics(startTime, gps2, 11.113178253173828f, 4, 4, 15, 0f, 0f)
|
||||
, contentProviderUtils.getTrack(trackId).getTrackStatistics());
|
||||
assertEquals(new Statistics(Instant.parse(startTime), Instant.parse(gps2), Duration.ofSeconds(4), Duration.ofSeconds(4), Distance.of(11.113178253173828), Speed.of(15), new AltitudeExtremities(0.0, 0.0), new AltitudeGainLoss(0, 0), null, null)
|
||||
, contentProviderUtils.getTrack(trackId).getStatistics());
|
||||
|
||||
// when
|
||||
String gps3 = "2020-02-02T02:02:08Z";
|
||||
TrackRecordingServiceTestUtils.sendGPSLocation(trackPointCreator, gps3, 45.0002, 35.0, 1, 15);
|
||||
sendGPSLocation(trackPointCreator, gps3, 45.0002, 35.0, 1, 15);
|
||||
|
||||
// then
|
||||
assertEquals(new TrackStatistics(startTime, gps3, 22.226356506347656, 6, 6, 15, 0f, 0f)
|
||||
, contentProviderUtils.getTrack(trackId).getTrackStatistics());
|
||||
|
||||
assertEquals(new Statistics(Instant.parse(startTime), Instant.parse(gps3), Duration.ofSeconds(6), Duration.ofSeconds(6), Distance.of(22.226356506347656), Speed.of(15), new AltitudeExtremities(0.0, 0.0), new AltitudeGainLoss(0, 0), null, null)
|
||||
, contentProviderUtils.getTrack(trackId).getStatistics());
|
||||
|
||||
// when
|
||||
String stopTime = "2020-02-02T02:02:12Z";
|
||||
@@ -367,8 +367,8 @@ public class TrackRecordingServiceRecordingTest {
|
||||
service.endCurrentTrack();
|
||||
|
||||
// then
|
||||
assertEquals(new TrackStatistics(startTime, stopTime, 22.226356506347656, 10, 10, 15, 0f, 0f)
|
||||
, contentProviderUtils.getTrack(trackId).getTrackStatistics());
|
||||
assertEquals(new Statistics(Instant.parse(startTime), Instant.parse(stopTime), Duration.ofSeconds(10), Duration.ofSeconds(10), Distance.of(22.226356506347656), Speed.of(15), new AltitudeExtremities(0.0, 0.0), new AltitudeGainLoss(0, 0), null, null)
|
||||
, contentProviderUtils.getTrack(trackId).getStatistics());
|
||||
|
||||
new TrackPointAssert().assertEquals(List.of(
|
||||
new TrackPoint(TrackPoint.Type.SEGMENT_START_MANUAL, Instant.parse(startTime)),
|
||||
@@ -379,8 +379,7 @@ public class TrackRecordingServiceRecordingTest {
|
||||
null, null,
|
||||
null,
|
||||
Speed.of(15)))
|
||||
.setAltitudeGain(0f)
|
||||
.setAltitudeLoss(0f),
|
||||
.setAltitudeGainLoss(0, 0),
|
||||
new TrackPoint(TrackPoint.Type.TRACKPOINT,
|
||||
new Position(
|
||||
Instant.parse(gps2),
|
||||
@@ -388,8 +387,7 @@ public class TrackRecordingServiceRecordingTest {
|
||||
null, null,
|
||||
null,
|
||||
Speed.of(15)))
|
||||
.setAltitudeGain(0f)
|
||||
.setAltitudeLoss(0f),
|
||||
.setAltitudeGainLoss(0, 0),
|
||||
new TrackPoint(TrackPoint.Type.TRACKPOINT,
|
||||
new Position(
|
||||
Instant.parse(gps3),
|
||||
@@ -397,11 +395,9 @@ public class TrackRecordingServiceRecordingTest {
|
||||
null, null,
|
||||
null,
|
||||
Speed.of(15)))
|
||||
.setAltitudeGain(0f)
|
||||
.setAltitudeLoss(0f),
|
||||
.setAltitudeGainLoss(0, 0),
|
||||
new TrackPoint(TrackPoint.Type.SEGMENT_END_MANUAL, Instant.parse(stopTime))
|
||||
.setAltitudeGain(0f)
|
||||
.setAltitudeLoss(0f)
|
||||
.setAltitudeGainLoss(0, 0)
|
||||
), TestDataUtil.getTrackPoints(contentProviderUtils, trackId));
|
||||
}
|
||||
|
||||
@@ -417,25 +413,25 @@ public class TrackRecordingServiceRecordingTest {
|
||||
|
||||
// when
|
||||
String gps1 = "2020-02-02T02:02:03Z";
|
||||
TrackRecordingServiceTestUtils.sendGPSLocation(trackPointCreator, gps1, 45.0, 35.0, 1, 15);
|
||||
sendGPSLocation(trackPointCreator, gps1, 45.0, 35.0, 1, 15);
|
||||
|
||||
// then
|
||||
TrackStatistics gps1Statistics = new TrackStatistics(startTime, gps1, 0, 1, 1, 15, 0f, 0f);
|
||||
assertEquals(gps1Statistics, contentProviderUtils.getTrack(trackId).getTrackStatistics());
|
||||
Statistics gps1Statistics = new Statistics(Instant.parse(startTime), Instant.parse(gps1), Duration.ofSeconds(1), Duration.ofSeconds(1), Distance.of(0), Speed.of(15), new AltitudeExtremities(0.0, 0.0), new AltitudeGainLoss(0, 0), null, null);
|
||||
assertEquals(gps1Statistics, contentProviderUtils.getTrack(trackId).getStatistics());
|
||||
|
||||
// when
|
||||
String gps2 = "2020-02-02T02:02:06Z";
|
||||
TrackRecordingServiceTestUtils.sendGPSLocation(trackPointCreator, gps2, 45.00001, 35.0, 1, 15);
|
||||
sendGPSLocation(trackPointCreator, gps2, 45.00001, 35.0, 1, 15);
|
||||
|
||||
// then
|
||||
assertEquals(gps1Statistics, contentProviderUtils.getTrack(trackId).getTrackStatistics());
|
||||
assertEquals(gps1Statistics, contentProviderUtils.getTrack(trackId).getStatistics());
|
||||
|
||||
// when
|
||||
String gps3 = "2020-02-02T02:02:08Z";
|
||||
TrackRecordingServiceTestUtils.sendGPSLocation(trackPointCreator, gps3, 45.00002, 35.0, 1, 15);
|
||||
sendGPSLocation(trackPointCreator, gps3, 45.00002, 35.0, 1, 15);
|
||||
|
||||
// then
|
||||
assertEquals(gps1Statistics, contentProviderUtils.getTrack(trackId).getTrackStatistics());
|
||||
assertEquals(gps1Statistics, contentProviderUtils.getTrack(trackId).getStatistics());
|
||||
|
||||
// when
|
||||
String stopTime = "2020-02-02T02:02:12Z";
|
||||
@@ -443,8 +439,8 @@ public class TrackRecordingServiceRecordingTest {
|
||||
service.endCurrentTrack();
|
||||
|
||||
// then
|
||||
assertEquals(new TrackStatistics(startTime, stopTime, 2.222635507583618, 10, 10, 15, 0f, 0f)
|
||||
, contentProviderUtils.getTrack(trackId).getTrackStatistics());
|
||||
assertEquals(new Statistics(Instant.parse(startTime), Instant.parse(stopTime), Duration.ofSeconds(10), Duration.ofSeconds(10), Distance.of(2.222635507583618), Speed.of(15), new AltitudeExtremities(0.0, 0.0), new AltitudeGainLoss(0, 0), null, null)
|
||||
, contentProviderUtils.getTrack(trackId).getStatistics());
|
||||
|
||||
new TrackPointAssert().assertEquals(List.of(
|
||||
new TrackPoint(TrackPoint.Type.SEGMENT_START_MANUAL, Instant.parse(startTime)),
|
||||
@@ -455,8 +451,7 @@ public class TrackRecordingServiceRecordingTest {
|
||||
null, null,
|
||||
null,
|
||||
Speed.of(15)))
|
||||
.setAltitudeGain(0f)
|
||||
.setAltitudeLoss(0f),
|
||||
.setAltitudeGainLoss(0, 0),
|
||||
new TrackPoint(TrackPoint.Type.TRACKPOINT,
|
||||
new Position(
|
||||
Instant.parse(gps3),
|
||||
@@ -464,8 +459,7 @@ public class TrackRecordingServiceRecordingTest {
|
||||
null, null,
|
||||
null,
|
||||
Speed.of(15)))
|
||||
.setAltitudeGain(0f)
|
||||
.setAltitudeLoss(0f),
|
||||
.setAltitudeGainLoss(0, 0),
|
||||
new TrackPoint(TrackPoint.Type.SEGMENT_END_MANUAL,
|
||||
new Position(
|
||||
Instant.parse(stopTime),
|
||||
@@ -473,8 +467,7 @@ public class TrackRecordingServiceRecordingTest {
|
||||
null, null,
|
||||
null,
|
||||
Speed.of(15)))
|
||||
.setAltitudeGain(0f)
|
||||
.setAltitudeLoss(0f)
|
||||
.setAltitudeGainLoss(0, 0)
|
||||
), TestDataUtil.getTrackPoints(contentProviderUtils, trackId));
|
||||
}
|
||||
|
||||
@@ -491,15 +484,15 @@ public class TrackRecordingServiceRecordingTest {
|
||||
|
||||
// when
|
||||
String gps1 = "2020-02-02T02:02:03Z";
|
||||
TrackRecordingServiceTestUtils.sendGPSLocation(trackPointCreator, gps1, 45.0, 35.0, 1, 15);
|
||||
sendGPSLocation(trackPointCreator, gps1, 45.0, 35.0, 1, 15);
|
||||
|
||||
// when - will be ignored
|
||||
String gps2 = "2020-02-02T02:02:04Z";
|
||||
TrackRecordingServiceTestUtils.sendGPSLocation(trackPointCreator, gps2, 45.0, 35.0, 1, 15);
|
||||
sendGPSLocation(trackPointCreator, gps2, 45.0, 35.0, 1, 15);
|
||||
|
||||
// when
|
||||
String gps3 = "2020-02-02T02:02:05Z";
|
||||
TrackRecordingServiceTestUtils.sendGPSLocation(trackPointCreator, gps3, 45.0001, 35.0, 1, 15);
|
||||
sendGPSLocation(trackPointCreator, gps3, 45.0001, 35.0, 1, 15);
|
||||
|
||||
// when
|
||||
String stopTime = "2020-02-02T02:02:12Z";
|
||||
@@ -516,8 +509,7 @@ public class TrackRecordingServiceRecordingTest {
|
||||
null, null,
|
||||
null,
|
||||
Speed.of(15)))
|
||||
.setAltitudeGain(0f)
|
||||
.setAltitudeLoss(0f),
|
||||
.setAltitudeGainLoss(0, 0),
|
||||
new TrackPoint(TrackPoint.Type.TRACKPOINT,
|
||||
new Position(
|
||||
Instant.parse(gps3),
|
||||
@@ -525,11 +517,9 @@ public class TrackRecordingServiceRecordingTest {
|
||||
null, null,
|
||||
null,
|
||||
Speed.of(15)))
|
||||
.setAltitudeGain(0f)
|
||||
.setAltitudeLoss(0f),
|
||||
.setAltitudeGainLoss(0, 0),
|
||||
new TrackPoint(TrackPoint.Type.SEGMENT_END_MANUAL, Instant.parse(stopTime))
|
||||
.setAltitudeGain(0f)
|
||||
.setAltitudeLoss(0f)
|
||||
.setAltitudeGainLoss(0, 0)
|
||||
), TestDataUtil.getTrackPoints(contentProviderUtils, trackId));
|
||||
}
|
||||
|
||||
@@ -545,19 +535,19 @@ public class TrackRecordingServiceRecordingTest {
|
||||
|
||||
// when
|
||||
String gps1 = "2020-02-02T02:02:03Z";
|
||||
TrackRecordingServiceTestUtils.sendGPSLocation(trackPointCreator, gps1, 45.0, 35.0, 100, 15);
|
||||
sendGPSLocation(trackPointCreator, gps1, 45.0, 35.0, 100, 15);
|
||||
|
||||
// then
|
||||
TrackStatistics startStatistics = new TrackStatistics(startTime, startTime, 0, 0, 0, 0, null, null);
|
||||
assertEquals(startStatistics, contentProviderUtils.getTrack(trackId).getTrackStatistics());
|
||||
Statistics startStatistics = new Statistics(Instant.parse(startTime), Instant.parse(startTime), Duration.ofSeconds(0), Duration.ofSeconds(0), Distance.of(0), Speed.of(0), new AltitudeExtremities(0.0, 0.0), null, null, null);
|
||||
assertEquals(startStatistics, contentProviderUtils.getTrack(trackId).getStatistics());
|
||||
|
||||
|
||||
// when
|
||||
String gps2 = "2020-02-02T02:02:06Z";
|
||||
TrackRecordingServiceTestUtils.sendGPSLocation(trackPointCreator, gps2, 45.1, 35.0, 100, 15);
|
||||
sendGPSLocation(trackPointCreator, gps2, 45.1, 35.0, 100, 15);
|
||||
|
||||
// then
|
||||
assertEquals(startStatistics, contentProviderUtils.getTrack(trackId).getTrackStatistics());
|
||||
assertEquals(startStatistics, contentProviderUtils.getTrack(trackId).getStatistics());
|
||||
|
||||
|
||||
// when
|
||||
@@ -566,16 +556,14 @@ public class TrackRecordingServiceRecordingTest {
|
||||
service.endCurrentTrack();
|
||||
|
||||
// then
|
||||
assertEquals(new TrackStatistics(startTime, stopTime, 0, 10, 10, 0, 0f, 0f)
|
||||
, contentProviderUtils.getTrack(trackId).getTrackStatistics());
|
||||
|
||||
assertEquals(new Statistics(Instant.parse(startTime), Instant.parse(stopTime), Duration.ofSeconds(10), Duration.ofSeconds(10), Distance.of(0), Speed.of(0), new AltitudeExtremities(0.0, 0.0), new AltitudeGainLoss(0, 0), null, null)
|
||||
, contentProviderUtils.getTrack(trackId).getStatistics());
|
||||
|
||||
// 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))
|
||||
.setAltitudeGain(0f)
|
||||
.setAltitudeLoss(0f)
|
||||
.setAltitudeGainLoss(0, 0)
|
||||
), TestDataUtil.getTrackPoints(contentProviderUtils, trackId));
|
||||
}
|
||||
|
||||
@@ -591,18 +579,19 @@ public class TrackRecordingServiceRecordingTest {
|
||||
|
||||
// when
|
||||
String gps1 = "2020-02-02T02:02:03Z";
|
||||
TrackRecordingServiceTestUtils.sendGPSLocation(trackPointCreator, gps1, 45.0, 35.0, 1, 15);
|
||||
sendGPSLocation(trackPointCreator, gps1, 45.0, 35.0, 1, 15);
|
||||
|
||||
// then
|
||||
assertEquals(new TrackStatistics(startTime, gps1, 0, 1, 1, 15, 0f, 0f), contentProviderUtils.getTrack(trackId).getTrackStatistics());
|
||||
assertEquals(new Statistics(Instant.parse(startTime), Instant.parse(gps1), Duration.ofSeconds(1), Duration.ofSeconds(1), Distance.of(0), Speed.of(15), new AltitudeExtremities(0.0, 0.0), new AltitudeGainLoss(0, 0), null, null)
|
||||
, contentProviderUtils.getTrack(trackId).getStatistics());
|
||||
|
||||
// when
|
||||
String gps2 = "2020-02-02T02:02:06Z";
|
||||
TrackRecordingServiceTestUtils.sendGPSLocation(trackPointCreator, gps2, 45.1, 35.0, 1, 15);
|
||||
sendGPSLocation(trackPointCreator, gps2, 45.1, 35.0, 1, 15);
|
||||
|
||||
// then
|
||||
assertEquals(new TrackStatistics(startTime, gps2, 11113.275390625, 4, 4, 2778.31884765625f, 0f, 0f).toString(), contentProviderUtils.getTrack(trackId).getTrackStatistics().toString());
|
||||
|
||||
assertEquals(new Statistics(Instant.parse(startTime), Instant.parse(gps2), Duration.ofSeconds(4), Duration.ofSeconds(4), Distance.of(11113.275390625), Speed.of(2778.31884765625f), new AltitudeExtremities(0.0, 0.0), new AltitudeGainLoss(0, 0), null, null)
|
||||
, contentProviderUtils.getTrack(trackId).getStatistics());
|
||||
|
||||
// when
|
||||
String stopTime = "2020-02-02T02:02:12Z";
|
||||
@@ -610,7 +599,8 @@ public class TrackRecordingServiceRecordingTest {
|
||||
service.endCurrentTrack();
|
||||
|
||||
// then
|
||||
assertEquals(new TrackStatistics(startTime, stopTime, 11113.275390625, 10, 10, 1111.3275390625f, 0f, 0f).toString(), contentProviderUtils.getTrack(trackId).getTrackStatistics().toString());
|
||||
assertEquals(new Statistics(Instant.parse(startTime), Instant.parse(stopTime), Duration.ofSeconds(10), Duration.ofSeconds(10), Distance.of(11113.275390625), Speed.of(1111.3275146484375), new AltitudeExtremities(0.0, 0.0), new AltitudeGainLoss(0, 0), null, null)
|
||||
, contentProviderUtils.getTrack(trackId).getStatistics());
|
||||
|
||||
|
||||
// then
|
||||
@@ -623,8 +613,7 @@ public class TrackRecordingServiceRecordingTest {
|
||||
null, null,
|
||||
null,
|
||||
Speed.of(15)))
|
||||
.setAltitudeGain(0f)
|
||||
.setAltitudeLoss(0f),
|
||||
.setAltitudeGainLoss(0, 0),
|
||||
new TrackPoint(TrackPoint.Type.SEGMENT_START_AUTOMATIC,
|
||||
new Position(
|
||||
Instant.parse(gps2),
|
||||
@@ -632,11 +621,9 @@ public class TrackRecordingServiceRecordingTest {
|
||||
null, null,
|
||||
null,
|
||||
Speed.of(15)))
|
||||
.setAltitudeGain(0f)
|
||||
.setAltitudeLoss(0f),
|
||||
.setAltitudeGainLoss(0, 0),
|
||||
new TrackPoint(TrackPoint.Type.SEGMENT_END_MANUAL, Instant.parse(stopTime))
|
||||
.setAltitudeGain(0f)
|
||||
.setAltitudeLoss(0f)
|
||||
.setAltitudeGainLoss(0, 0)
|
||||
), TestDataUtil.getTrackPoints(contentProviderUtils, trackId));
|
||||
}
|
||||
|
||||
@@ -669,7 +656,7 @@ public class TrackRecordingServiceRecordingTest {
|
||||
|
||||
// when
|
||||
String gps1 = "2020-02-02T02:02:05Z";
|
||||
TrackRecordingServiceTestUtils.sendGPSLocation(trackPointCreator, gps1, 45.0, 35.0, 1, 15);
|
||||
sendGPSLocation(trackPointCreator, gps1, 45.0, 35.0, 1, 15);
|
||||
|
||||
// when
|
||||
String sensor3 = "2020-02-02T02:02:06Z";
|
||||
@@ -683,7 +670,7 @@ public class TrackRecordingServiceRecordingTest {
|
||||
|
||||
// when
|
||||
String gps2 = "2020-02-02T02:02:08Z";
|
||||
TrackRecordingServiceTestUtils.sendGPSLocation(trackPointCreator, gps2, 45.0, 35.0, 4, 15); //Should be ignored
|
||||
sendGPSLocation(trackPointCreator, gps2, 45.0, 35.0, 4, 15); //Should be ignored
|
||||
|
||||
// when
|
||||
String sensor5 = "2020-02-02T02:02:10Z";
|
||||
@@ -692,11 +679,11 @@ public class TrackRecordingServiceRecordingTest {
|
||||
|
||||
// when
|
||||
String gps3 = "2020-02-02T02:02:12Z";
|
||||
TrackRecordingServiceTestUtils.sendGPSLocation(trackPointCreator, gps3, 45.001, 35.0, 1, 15);
|
||||
sendGPSLocation(trackPointCreator, gps3, 45.001, 35.0, 1, 15);
|
||||
|
||||
// when
|
||||
String gps4 = "2020-02-02T02:02:14Z";
|
||||
TrackRecordingServiceTestUtils.sendGPSLocation(trackPointCreator, gps4, 45.001, 35.0, 1, 15);
|
||||
sendGPSLocation(trackPointCreator, gps4, 45.001, 35.0, 1, 15);
|
||||
|
||||
|
||||
// when
|
||||
@@ -761,4 +748,16 @@ public class TrackRecordingServiceRecordingTest {
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
private static void sendGPSLocation(TrackPointCreator trackPointCreator, String time, double latitude, double longitude, float accuracy, long speed) {
|
||||
Location location = new Location("mock");
|
||||
location.setTime(1L); // Should be ignored anyhow.
|
||||
location.setLatitude(latitude);
|
||||
location.setLongitude(longitude);
|
||||
location.setAccuracy(accuracy);
|
||||
location.setSpeed(speed);
|
||||
|
||||
trackPointCreator.setClock(time);
|
||||
trackPointCreator.getSensorManager().getGpsManager().onLocationChanged(location);
|
||||
}
|
||||
}
|
||||
|
||||
-20
@@ -1,20 +0,0 @@
|
||||
package de.dennisguse.opentracks.services;
|
||||
|
||||
import android.location.Location;
|
||||
|
||||
import de.dennisguse.opentracks.services.handlers.TrackPointCreator;
|
||||
|
||||
public class TrackRecordingServiceTestUtils {
|
||||
|
||||
static void sendGPSLocation(TrackPointCreator trackPointCreator, String time, double latitude, double longitude, float accuracy, long speed) {
|
||||
Location location = new Location("mock");
|
||||
location.setTime(1L); // Should be ignored anyhow.
|
||||
location.setLatitude(latitude);
|
||||
location.setLongitude(longitude);
|
||||
location.setAccuracy(accuracy);
|
||||
location.setSpeed(speed);
|
||||
|
||||
trackPointCreator.setClock(time);
|
||||
trackPointCreator.getSensorManager().getGpsManager().onLocationChanged(location);
|
||||
}
|
||||
}
|
||||
+7
-7
@@ -22,7 +22,7 @@ import java.util.concurrent.TimeoutException;
|
||||
|
||||
import de.dennisguse.opentracks.data.models.Distance;
|
||||
import de.dennisguse.opentracks.services.TrackRecordingService;
|
||||
import de.dennisguse.opentracks.stats.TrackStatistics;
|
||||
import de.dennisguse.opentracks.stats.SegmentStatisticUpdater;
|
||||
|
||||
@RunWith(AndroidJUnit4.class)
|
||||
public class VoiceAnnouncementManagerTest {
|
||||
@@ -54,13 +54,13 @@ public class VoiceAnnouncementManagerTest {
|
||||
voiceAnnouncementManager.setFrequency(Distance.ofKilometer(5));
|
||||
|
||||
// when
|
||||
TrackStatistics statistics = new TrackStatistics();
|
||||
SegmentStatisticUpdater statistics = new SegmentStatisticUpdater();
|
||||
statistics.setTotalDistance(Distance.ofKilometer(13));
|
||||
voiceAnnouncementManager.start(statistics);
|
||||
voiceAnnouncementManager.start(statistics.getStatistics());
|
||||
assertEquals(Distance.of(15000), voiceAnnouncementManager.getNextTotalDistance());
|
||||
|
||||
statistics.setTotalDistance(Distance.of(15100));
|
||||
voiceAnnouncementManager.start(statistics);
|
||||
voiceAnnouncementManager.start(statistics.getStatistics());
|
||||
assertEquals(Distance.of(20000), voiceAnnouncementManager.getNextTotalDistance());
|
||||
}
|
||||
|
||||
@@ -74,13 +74,13 @@ public class VoiceAnnouncementManagerTest {
|
||||
voiceAnnouncementManager.setFrequency(Duration.ofSeconds(5));
|
||||
|
||||
// when
|
||||
TrackStatistics statistics = new TrackStatistics();
|
||||
SegmentStatisticUpdater statistics = new SegmentStatisticUpdater();
|
||||
statistics.setTotalTime(Duration.ofSeconds(91));
|
||||
voiceAnnouncementManager.start(statistics);
|
||||
voiceAnnouncementManager.start(statistics.getStatistics());
|
||||
assertEquals(Duration.ofSeconds(95), voiceAnnouncementManager.getNextTotalTime());
|
||||
|
||||
statistics.setTotalTime(Duration.ofSeconds(95));
|
||||
voiceAnnouncementManager.start(statistics);
|
||||
voiceAnnouncementManager.start(statistics.getStatistics());
|
||||
assertEquals(Duration.ofSeconds(100), voiceAnnouncementManager.getNextTotalTime());
|
||||
}
|
||||
|
||||
|
||||
+168
-192
@@ -19,23 +19,24 @@ import org.mockito.Mockito;
|
||||
|
||||
import java.time.Duration;
|
||||
import java.time.Instant;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Locale;
|
||||
import java.util.Map;
|
||||
|
||||
import de.dennisguse.opentracks.LocaleRule;
|
||||
import de.dennisguse.opentracks.content.data.TestDataUtil;
|
||||
import de.dennisguse.opentracks.data.ContentProviderUtils;
|
||||
import de.dennisguse.opentracks.data.TrackPointIterator;
|
||||
import de.dennisguse.opentracks.data.models.AltitudeGainLoss;
|
||||
import de.dennisguse.opentracks.data.models.Distance;
|
||||
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.models.TrackPoint;
|
||||
import de.dennisguse.opentracks.sensors.sensorData.SensorDataSet;
|
||||
import de.dennisguse.opentracks.settings.PreferencesUtils;
|
||||
import de.dennisguse.opentracks.settings.UnitSystem;
|
||||
import de.dennisguse.opentracks.stats.SensorStatistics;
|
||||
import de.dennisguse.opentracks.stats.TrackStatistics;
|
||||
import de.dennisguse.opentracks.ui.intervals.IntervalStatistics;
|
||||
import de.dennisguse.opentracks.ui.intervals.IntervalStatisticsUpdater;
|
||||
|
||||
@RunWith(AndroidJUnit4.class)
|
||||
public class VoiceAnnouncementUtilsTest {
|
||||
@@ -44,12 +45,9 @@ public class VoiceAnnouncementUtilsTest {
|
||||
public final LocaleRule mLocaleRule = new LocaleRule(Locale.ENGLISH);
|
||||
|
||||
private final Context context = ApplicationProvider.getApplicationContext();
|
||||
private ContentProviderUtils contentProviderUtils;
|
||||
|
||||
@Before
|
||||
public void setUp() {
|
||||
contentProviderUtils = new ContentProviderUtils(context);
|
||||
|
||||
PreferencesUtils.setVoiceAnnounceHeartRateCurrent(false);
|
||||
PreferencesUtils.setVoiceAnnounceLapHeartRate(false);
|
||||
PreferencesUtils.setVoiceAnnounceAverageHeartRate(false);
|
||||
@@ -66,16 +64,18 @@ public class VoiceAnnouncementUtilsTest {
|
||||
|
||||
@Test
|
||||
public void getAnnouncement_metric_speed() {
|
||||
TrackStatistics stats = new TrackStatistics();
|
||||
stats.setStartTime(Instant.EPOCH);
|
||||
stats.setTotalDistance(Distance.of(20000));
|
||||
stats.setTotalTime(Duration.ofHours(2).plusMinutes(5).plusSeconds(10));
|
||||
stats.setMovingTime(Duration.ofHours(1).plusMinutes(5).plusSeconds(10));
|
||||
stats.setMaxSpeed(Speed.of(100));
|
||||
stats.setTotalAltitudeGain(6000f);
|
||||
|
||||
Track track = new Track();
|
||||
track.setTrackStatistics(stats);
|
||||
Track track = new Track(new Statistics(
|
||||
Instant.EPOCH,
|
||||
Instant.EPOCH,
|
||||
Duration.ofHours(2).plusMinutes(5).plusSeconds(10),
|
||||
Duration.ofHours(1).plusMinutes(5).plusSeconds(10),
|
||||
Distance.of(20000),
|
||||
Speed.of(100),
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
null
|
||||
));
|
||||
|
||||
SensorDataSet dataSet = Mockito.mock(SensorDataSet.class);
|
||||
Mockito.when(dataSet.getHeartRate()).thenReturn(new Pair<>(HeartRate.of(60), "unused"));
|
||||
@@ -89,16 +89,18 @@ public class VoiceAnnouncementUtilsTest {
|
||||
|
||||
@Test
|
||||
public void getAnnouncement_metric_speed_rounding_check() {
|
||||
TrackStatistics stats = new TrackStatistics();
|
||||
stats.setStartTime(Instant.EPOCH);
|
||||
stats.setTotalDistance(Distance.of(20000));
|
||||
stats.setTotalTime(Duration.ofHours(1).plusMinutes(5).plusSeconds(10));
|
||||
stats.setMovingTime(Duration.ofHours(1).plusSeconds(1));
|
||||
stats.setMaxSpeed(Speed.of(100));
|
||||
stats.setTotalAltitudeGain(6000f);
|
||||
|
||||
Track track = new Track();
|
||||
track.setTrackStatistics(stats);
|
||||
Track track = new Track(new Statistics(
|
||||
Instant.EPOCH,
|
||||
Instant.EPOCH,
|
||||
Duration.ofHours(2).plusMinutes(5).plusSeconds(10),
|
||||
Duration.ofHours(1).plusSeconds(1),
|
||||
Distance.of(20000),
|
||||
Speed.of(100),
|
||||
null,
|
||||
new AltitudeGainLoss(6000, 6000),
|
||||
null,
|
||||
null
|
||||
));
|
||||
|
||||
SensorDataSet dataSet = Mockito.mock(SensorDataSet.class);
|
||||
Mockito.when(dataSet.getHeartRate()).thenReturn(new Pair<>(HeartRate.of(60), "unused"));
|
||||
@@ -112,16 +114,18 @@ public class VoiceAnnouncementUtilsTest {
|
||||
|
||||
@Test
|
||||
public void getAnnouncement_metric_distance_rounding_check() {
|
||||
TrackStatistics stats = new TrackStatistics();
|
||||
stats.setStartTime(Instant.EPOCH);
|
||||
stats.setTotalDistance(Distance.of(19999));
|
||||
stats.setTotalTime(Duration.ofHours(1).plusMinutes(5).plusSeconds(10));
|
||||
stats.setMovingTime(Duration.ofHours(1));
|
||||
stats.setMaxSpeed(Speed.of(100));
|
||||
stats.setTotalAltitudeGain(6000f);
|
||||
|
||||
Track track = new Track();
|
||||
track.setTrackStatistics(stats);
|
||||
Track track = new Track(new Statistics(
|
||||
Instant.EPOCH,
|
||||
Instant.EPOCH,
|
||||
Duration.ofHours(2).plusMinutes(5).plusSeconds(10),
|
||||
Duration.ofHours(1),
|
||||
Distance.of(19999),
|
||||
Speed.of(100),
|
||||
null,
|
||||
new AltitudeGainLoss(6000, 6000),
|
||||
null,
|
||||
null
|
||||
));
|
||||
|
||||
SensorDataSet dataSet = Mockito.mock(SensorDataSet.class);
|
||||
Mockito.when(dataSet.getHeartRate()).thenReturn(new Pair<>(HeartRate.of(60), "unused"));
|
||||
@@ -135,16 +139,18 @@ public class VoiceAnnouncementUtilsTest {
|
||||
|
||||
@Test
|
||||
public void getAnnouncement_metric_distance_rounding_check_two() {
|
||||
TrackStatistics stats = new TrackStatistics();
|
||||
stats.setStartTime(Instant.EPOCH);
|
||||
stats.setTotalDistance(Distance.of(19900));
|
||||
stats.setTotalTime(Duration.ofHours(1).plusMinutes(5).plusSeconds(10));
|
||||
stats.setMovingTime(Duration.ofHours(1));
|
||||
stats.setMaxSpeed(Speed.of(100));
|
||||
stats.setTotalAltitudeGain(6000f);
|
||||
|
||||
Track track = new Track();
|
||||
track.setTrackStatistics(stats);
|
||||
Track track = new Track(new Statistics(
|
||||
Instant.EPOCH,
|
||||
Instant.EPOCH,
|
||||
Duration.ofHours(2).plusMinutes(5).plusSeconds(10),
|
||||
Duration.ofHours(1),
|
||||
Distance.of(19900),
|
||||
Speed.of(100),
|
||||
null,
|
||||
new AltitudeGainLoss(6000, 6000),
|
||||
null,
|
||||
null
|
||||
));
|
||||
|
||||
SensorDataSet dataSet = Mockito.mock(SensorDataSet.class);
|
||||
Mockito.when(dataSet.getHeartRate()).thenReturn(new Pair<>(HeartRate.of(60), "unused"));
|
||||
@@ -159,26 +165,13 @@ public class VoiceAnnouncementUtilsTest {
|
||||
@Test
|
||||
public void getAnnouncement_withInterval_metric_speed() {
|
||||
// given
|
||||
int numberOfPoints = 1000;
|
||||
Pair<Track.Id, TrackStatistics> trackWithStats = TestDataUtil.buildTrackWithTrackPoints(contentProviderUtils, numberOfPoints);
|
||||
Track.Id trackId = trackWithStats.first;
|
||||
TrackStatistics stats = trackWithStats.second;
|
||||
IntervalStatistics.Interval lastInterval;
|
||||
try (TrackPointIterator trackPointIterator = contentProviderUtils.getTrackPointLocationIterator(trackId, null)) {
|
||||
assertEquals(numberOfPoints, trackPointIterator.getCount());
|
||||
IntervalStatistics intervalStatistics = new IntervalStatistics(Distance.of(1000));
|
||||
intervalStatistics.addTrackPoints(trackPointIterator);
|
||||
lastInterval = intervalStatistics.getIntervalList().get(intervalStatistics.getIntervalList().size() - 1);
|
||||
}
|
||||
|
||||
Pair<Track, Statistics> data = buildTrackWithTrackPoints();
|
||||
|
||||
SensorDataSet dataSet = Mockito.mock(SensorDataSet.class);
|
||||
Mockito.when(dataSet.getHeartRate()).thenReturn(new Pair<>(HeartRate.of(60), "unused"));
|
||||
|
||||
Track track = new Track();
|
||||
track.setTrackStatistics(stats);
|
||||
|
||||
// when
|
||||
String announcement = VoiceAnnouncementUtils.createStatistics(context, track, dataSet, UnitSystem.METRIC, true, lastInterval, null).toString();
|
||||
String announcement = VoiceAnnouncementUtils.createStatistics(context, data.first, dataSet, UnitSystem.METRIC, true, data.second, null).toString();
|
||||
|
||||
// then
|
||||
assertEquals("12:16 AM. Total distance 14.2 kilometers. 16 minutes 39 seconds. Average moving speed 51.2 kilometers per hour. Lap speed 51.2 kilometers per hour.", announcement);
|
||||
@@ -186,16 +179,18 @@ public class VoiceAnnouncementUtilsTest {
|
||||
|
||||
@Test
|
||||
public void getAnnouncement_metric_pace() {
|
||||
TrackStatistics stats = new TrackStatistics();
|
||||
stats.setStartTime(Instant.EPOCH);
|
||||
stats.setTotalDistance(Distance.of(20000));
|
||||
stats.setTotalTime(Duration.ofHours(2).plusMinutes(5).plusSeconds(10));
|
||||
stats.setMovingTime(Duration.ofHours(1).plusMinutes(5).plusSeconds(10));
|
||||
stats.setMaxSpeed(Speed.of(100));
|
||||
stats.setTotalAltitudeGain(6000f);
|
||||
|
||||
Track track = new Track();
|
||||
track.setTrackStatistics(stats);
|
||||
Track track = new Track(new Statistics(
|
||||
Instant.EPOCH,
|
||||
Instant.EPOCH,
|
||||
Duration.ofHours(2).plusMinutes(5).plusSeconds(10),
|
||||
Duration.ofHours(1).plusMinutes(5).plusSeconds(10),
|
||||
Distance.of(20000),
|
||||
Speed.of(100),
|
||||
null,
|
||||
new AltitudeGainLoss(6000, 6000),
|
||||
null,
|
||||
null
|
||||
));
|
||||
|
||||
SensorDataSet dataSet = Mockito.mock(SensorDataSet.class);
|
||||
Mockito.when(dataSet.getHeartRate()).thenReturn(new Pair<>(HeartRate.of(60), "unused"));
|
||||
@@ -210,26 +205,13 @@ public class VoiceAnnouncementUtilsTest {
|
||||
@Test
|
||||
public void getAnnouncement_withInterval_metric_pace() {
|
||||
// given
|
||||
int numberOfPoints = 1000;
|
||||
Pair<Track.Id, TrackStatistics> trackWithStats = TestDataUtil.buildTrackWithTrackPoints(contentProviderUtils, numberOfPoints);
|
||||
Track.Id trackId = trackWithStats.first;
|
||||
TrackStatistics stats = trackWithStats.second;
|
||||
IntervalStatistics.Interval lastInterval;
|
||||
try (TrackPointIterator trackPointIterator = contentProviderUtils.getTrackPointLocationIterator(trackId, null)) {
|
||||
assertEquals(numberOfPoints, trackPointIterator.getCount());
|
||||
IntervalStatistics intervalStatistics = new IntervalStatistics(Distance.of(1000));
|
||||
intervalStatistics.addTrackPoints(trackPointIterator);
|
||||
lastInterval = intervalStatistics.getIntervalList().get(intervalStatistics.getIntervalList().size() - 1);
|
||||
}
|
||||
|
||||
Track track = new Track();
|
||||
track.setTrackStatistics(stats);
|
||||
Pair<Track, Statistics> data = buildTrackWithTrackPoints();
|
||||
|
||||
SensorDataSet dataSet = Mockito.mock(SensorDataSet.class);
|
||||
Mockito.when(dataSet.getHeartRate()).thenReturn(new Pair<>(HeartRate.of(60), "unused"));
|
||||
|
||||
// when
|
||||
String announcement = VoiceAnnouncementUtils.createStatistics(context, track, dataSet, UnitSystem.METRIC, false, lastInterval, null).toString();
|
||||
String announcement = VoiceAnnouncementUtils.createStatistics(context, data.first, dataSet, UnitSystem.METRIC, false, data.second, null).toString();
|
||||
|
||||
// then
|
||||
assertEquals("12:16 AM. Total distance 14.2 kilometers. 16 minutes 39 seconds. Pace 1 minute 10 seconds per kilometer. Lap time 1 minute 10 seconds per kilometer.", announcement);
|
||||
@@ -237,16 +219,18 @@ public class VoiceAnnouncementUtilsTest {
|
||||
|
||||
@Test
|
||||
public void getAnnouncement_imperial_speed() {
|
||||
TrackStatistics stats = new TrackStatistics();
|
||||
stats.setStartTime(Instant.EPOCH);
|
||||
stats.setTotalDistance(Distance.of(20000));
|
||||
stats.setTotalTime(Duration.ofHours(2).plusMinutes(5).plusSeconds(10));
|
||||
stats.setMovingTime(Duration.ofHours(1).plusMinutes(5).plusSeconds(10));
|
||||
stats.setMaxSpeed(Speed.of(100));
|
||||
stats.setTotalAltitudeGain(6000f);
|
||||
|
||||
Track track = new Track();
|
||||
track.setTrackStatistics(stats);
|
||||
Track track = new Track(new Statistics(
|
||||
Instant.EPOCH,
|
||||
Instant.EPOCH,
|
||||
Duration.ofHours(2).plusMinutes(5).plusSeconds(10),
|
||||
Duration.ofHours(1).plusMinutes(5).plusSeconds(10),
|
||||
Distance.of(20000),
|
||||
Speed.of(100),
|
||||
null,
|
||||
new AltitudeGainLoss(6000, 6000),
|
||||
null,
|
||||
null
|
||||
));
|
||||
|
||||
SensorDataSet dataSet = Mockito.mock(SensorDataSet.class);
|
||||
Mockito.when(dataSet.getHeartRate()).thenReturn(new Pair<>(HeartRate.of(60), "unused"));
|
||||
@@ -260,14 +244,18 @@ public class VoiceAnnouncementUtilsTest {
|
||||
|
||||
@Test
|
||||
public void getAnnouncement_imperial_speed_1() {
|
||||
TrackStatistics stats = new TrackStatistics();
|
||||
stats.setStartTime(Instant.EPOCH);
|
||||
stats.setTotalDistance(Distance.ofMile(1.1));
|
||||
stats.setTotalTime(Duration.ofHours(2).plusMinutes(5).plusSeconds(10));
|
||||
stats.setMovingTime(Duration.ofHours(1));
|
||||
|
||||
Track track = new Track();
|
||||
track.setTrackStatistics(stats);
|
||||
Track track = new Track(new Statistics(
|
||||
Instant.EPOCH,
|
||||
Instant.EPOCH,
|
||||
Duration.ofHours(2).plusMinutes(5).plusSeconds(10),
|
||||
Duration.ofHours(1),
|
||||
Distance.ofMile(1.1),
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
null
|
||||
));
|
||||
|
||||
SensorDataSet dataSet = Mockito.mock(SensorDataSet.class);
|
||||
Mockito.when(dataSet.getHeartRate()).thenReturn(new Pair<>(HeartRate.of(60), "unused"));
|
||||
@@ -281,14 +269,18 @@ public class VoiceAnnouncementUtilsTest {
|
||||
|
||||
@Test
|
||||
public void getAnnouncement_imperial_meter_speed_1() {
|
||||
TrackStatistics stats = new TrackStatistics();
|
||||
stats.setStartTime(Instant.EPOCH);
|
||||
stats.setTotalDistance(Distance.ofMile(1.1));
|
||||
stats.setTotalTime(Duration.ofHours(2).plusMinutes(5).plusSeconds(10));
|
||||
stats.setMovingTime(Duration.ofHours(1));
|
||||
|
||||
Track track = new Track();
|
||||
track.setTrackStatistics(stats);
|
||||
Track track = new Track(new Statistics(
|
||||
Instant.EPOCH,
|
||||
Instant.EPOCH,
|
||||
Duration.ofHours(2).plusMinutes(5).plusSeconds(10),
|
||||
Duration.ofHours(1),
|
||||
Distance.ofMile(1.1),
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
null
|
||||
));
|
||||
|
||||
SensorDataSet dataSet = Mockito.mock(SensorDataSet.class);
|
||||
Mockito.when(dataSet.getHeartRate()).thenReturn(new Pair<>(HeartRate.of(60), "unused"));
|
||||
@@ -302,14 +294,18 @@ public class VoiceAnnouncementUtilsTest {
|
||||
|
||||
@Test
|
||||
public void getAnnouncement_metric_speed_1() {
|
||||
TrackStatistics stats = new TrackStatistics();
|
||||
stats.setStartTime(Instant.EPOCH);
|
||||
stats.setTotalDistance(Distance.ofKilometer(1.1));
|
||||
stats.setTotalTime(Duration.ofHours(2).plusMinutes(5).plusSeconds(10));
|
||||
stats.setMovingTime(Duration.ofHours(1));
|
||||
|
||||
Track track = new Track();
|
||||
track.setTrackStatistics(stats);
|
||||
Track track = new Track(new Statistics(
|
||||
Instant.EPOCH,
|
||||
Instant.EPOCH,
|
||||
Duration.ofHours(2).plusMinutes(5).plusSeconds(10),
|
||||
Duration.ofHours(1),
|
||||
Distance.ofKilometer(1.1),
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
null
|
||||
));
|
||||
|
||||
SensorDataSet dataSet = Mockito.mock(SensorDataSet.class);
|
||||
Mockito.when(dataSet.getHeartRate()).thenReturn(new Pair<>(HeartRate.of(60), "unused"));
|
||||
@@ -324,26 +320,13 @@ public class VoiceAnnouncementUtilsTest {
|
||||
@Test
|
||||
public void getAnnouncement_withInterval_imperial_speed() {
|
||||
// given
|
||||
int numberOfPoints = 1000;
|
||||
Pair<Track.Id, TrackStatistics> trackWithStats = TestDataUtil.buildTrackWithTrackPoints(contentProviderUtils, numberOfPoints);
|
||||
Track.Id trackId = trackWithStats.first;
|
||||
TrackStatistics stats = trackWithStats.second;
|
||||
IntervalStatistics.Interval lastInterval;
|
||||
try (TrackPointIterator trackPointIterator = contentProviderUtils.getTrackPointLocationIterator(trackId, null)) {
|
||||
assertEquals(numberOfPoints, trackPointIterator.getCount());
|
||||
IntervalStatistics intervalStatistics = new IntervalStatistics(Distance.of(1000));
|
||||
intervalStatistics.addTrackPoints(trackPointIterator);
|
||||
lastInterval = intervalStatistics.getIntervalList().get(intervalStatistics.getIntervalList().size() - 1);
|
||||
}
|
||||
|
||||
Track track = new Track();
|
||||
track.setTrackStatistics(stats);
|
||||
Pair<Track, Statistics> data = buildTrackWithTrackPoints();
|
||||
|
||||
SensorDataSet dataSet = Mockito.mock(SensorDataSet.class);
|
||||
Mockito.when(dataSet.getHeartRate()).thenReturn(new Pair<>(HeartRate.of(60), "unused"));
|
||||
|
||||
// when
|
||||
String announcement = VoiceAnnouncementUtils.createStatistics(context, track, dataSet, UnitSystem.IMPERIAL_FEET, true, lastInterval, null).toString();
|
||||
String announcement = VoiceAnnouncementUtils.createStatistics(context, data.first, dataSet, UnitSystem.IMPERIAL_FEET, true, data.second, null).toString();
|
||||
|
||||
// then
|
||||
assertEquals("12:16 AM. Total distance 8.8 miles. 16 minutes 39 seconds. Average moving speed 31.8 miles per hour. Lap speed 31.8 miles per hour.", announcement);
|
||||
@@ -351,16 +334,18 @@ public class VoiceAnnouncementUtilsTest {
|
||||
|
||||
@Test
|
||||
public void getAnnouncement_imperial_pace() {
|
||||
TrackStatistics stats = new TrackStatistics();
|
||||
stats.setStartTime(Instant.EPOCH);
|
||||
stats.setTotalDistance(Distance.of(20000));
|
||||
stats.setTotalTime(Duration.ofHours(2).plusMinutes(5).plusSeconds(10));
|
||||
stats.setMovingTime(Duration.ofHours(1).plusMinutes(5).plusSeconds(10));
|
||||
stats.setMaxSpeed(Speed.of(100));
|
||||
stats.setTotalAltitudeGain(6000f);
|
||||
|
||||
Track track = new Track();
|
||||
track.setTrackStatistics(stats);
|
||||
Track track = new Track(new Statistics(
|
||||
Instant.EPOCH,
|
||||
Instant.EPOCH,
|
||||
Duration.ofHours(2).plusMinutes(5).plusSeconds(10),
|
||||
Duration.ofHours(1).plusMinutes(5).plusSeconds(10),
|
||||
Distance.of(20000),
|
||||
Speed.of(100),
|
||||
null,
|
||||
new AltitudeGainLoss(6000, 6000),
|
||||
null,
|
||||
null
|
||||
));
|
||||
|
||||
SensorDataSet dataSet = Mockito.mock(SensorDataSet.class);
|
||||
Mockito.when(dataSet.getHeartRate()).thenReturn(new Pair<>(HeartRate.of(60), "unused"));
|
||||
@@ -375,26 +360,13 @@ public class VoiceAnnouncementUtilsTest {
|
||||
@Test
|
||||
public void getAnnouncement_withInterval_imperial_pace() {
|
||||
// given
|
||||
int numberOfPoints = 1000;
|
||||
Pair<Track.Id, TrackStatistics> trackWithStats = TestDataUtil.buildTrackWithTrackPoints(contentProviderUtils, numberOfPoints);
|
||||
Track.Id trackId = trackWithStats.first;
|
||||
TrackStatistics stats = trackWithStats.second;
|
||||
IntervalStatistics.Interval lastInterval;
|
||||
try (TrackPointIterator trackPointIterator = contentProviderUtils.getTrackPointLocationIterator(trackId, null)) {
|
||||
assertEquals(numberOfPoints, trackPointIterator.getCount());
|
||||
IntervalStatistics intervalStatistics = new IntervalStatistics(Distance.of(1000));
|
||||
intervalStatistics.addTrackPoints(trackPointIterator);
|
||||
lastInterval = intervalStatistics.getIntervalList().get(intervalStatistics.getIntervalList().size() - 1);
|
||||
}
|
||||
|
||||
Track track = new Track();
|
||||
track.setTrackStatistics(stats);
|
||||
Pair<Track, Statistics> data = buildTrackWithTrackPoints();
|
||||
|
||||
SensorDataSet dataSet = Mockito.mock(SensorDataSet.class);
|
||||
Mockito.when(dataSet.getHeartRate()).thenReturn(new Pair<>(HeartRate.of(60), "unused"));
|
||||
|
||||
// when
|
||||
String announcement = VoiceAnnouncementUtils.createStatistics(context, track, dataSet, UnitSystem.IMPERIAL_FEET, false, lastInterval, null).toString();
|
||||
String announcement = VoiceAnnouncementUtils.createStatistics(context, data.first, dataSet, UnitSystem.IMPERIAL_FEET, false, data.second, null).toString();
|
||||
|
||||
// then
|
||||
assertEquals("12:16 AM. Total distance 8.8 miles. 16 minutes 39 seconds. Pace 1 minute 53 seconds per mile. Lap time 1 minute 53 seconds per mile.", announcement);
|
||||
@@ -402,31 +374,19 @@ public class VoiceAnnouncementUtilsTest {
|
||||
|
||||
@Test
|
||||
public void getAnnouncement_heart_rate_and_sensor_statistics() {
|
||||
// given
|
||||
PreferencesUtils.setVoiceAnnounceAverageHeartRate(true);
|
||||
PreferencesUtils.setVoiceAnnounceLapHeartRate(true);
|
||||
|
||||
int numberOfPoints = 1000;
|
||||
Pair<Track.Id, TrackStatistics> trackWithStats = TestDataUtil.buildTrackWithTrackPoints(contentProviderUtils, numberOfPoints);
|
||||
Track.Id trackId = trackWithStats.first;
|
||||
TrackStatistics stats = trackWithStats.second;
|
||||
IntervalStatistics.Interval lastInterval;
|
||||
try (TrackPointIterator trackPointIterator = contentProviderUtils.getTrackPointLocationIterator(trackId, null)) {
|
||||
assertEquals(numberOfPoints, trackPointIterator.getCount());
|
||||
IntervalStatistics intervalStatistics = new IntervalStatistics(Distance.of(1000));
|
||||
intervalStatistics.addTrackPoints(trackPointIterator);
|
||||
lastInterval = intervalStatistics.getIntervalList().get(intervalStatistics.getIntervalList().size() - 1);
|
||||
}
|
||||
Pair<Track, Statistics> data = buildTrackWithTrackPoints();
|
||||
|
||||
SensorStatistics sensorStatistics = new SensorStatistics(HeartRate.of(180f), HeartRate.of(180f), null, null, null, null);
|
||||
|
||||
Track track = new Track();
|
||||
track.setTrackStatistics(stats);
|
||||
|
||||
SensorDataSet dataSet = Mockito.mock(SensorDataSet.class);
|
||||
Mockito.when(dataSet.getHeartRate()).thenReturn(new Pair<>(HeartRate.of(60), "unused"));
|
||||
|
||||
// when
|
||||
String announcement = VoiceAnnouncementUtils.createStatistics(context, track, dataSet, UnitSystem.METRIC, true, lastInterval, sensorStatistics).toString();
|
||||
String announcement = VoiceAnnouncementUtils.createStatistics(context, data.first, dataSet, UnitSystem.METRIC, true, data.second, sensorStatistics).toString();
|
||||
|
||||
// then
|
||||
assertEquals("12:16 AM. Total distance 14.2 kilometers. 16 minutes 39 seconds. Average moving speed 51.2 kilometers per hour. Lap speed 51.2 kilometers per hour. Average heart rate 180 bpm. Lap heart rate 133 bpm.", announcement);
|
||||
@@ -434,6 +394,7 @@ public class VoiceAnnouncementUtilsTest {
|
||||
|
||||
@Test
|
||||
public void getAnnouncement_heart_rate() {
|
||||
// given
|
||||
PreferencesUtils.setVoiceAnnounceHeartRateCurrent(true);
|
||||
PreferencesUtils.setVoiceAnnounceLapHeartRate(true);
|
||||
PreferencesUtils.setVoiceAnnounceAverageHeartRate(true);
|
||||
@@ -443,28 +404,15 @@ public class VoiceAnnouncementUtilsTest {
|
||||
PreferencesUtils.setVoiceAnnounceAverageSpeedPace(false);
|
||||
PreferencesUtils.setVoiceAnnounceLapSpeedPace(false);
|
||||
|
||||
int numberOfPoints = 1000;
|
||||
Pair<Track.Id, TrackStatistics> trackWithStats = TestDataUtil.buildTrackWithTrackPoints(contentProviderUtils, numberOfPoints);
|
||||
Track.Id trackId = trackWithStats.first;
|
||||
TrackStatistics stats = trackWithStats.second;
|
||||
IntervalStatistics.Interval lastInterval;
|
||||
try (TrackPointIterator trackPointIterator = contentProviderUtils.getTrackPointLocationIterator(trackId, null)) {
|
||||
assertEquals(numberOfPoints, trackPointIterator.getCount());
|
||||
IntervalStatistics intervalStatistics = new IntervalStatistics(Distance.of(1000));
|
||||
intervalStatistics.addTrackPoints(trackPointIterator);
|
||||
lastInterval = intervalStatistics.getIntervalList().get(intervalStatistics.getIntervalList().size() - 1);
|
||||
}
|
||||
Pair<Track, Statistics> data = buildTrackWithTrackPoints();
|
||||
|
||||
SensorStatistics sensorStatistics = new SensorStatistics(HeartRate.of(180f), HeartRate.of(180f), null, null, null, null);
|
||||
|
||||
Track track = new Track();
|
||||
track.setTrackStatistics(stats);
|
||||
|
||||
SensorDataSet dataSet = Mockito.mock(SensorDataSet.class);
|
||||
Mockito.when(dataSet.getHeartRate()).thenReturn(new Pair<>(HeartRate.of(60), "unused"));
|
||||
|
||||
// when
|
||||
String announcement = VoiceAnnouncementUtils.createStatistics(context, track, dataSet, UnitSystem.METRIC, true, lastInterval, sensorStatistics).toString();
|
||||
String announcement = VoiceAnnouncementUtils.createStatistics(context, data.first, dataSet, UnitSystem.METRIC, true, data.second, sensorStatistics).toString();
|
||||
|
||||
// then
|
||||
assertEquals("12:16 AM. Current heart rate 60 bpm. Average heart rate 180 bpm. Lap heart rate 133 bpm.", announcement);
|
||||
@@ -484,4 +432,32 @@ public class VoiceAnnouncementUtilsTest {
|
||||
assertEquals("1.1 miles", MessageFormat.format(template, Map.of("n", 1.11)));
|
||||
assertEquals("1.2 miles", MessageFormat.format(template, Map.of("n", 1.18)));
|
||||
}
|
||||
|
||||
private static Pair<Track, Statistics> buildTrackWithTrackPoints() {
|
||||
int numberOfPoints = 1000;
|
||||
|
||||
ArrayList<TrackPoint> trackPoints = new ArrayList<>();
|
||||
for (int i = 0; i < numberOfPoints; i++) {
|
||||
trackPoints.add(TestDataUtil.createTrackPoint(i));
|
||||
}
|
||||
|
||||
IntervalStatisticsUpdater intervalStatistics = new IntervalStatisticsUpdater(Distance.of(1000));
|
||||
intervalStatistics.addTrackPoints(trackPoints.iterator());
|
||||
Statistics lastInterval = intervalStatistics.getLastInterval();
|
||||
|
||||
Track track = new Track(new Statistics(
|
||||
Instant.EPOCH,
|
||||
Instant.EPOCH.plusSeconds(999),
|
||||
Duration.ofMinutes(16).plusSeconds(39),
|
||||
Duration.ofMinutes(16).plusSeconds(39),
|
||||
Distance.of(14208),
|
||||
Speed.of(104),
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
null
|
||||
));
|
||||
|
||||
return new Pair<>(track, lastInterval);
|
||||
}
|
||||
}
|
||||
|
||||
+16
-17
@@ -18,8 +18,6 @@ package de.dennisguse.opentracks.share;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
|
||||
import android.content.Context;
|
||||
|
||||
import androidx.test.core.app.ApplicationProvider;
|
||||
import androidx.test.ext.junit.runners.AndroidJUnit4;
|
||||
|
||||
@@ -36,10 +34,12 @@ import java.util.Locale;
|
||||
|
||||
import de.dennisguse.opentracks.LocaleRule;
|
||||
import de.dennisguse.opentracks.R;
|
||||
import de.dennisguse.opentracks.data.models.AltitudeExtremities;
|
||||
import de.dennisguse.opentracks.data.models.AltitudeGainLoss;
|
||||
import de.dennisguse.opentracks.data.models.Distance;
|
||||
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.stats.TrackStatistics;
|
||||
import de.dennisguse.opentracks.util.StringUtils;
|
||||
|
||||
/**
|
||||
@@ -56,8 +56,6 @@ public class TrackShareSummaryGeneratorTest {
|
||||
private static final Instant START_TIME = Instant.ofEpochMilli(1288721514000L);
|
||||
private TrackShareSummaryGenerator descriptionGenerator;
|
||||
|
||||
private final Context context = ApplicationProvider.getApplicationContext();
|
||||
|
||||
@Before
|
||||
public void setUp() {
|
||||
descriptionGenerator = new TrackShareSummaryGenerator(ApplicationProvider.getApplicationContext());
|
||||
@@ -65,19 +63,20 @@ public class TrackShareSummaryGeneratorTest {
|
||||
|
||||
@Test
|
||||
public void testGenerateTrackDescription() {
|
||||
Track track = new Track();
|
||||
TrackStatistics stats = new TrackStatistics();
|
||||
stats.setTotalDistance(Distance.of(20000));
|
||||
stats.setTotalTime(Duration.ofMillis(600000));
|
||||
stats.setMovingTime(Duration.ofMillis(300000));
|
||||
stats.setMaxSpeed(Speed.of(100));
|
||||
stats.setMaxAltitude(550);
|
||||
stats.setMinAltitude(-500);
|
||||
stats.setTotalAltitudeGain(6000f);
|
||||
stats.setTotalAltitudeLoss(6000f);
|
||||
stats.setStartTime(START_TIME);
|
||||
track.setTrackStatistics(stats);
|
||||
Track track = new Track(new Statistics(
|
||||
START_TIME,
|
||||
START_TIME,
|
||||
Duration.ofSeconds(600),
|
||||
Duration.ofSeconds(300),
|
||||
Distance.of(20000),
|
||||
Speed.of(100),
|
||||
new AltitudeExtremities(-500, 550),
|
||||
new AltitudeGainLoss(6000, 6000),
|
||||
null,
|
||||
null
|
||||
));
|
||||
track.setActivityTypeLocalized("hiking");
|
||||
|
||||
String expected = //"Created by"
|
||||
"OpenTracks (Debug)\n\n"
|
||||
+ "Name: -\n"
|
||||
|
||||
+6
-6
@@ -32,25 +32,25 @@ import de.dennisguse.opentracks.data.models.HeartRate;
|
||||
import de.dennisguse.opentracks.data.models.Speed;
|
||||
|
||||
/**
|
||||
* Tests for {@link TrackStatistics}.
|
||||
* Tests for {@link SegmentStatisticUpdater}.
|
||||
* This only tests non-trivial pieces of that class.
|
||||
*
|
||||
* @author Rodrigo Damazio
|
||||
*/
|
||||
@RunWith(AndroidJUnit4.class)
|
||||
public class TrackStatisticsTest {
|
||||
public class SegmentStatisticUpdaterTest {
|
||||
|
||||
private TrackStatistics statistics;
|
||||
private SegmentStatisticUpdater statistics;
|
||||
|
||||
@Before
|
||||
public void setUp() {
|
||||
statistics = new TrackStatistics();
|
||||
statistics = new SegmentStatisticUpdater();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testMerge_no_data() {
|
||||
// given
|
||||
TrackStatistics statistics2 = new TrackStatistics();
|
||||
SegmentStatisticUpdater statistics2 = new SegmentStatisticUpdater();
|
||||
|
||||
// when
|
||||
statistics.merge(statistics2);
|
||||
@@ -74,7 +74,7 @@ public class TrackStatisticsTest {
|
||||
@Test
|
||||
public void testMerge() {
|
||||
// given
|
||||
TrackStatistics statistics2 = new TrackStatistics();
|
||||
SegmentStatisticUpdater statistics2 = new SegmentStatisticUpdater();
|
||||
statistics.setStartTime(Instant.ofEpochMilli(1000)); // Resulting start time
|
||||
statistics.setStopTime(Instant.ofEpochMilli(2500));
|
||||
statistics2.setStartTime(Instant.ofEpochMilli(3000));
|
||||
+36
-36
@@ -31,7 +31,7 @@ public class TrackStatisticsUpdaterTest {
|
||||
TrackStatisticsUpdater subject = new TrackStatisticsUpdater();
|
||||
|
||||
// then
|
||||
TrackStatistics statistics = subject.getTrackStatistics();
|
||||
SegmentStatisticUpdater statistics = subject.getSegmentStatisticUpdater();
|
||||
assertNull(statistics.getStartTime());
|
||||
assertNull(statistics.getStopTime());
|
||||
assertEquals(Duration.ZERO, statistics.getTotalTime());
|
||||
@@ -58,7 +58,7 @@ public class TrackStatisticsUpdaterTest {
|
||||
subject.addTrackPoint(tp);
|
||||
|
||||
// then
|
||||
TrackStatistics statistics = subject.getTrackStatistics();
|
||||
SegmentStatisticUpdater statistics = subject.getSegmentStatisticUpdater();
|
||||
assertEquals(startTime, statistics.getStartTime());
|
||||
assertEquals(startTime, statistics.getStopTime());
|
||||
assertEquals(Duration.ZERO, statistics.getTotalTime());
|
||||
@@ -83,7 +83,7 @@ public class TrackStatisticsUpdaterTest {
|
||||
data.trackPoints().forEach(subject::addTrackPoint);
|
||||
|
||||
// then
|
||||
TrackStatistics statistics = subject.getTrackStatistics();
|
||||
SegmentStatisticUpdater statistics = subject.getSegmentStatisticUpdater();
|
||||
assertEquals(142.26, statistics.getTotalDistance().toM(), 0.01);
|
||||
assertEquals(Duration.ofSeconds(12), statistics.getTotalTime());
|
||||
assertEquals(Duration.ofSeconds(12), statistics.getMovingTime());
|
||||
@@ -115,7 +115,7 @@ public class TrackStatisticsUpdaterTest {
|
||||
subject.addTrackPoint(tp3);
|
||||
|
||||
// then
|
||||
assertEquals(1.10, subject.getTrackStatistics().getTotalDistance().toM(), 0.01);
|
||||
assertEquals(1.10, subject.getSegmentStatisticUpdater().getTotalDistance().toM(), 0.01);
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -140,14 +140,14 @@ public class TrackStatisticsUpdaterTest {
|
||||
subject.addTrackPoint(tp3);
|
||||
|
||||
// then
|
||||
assertEquals(110.57, subject.getTrackStatistics().getTotalDistance().toM(), 0.01);
|
||||
assertEquals(110.57, subject.getSegmentStatisticUpdater().getTotalDistance().toM(), 0.01);
|
||||
|
||||
// when
|
||||
subject.addTrackPoint(tp4);
|
||||
subject.addTrackPoint(tp5);
|
||||
|
||||
// then
|
||||
assertEquals(125.57, subject.getTrackStatistics().getTotalDistance().toM(), 0.01);
|
||||
assertEquals(125.57, subject.getSegmentStatisticUpdater().getTotalDistance().toM(), 0.01);
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -171,20 +171,20 @@ public class TrackStatisticsUpdaterTest {
|
||||
subject.addTrackPoint(tp3);
|
||||
|
||||
// then
|
||||
assertEquals(5, subject.getTrackStatistics().getTotalDistance().toM(), 0.01);
|
||||
assertEquals(5, subject.getSegmentStatisticUpdater().getTotalDistance().toM(), 0.01);
|
||||
|
||||
// when
|
||||
subject.addTrackPoint(tp4);
|
||||
subject.addTrackPoint(tp5);
|
||||
|
||||
// then
|
||||
assertEquals(59.18, subject.getTrackStatistics().getTotalDistance().toM(), 0.01);
|
||||
assertEquals(59.18, subject.getSegmentStatisticUpdater().getTotalDistance().toM(), 0.01);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void addTrackPoint_maxSpeed_multiple_segments() {
|
||||
TrackStatisticsUpdater subject = new TrackStatisticsUpdater();
|
||||
assertEquals(Speed.of(0f), subject.getTrackStatistics().getMaxSpeed());
|
||||
assertEquals(Speed.of(0f), subject.getSegmentStatisticUpdater().getMaxSpeed());
|
||||
|
||||
subject.addTrackPoints(List.of(
|
||||
new TrackPoint(TrackPoint.Type.SEGMENT_START_MANUAL, Instant.ofEpochSecond(0)),
|
||||
@@ -194,7 +194,7 @@ public class TrackStatisticsUpdaterTest {
|
||||
.setSpeed(Speed.of(2f)),
|
||||
new TrackPoint(TrackPoint.Type.SEGMENT_END_MANUAL, Instant.ofEpochSecond(4))
|
||||
));
|
||||
assertEquals(Speed.of(2f), subject.getTrackStatistics().getMaxSpeed());
|
||||
assertEquals(Speed.of(2f), subject.getSegmentStatisticUpdater().getMaxSpeed());
|
||||
|
||||
// when
|
||||
subject.addTrackPoints(List.of(
|
||||
@@ -207,7 +207,7 @@ public class TrackStatisticsUpdaterTest {
|
||||
));
|
||||
|
||||
// then
|
||||
assertEquals(Speed.of(2f), subject.getTrackStatistics().getMaxSpeed());
|
||||
assertEquals(Speed.of(2f), subject.getSegmentStatisticUpdater().getMaxSpeed());
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -234,7 +234,7 @@ public class TrackStatisticsUpdaterTest {
|
||||
));
|
||||
|
||||
// then
|
||||
assertEquals(Duration.ofSeconds(35), subject.getTrackStatistics().getMovingTime());
|
||||
assertEquals(Duration.ofSeconds(35), subject.getSegmentStatisticUpdater().getMovingTime());
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -264,8 +264,8 @@ public class TrackStatisticsUpdaterTest {
|
||||
));
|
||||
|
||||
// then
|
||||
assertEquals(Duration.ofSeconds(40), subject.getTrackStatistics().getMovingTime());
|
||||
assertEquals(Distance.of(1040), subject.getTrackStatistics().getTotalDistance());
|
||||
assertEquals(Duration.ofSeconds(40), subject.getSegmentStatisticUpdater().getMovingTime());
|
||||
assertEquals(Distance.of(1040), subject.getSegmentStatisticUpdater().getTotalDistance());
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -280,49 +280,49 @@ public class TrackStatisticsUpdaterTest {
|
||||
|
||||
subject.addTrackPoint(new TrackPoint(TrackPoint.Type.IDLE, Instant.ofEpochSecond(30)));
|
||||
// then
|
||||
assertTrue(subject.getTrackStatistics().isIdle());
|
||||
assertEquals(Duration.ofSeconds(30), subject.getTrackStatistics().getMovingTime());
|
||||
assertEquals(Duration.ofSeconds(30), subject.getTrackStatistics().getTotalTime());
|
||||
assertEquals(Distance.of(10), subject.getTrackStatistics().getTotalDistance());
|
||||
assertTrue(subject.getSegmentStatisticUpdater().isIdle());
|
||||
assertEquals(Duration.ofSeconds(30), subject.getSegmentStatisticUpdater().getMovingTime());
|
||||
assertEquals(Duration.ofSeconds(30), subject.getSegmentStatisticUpdater().getTotalTime());
|
||||
assertEquals(Distance.of(10), subject.getSegmentStatisticUpdater().getTotalDistance());
|
||||
|
||||
// when
|
||||
subject.addTrackPoint(
|
||||
new TrackPoint(TrackPoint.Type.TRACKPOINT, Instant.ofEpochSecond(40))
|
||||
.setSensorDistance(Distance.of(0)));
|
||||
// then
|
||||
assertTrue(subject.getTrackStatistics().isIdle());
|
||||
assertEquals(Duration.ofSeconds(30), subject.getTrackStatistics().getMovingTime());
|
||||
assertEquals(Duration.ofSeconds(40), subject.getTrackStatistics().getTotalTime());
|
||||
assertEquals(Distance.of(10), subject.getTrackStatistics().getTotalDistance());
|
||||
assertTrue(subject.getSegmentStatisticUpdater().isIdle());
|
||||
assertEquals(Duration.ofSeconds(30), subject.getSegmentStatisticUpdater().getMovingTime());
|
||||
assertEquals(Duration.ofSeconds(40), subject.getSegmentStatisticUpdater().getTotalTime());
|
||||
assertEquals(Distance.of(10), subject.getSegmentStatisticUpdater().getTotalDistance());
|
||||
|
||||
// when
|
||||
subject.addTrackPoint(
|
||||
new TrackPoint(TrackPoint.Type.TRACKPOINT, Instant.ofEpochSecond(45))
|
||||
.setSensorDistance(Distance.of(1)));
|
||||
// then
|
||||
assertTrue(subject.getTrackStatistics().isIdle());
|
||||
assertEquals(Duration.ofSeconds(30), subject.getTrackStatistics().getMovingTime());
|
||||
assertEquals(Duration.ofSeconds(45), subject.getTrackStatistics().getTotalTime());
|
||||
assertEquals(Distance.of(11), subject.getTrackStatistics().getTotalDistance());
|
||||
assertTrue(subject.getSegmentStatisticUpdater().isIdle());
|
||||
assertEquals(Duration.ofSeconds(30), subject.getSegmentStatisticUpdater().getMovingTime());
|
||||
assertEquals(Duration.ofSeconds(45), subject.getSegmentStatisticUpdater().getTotalTime());
|
||||
assertEquals(Distance.of(11), subject.getSegmentStatisticUpdater().getTotalDistance());
|
||||
|
||||
// when
|
||||
subject.addTrackPoint(
|
||||
new TrackPoint(TrackPoint.Type.TRACKPOINT, Instant.ofEpochSecond(50))
|
||||
.setSensorDistance(Distance.of(10)));
|
||||
// then
|
||||
assertFalse(subject.getTrackStatistics().isIdle());
|
||||
assertEquals(Duration.ofSeconds(30), subject.getTrackStatistics().getMovingTime());
|
||||
assertEquals(Duration.ofSeconds(50), subject.getTrackStatistics().getTotalTime());
|
||||
assertEquals(Distance.of(21), subject.getTrackStatistics().getTotalDistance());
|
||||
assertFalse(subject.getSegmentStatisticUpdater().isIdle());
|
||||
assertEquals(Duration.ofSeconds(30), subject.getSegmentStatisticUpdater().getMovingTime());
|
||||
assertEquals(Duration.ofSeconds(50), subject.getSegmentStatisticUpdater().getTotalTime());
|
||||
assertEquals(Distance.of(21), subject.getSegmentStatisticUpdater().getTotalDistance());
|
||||
|
||||
// when
|
||||
subject.addTrackPoint(new TrackPoint(TrackPoint.Type.SEGMENT_END_MANUAL, Instant.ofEpochSecond(60)));
|
||||
|
||||
// then
|
||||
assertFalse(subject.getTrackStatistics().isIdle());
|
||||
assertEquals(Duration.ofSeconds(40), subject.getTrackStatistics().getMovingTime());
|
||||
assertEquals(Duration.ofSeconds(60), subject.getTrackStatistics().getTotalTime());
|
||||
assertEquals(Distance.of(21), subject.getTrackStatistics().getTotalDistance());
|
||||
assertFalse(subject.getSegmentStatisticUpdater().isIdle());
|
||||
assertEquals(Duration.ofSeconds(40), subject.getSegmentStatisticUpdater().getMovingTime());
|
||||
assertEquals(Duration.ofSeconds(60), subject.getSegmentStatisticUpdater().getTotalTime());
|
||||
assertEquals(Distance.of(21), subject.getSegmentStatisticUpdater().getTotalDistance());
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -350,8 +350,8 @@ public class TrackStatisticsUpdaterTest {
|
||||
copy.addTrackPoint(tp5);
|
||||
|
||||
// then
|
||||
assertEquals(55.287, subject.getTrackStatistics().getTotalDistance().toM(), 0.01);
|
||||
assertEquals(55.287, copy.getTrackStatistics().getTotalDistance().toM(), 0.01);
|
||||
assertEquals(55.287, subject.getSegmentStatisticUpdater().getTotalDistance().toM(), 0.01);
|
||||
assertEquals(55.287, copy.getSegmentStatisticUpdater().getTotalDistance().toM(), 0.01);
|
||||
}
|
||||
|
||||
public TrackPoint createTrackPoint(double latitude, double longitude, Altitude altitude, Instant time) {
|
||||
|
||||
+25
-23
@@ -17,10 +17,13 @@ import java.util.List;
|
||||
|
||||
import de.dennisguse.opentracks.R;
|
||||
import de.dennisguse.opentracks.data.models.ActivityType;
|
||||
import de.dennisguse.opentracks.data.models.AltitudeExtremities;
|
||||
import de.dennisguse.opentracks.data.models.AltitudeGainLoss;
|
||||
import de.dennisguse.opentracks.data.models.Distance;
|
||||
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.stats.TrackStatistics;
|
||||
import de.dennisguse.opentracks.stats.SegmentStatisticUpdater;
|
||||
|
||||
@RunWith(JUnit4.class)
|
||||
public class AggregatedStatisticsTest {
|
||||
@@ -28,21 +31,20 @@ public class AggregatedStatisticsTest {
|
||||
private final Context context = ApplicationProvider.getApplicationContext();
|
||||
|
||||
private static Track createTrack(Context context, Distance totalDistance, Duration totalTime, String activityTypeLocalized) {
|
||||
TrackStatistics statistics = new TrackStatistics();
|
||||
statistics.setStartTime(Instant.ofEpochMilli(1000L)); // Resulting start time
|
||||
statistics.setStopTime(statistics.getStartTime().plus(totalTime));
|
||||
statistics.setTotalTime(totalTime);
|
||||
statistics.setMovingTime(totalTime);
|
||||
statistics.setTotalDistance(totalDistance);
|
||||
statistics.setTotalAltitudeGain(50.0f);
|
||||
statistics.setMaxSpeed(Speed.of(50.0)); // Resulting max speed
|
||||
statistics.setMaxAltitude(1250.0);
|
||||
statistics.setMinAltitude(1200.0); // Resulting min altitude
|
||||
|
||||
Track track = new Track();
|
||||
Track track = new Track(new Statistics(
|
||||
Instant.ofEpochMilli(1000),
|
||||
Instant.ofEpochMilli(1000).plus(totalTime),
|
||||
totalTime,
|
||||
totalTime,
|
||||
totalDistance,
|
||||
Speed.of(50),
|
||||
new AltitudeExtremities(1250, 1250),
|
||||
new AltitudeGainLoss(50, 50),
|
||||
null,
|
||||
null
|
||||
));
|
||||
track.setActivityType(ActivityType.findByLocalizedString(context, activityTypeLocalized));
|
||||
track.setActivityTypeLocalized(activityTypeLocalized);
|
||||
track.setTrackStatistics(statistics);
|
||||
return track;
|
||||
}
|
||||
|
||||
@@ -63,7 +65,7 @@ public class AggregatedStatisticsTest {
|
||||
assertNotNull(aggregatedStatistics.get(biking));
|
||||
assertEquals(1, aggregatedStatistics.get(biking).getCountTracks());
|
||||
|
||||
TrackStatistics statistics2 = aggregatedStatistics.get(biking).getTrackStatistics();
|
||||
SegmentStatisticUpdater statistics2 = aggregatedStatistics.get(biking).getTrackStatistics();
|
||||
assertEquals(totalDistance, statistics2.getTotalDistance());
|
||||
assertEquals(totalTime, statistics2.getMovingTime());
|
||||
}
|
||||
@@ -117,7 +119,7 @@ public class AggregatedStatisticsTest {
|
||||
assertNotNull(aggregatedStatistics.get(biking));
|
||||
assertEquals(2, aggregatedStatistics.get(biking).getCountTracks());
|
||||
|
||||
TrackStatistics statistics2 = aggregatedStatistics.get(biking).getTrackStatistics();
|
||||
SegmentStatisticUpdater statistics2 = aggregatedStatistics.get(biking).getTrackStatistics();
|
||||
assertEquals(totalDistance.multipliedBy(2), statistics2.getTotalDistance());
|
||||
assertEquals(totalTime.multipliedBy(2), statistics2.getMovingTime());
|
||||
}
|
||||
@@ -151,19 +153,19 @@ public class AggregatedStatisticsTest {
|
||||
assertEquals(1, aggregatedStatistics.get(walking).getCountTracks());
|
||||
|
||||
{
|
||||
TrackStatistics statistics2 = aggregatedStatistics.get(biking).getTrackStatistics();
|
||||
SegmentStatisticUpdater statistics2 = aggregatedStatistics.get(biking).getTrackStatistics();
|
||||
assertEquals(totalDistance, statistics2.getTotalDistance());
|
||||
assertEquals(totalTime, statistics2.getMovingTime());
|
||||
}
|
||||
|
||||
{
|
||||
TrackStatistics statistics2 = aggregatedStatistics.get(running).getTrackStatistics();
|
||||
SegmentStatisticUpdater statistics2 = aggregatedStatistics.get(running).getTrackStatistics();
|
||||
assertEquals(totalDistance, statistics2.getTotalDistance());
|
||||
assertEquals(totalTime, statistics2.getMovingTime());
|
||||
}
|
||||
|
||||
{
|
||||
TrackStatistics statistics2 = aggregatedStatistics.get(walking).getTrackStatistics();
|
||||
SegmentStatisticUpdater statistics2 = aggregatedStatistics.get(walking).getTrackStatistics();
|
||||
assertEquals(totalDistance, statistics2.getTotalDistance());
|
||||
assertEquals(totalTime, statistics2.getMovingTime());
|
||||
}
|
||||
@@ -214,28 +216,28 @@ public class AggregatedStatisticsTest {
|
||||
|
||||
// Biking.
|
||||
{
|
||||
TrackStatistics statistics2 = aggregatedStatistics.get(biking).getTrackStatistics();
|
||||
SegmentStatisticUpdater statistics2 = aggregatedStatistics.get(biking).getTrackStatistics();
|
||||
assertEquals(totalDistance.multipliedBy(5), statistics2.getTotalDistance());
|
||||
assertEquals(totalTime.multipliedBy(5), statistics2.getMovingTime());
|
||||
}
|
||||
|
||||
// Running.
|
||||
{
|
||||
TrackStatistics statistics2 = aggregatedStatistics.get(running).getTrackStatistics();
|
||||
SegmentStatisticUpdater statistics2 = aggregatedStatistics.get(running).getTrackStatistics();
|
||||
assertEquals(totalDistance.multipliedBy(2), statistics2.getTotalDistance());
|
||||
assertEquals(totalTime.multipliedBy(2), statistics2.getMovingTime());
|
||||
}
|
||||
|
||||
// Walking.
|
||||
{
|
||||
TrackStatistics statistics2 = aggregatedStatistics.get(walking).getTrackStatistics();
|
||||
SegmentStatisticUpdater statistics2 = aggregatedStatistics.get(walking).getTrackStatistics();
|
||||
assertEquals(totalDistance.multipliedBy(2), statistics2.getTotalDistance());
|
||||
assertEquals(totalTime.multipliedBy(2), statistics2.getMovingTime());
|
||||
}
|
||||
|
||||
// Driving.
|
||||
{
|
||||
TrackStatistics statistics2 = aggregatedStatistics.get(driving).getTrackStatistics();
|
||||
SegmentStatisticUpdater statistics2 = aggregatedStatistics.get(driving).getTrackStatistics();
|
||||
assertEquals(totalDistance, statistics2.getTotalDistance());
|
||||
assertEquals(totalTime, statistics2.getMovingTime());
|
||||
}
|
||||
|
||||
+49
-68
@@ -3,47 +3,24 @@ package de.dennisguse.opentracks.ui.intervals;
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertTrue;
|
||||
|
||||
import android.content.Context;
|
||||
import android.util.Pair;
|
||||
|
||||
import androidx.test.core.app.ApplicationProvider;
|
||||
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
import org.junit.runners.JUnit4;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import de.dennisguse.opentracks.content.data.TestDataUtil;
|
||||
import de.dennisguse.opentracks.data.ContentProviderUtils;
|
||||
import de.dennisguse.opentracks.data.TrackPointIterator;
|
||||
import de.dennisguse.opentracks.data.models.Distance;
|
||||
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.stats.TrackStatistics;
|
||||
import de.dennisguse.opentracks.stats.TrackStatisticsUpdater;
|
||||
|
||||
@RunWith(JUnit4.class)
|
||||
public class IntervalStatisticsTest {
|
||||
|
||||
private static final String TAG = IntervalStatisticsTest.class.getSimpleName();
|
||||
|
||||
private final Context context = ApplicationProvider.getApplicationContext();
|
||||
private ContentProviderUtils contentProviderUtils;
|
||||
|
||||
@Before
|
||||
public void setUp() {
|
||||
contentProviderUtils = new ContentProviderUtils(context);
|
||||
}
|
||||
|
||||
private TrackStatistics buildTrackStatistics(List<TrackPoint> trackPoints) {
|
||||
TrackStatisticsUpdater trackStatisticsUpdater = new TrackStatisticsUpdater();
|
||||
for (TrackPoint tp : trackPoints) {
|
||||
trackStatisticsUpdater.addTrackPoint(tp);
|
||||
}
|
||||
return trackStatisticsUpdater.getTrackStatistics();
|
||||
}
|
||||
public class IntervalStatisticsUpdaterTest {
|
||||
|
||||
/**
|
||||
* Tests that build method compute the distance correctly comparing the result with TrackStatisticsUpdater result.
|
||||
@@ -125,77 +102,81 @@ public class IntervalStatisticsTest {
|
||||
Track dummyTrack = new Track();
|
||||
dummyTrack.setId(new Track.Id(System.currentTimeMillis()));
|
||||
dummyTrack.setName("Dummy Track Without Elevation Loss");
|
||||
contentProviderUtils.insertTrack(dummyTrack);
|
||||
TrackStatisticsUpdater trackStatisticsUpdater = new TrackStatisticsUpdater();
|
||||
|
||||
ArrayList<TrackPoint> trackPoints = new ArrayList<>();
|
||||
for (int i = 0; i < numberOfPoints; i++) {
|
||||
TrackPoint tp = TestDataUtil.createTrackPoint(i);
|
||||
tp.setAltitudeLoss(null);
|
||||
contentProviderUtils.insertTrackPoint(tp, dummyTrack.getId());
|
||||
trackStatisticsUpdater.addTrackPoint(tp);
|
||||
trackPoints.add(TestDataUtil.createTrackPoint(i).setAltitudeGainLoss(null));
|
||||
}
|
||||
dummyTrack.setTrackStatistics(trackStatisticsUpdater.getTrackStatistics());
|
||||
contentProviderUtils.updateTrack(dummyTrack);
|
||||
Pair<Track.Id, TrackStatistics> trackWithStats = new Pair<>(dummyTrack.getId(), trackStatisticsUpdater.getTrackStatistics());
|
||||
trackStatisticsUpdater.addTrackPoints(trackPoints);
|
||||
|
||||
dummyTrack.setStatistics(trackStatisticsUpdater.getTrackStatistics());
|
||||
Pair<Track, List<TrackPoint>> trackWithStats = new Pair<>(dummyTrack, trackPoints);
|
||||
|
||||
// when and then
|
||||
whenAndThen(trackWithStats, numberOfPoints, distanceInterval);
|
||||
whenAndThen(trackWithStats.first, trackWithStats.second, numberOfPoints, distanceInterval);
|
||||
}
|
||||
|
||||
private void whenAndThen(int numberOfPoints, float distanceInterval) {
|
||||
Pair<Track.Id, TrackStatistics> trackWithStats = TestDataUtil.buildTrackWithTrackPoints(contentProviderUtils, numberOfPoints);
|
||||
whenAndThen(trackWithStats, numberOfPoints, distanceInterval);
|
||||
|
||||
Pair<Track, List<TrackPoint>> trackWithStats = buildTrackWithTrackPoints(numberOfPoints);
|
||||
whenAndThen(trackWithStats.first, trackWithStats.second, numberOfPoints, distanceInterval);
|
||||
}
|
||||
|
||||
private void whenAndThen(Pair<Track.Id, TrackStatistics> trackWithStats, int numberOfPoints, float distanceInterval) {
|
||||
IntervalStatistics intervalStatistics = new IntervalStatistics(Distance.of(distanceInterval));
|
||||
Track.Id trackId = trackWithStats.first;
|
||||
TrackStatistics trackStatistics = trackWithStats.second;
|
||||
try (TrackPointIterator trackPointIterator = contentProviderUtils.getTrackPointLocationIterator(trackId, null)) {
|
||||
assertEquals(trackPointIterator.getCount(), numberOfPoints);
|
||||
intervalStatistics.addTrackPoints(trackPointIterator);
|
||||
}
|
||||
List<IntervalStatistics.Interval> intervalList = intervalStatistics.getIntervalList();
|
||||
private void whenAndThen(Track track, List<TrackPoint> trackPoints, int numberOfPoints, float distanceInterval) {
|
||||
IntervalStatisticsUpdater intervalStatistics = new IntervalStatisticsUpdater(Distance.of(distanceInterval));
|
||||
|
||||
intervalStatistics.addTrackPoints(trackPoints.iterator());
|
||||
|
||||
List<Statistics> intervalList = intervalStatistics.getIntervalList();
|
||||
Distance totalDistance = Distance.of(0);
|
||||
float totalTime = 0L;
|
||||
Float totalGain = null;
|
||||
Float totalLoss = null;
|
||||
for (IntervalStatistics.Interval i : intervalList) {
|
||||
totalDistance = totalDistance.plus(i.getDistance());
|
||||
totalTime += i.getDistance().toM() / i.getSpeed().toMPS();
|
||||
for (Statistics i : intervalList) {
|
||||
totalDistance = totalDistance.plus(i.totalDistance());
|
||||
totalTime += i.totalDistance().toM() / i.getAverageSpeed().toMPS();
|
||||
|
||||
if (totalGain == null) {
|
||||
totalGain = i.getGain_m();
|
||||
} else if (i.getGain_m() != null) {
|
||||
totalGain += i.getGain_m();
|
||||
totalGain = i.altitudeGainLoss() != null ? i.altitudeGainLoss().gain_m() : null;
|
||||
} else if (i.altitudeGainLoss() != null) {
|
||||
totalGain += i.altitudeGainLoss().gain_m();
|
||||
}
|
||||
|
||||
if (totalLoss == null) {
|
||||
totalLoss = i.getLoss_m();
|
||||
} else if (i.getLoss_m() != null) {
|
||||
totalLoss += i.getLoss_m();
|
||||
totalLoss = i.altitudeGainLoss() != null ? i.altitudeGainLoss().loss_m() : null;
|
||||
} else if (i.altitudeGainLoss() != null) {
|
||||
totalLoss += i.altitudeGainLoss().loss_m();
|
||||
}
|
||||
}
|
||||
|
||||
// then
|
||||
assertEquals(trackStatistics.getTotalDistance().toM(), totalDistance.toM(), 0.01);
|
||||
assertEquals(trackStatistics.getTotalTime().toSeconds(), totalTime, 0.01);
|
||||
assertEquals(intervalList.size(), (int) Math.ceil(trackStatistics.getTotalDistance().toM() / distanceInterval));
|
||||
if (totalGain != null) {
|
||||
assertEquals(track.getStatistics().totalTime().toSeconds(), totalTime, 0.01);
|
||||
assertEquals(track.getStatistics().totalDistance().toM(), totalDistance.toM(), 0.01);
|
||||
assertEquals(intervalList.size(), (int) Math.ceil(track.getStatistics().totalDistance().toM() / distanceInterval));
|
||||
if (totalGain != null && totalLoss != null) {
|
||||
assertEquals(totalGain, numberOfPoints * TestDataUtil.ALTITUDE_GAIN, 0.1);
|
||||
} else {
|
||||
assertTrue(intervalStatistics.getIntervalList().stream().noneMatch(IntervalStatistics.Interval::hasGain));
|
||||
}
|
||||
if (totalLoss != null) {
|
||||
assertEquals(totalLoss, numberOfPoints * TestDataUtil.ALTITUDE_LOSS, 0.1);
|
||||
|
||||
} else {
|
||||
assertTrue(intervalStatistics.getIntervalList().stream().noneMatch(IntervalStatistics.Interval::hasLoss));
|
||||
assertTrue(intervalStatistics.getIntervalList().stream().noneMatch(i -> i.altitudeGainLoss() != null));
|
||||
}
|
||||
|
||||
for (int i = 0; i < intervalList.size() - 1; i++) {
|
||||
assertEquals(intervalList.get(i).getDistance().toM(), distanceInterval, 0.001);
|
||||
totalDistance = totalDistance.minus(intervalList.get(i).getDistance());
|
||||
assertEquals(intervalList.get(i).totalDistance().toM(), distanceInterval, 0.001);
|
||||
totalDistance = totalDistance.minus(intervalList.get(i).totalDistance());
|
||||
}
|
||||
assertEquals(intervalList.get(intervalList.size() - 1).getDistance().toM(), totalDistance.toM(), 0.01);
|
||||
assertEquals(intervalList.get(intervalList.size() - 1).totalDistance().toM(), totalDistance.toM(), 0.01);
|
||||
}
|
||||
|
||||
private static Pair<Track, List<TrackPoint>> buildTrackWithTrackPoints(int numberOfPoints) {
|
||||
TrackStatisticsUpdater trackStatisticsUpdater = new TrackStatisticsUpdater();
|
||||
|
||||
ArrayList<TrackPoint> trackPoints = new ArrayList<>();
|
||||
for (int i = 0; i < numberOfPoints; i++) {
|
||||
trackPoints.add(TestDataUtil.createTrackPoint(i));
|
||||
}
|
||||
trackStatisticsUpdater.addTrackPoints(trackPoints);
|
||||
|
||||
return new Pair<>(new Track(trackStatisticsUpdater.getTrackStatistics()), trackPoints);
|
||||
}
|
||||
}
|
||||
@@ -74,7 +74,6 @@ Average moving pace: 0:00 min/km (0:00 min/mi)
|
||||
Fastest pace: 0:00 min/km (0:00 min/mi)
|
||||
Max elevation: -9223372036854775808 m (-9223372036854775808 ft)
|
||||
Min elevation: 9223372036854775807 m (9223372036854775807 ft)
|
||||
Elevation gain: 0 m (0 ft)
|
||||
Max grade: 0 %
|
||||
Min grade: 0 %
|
||||
Recorded: 6/23 11:29]]></description>
|
||||
@@ -126,11 +125,6 @@ Recorded: 6/23 11:29]]></description>
|
||||
<gx:value>0.15777911</gx:value>
|
||||
<gx:value>0.0</gx:value>
|
||||
</gx:SimpleArrayData>
|
||||
<gx:SimpleArrayData name="elevation_gain">
|
||||
<gx:value>0.0</gx:value>
|
||||
<gx:value>0.0</gx:value>
|
||||
<gx:value>0.0</gx:value>
|
||||
</gx:SimpleArrayData>
|
||||
</SchemaData>
|
||||
</ExtendedData>
|
||||
</gx:Track>
|
||||
@@ -148,14 +142,6 @@ Recorded: 6/23 11:29]]></description>
|
||||
<gx:value>0.0</gx:value>
|
||||
<gx:value>0.0</gx:value>
|
||||
</gx:SimpleArrayData>
|
||||
<gx:SimpleArrayData name="elevation_gain">
|
||||
<gx:value>0.0</gx:value>
|
||||
<gx:value>0.0</gx:value>
|
||||
<gx:value>0.0</gx:value>
|
||||
<gx:value>0.0</gx:value>
|
||||
<gx:value>0.0</gx:value>
|
||||
<gx:value>0.0</gx:value>
|
||||
</gx:SimpleArrayData>
|
||||
</SchemaData>
|
||||
</ExtendedData>
|
||||
</gx:Track>
|
||||
@@ -179,7 +165,6 @@ Average moving pace: 0:00 min/km (0:00 min/mi)
|
||||
Fastest pace: 0:00 min/km (0:00 min/mi)
|
||||
Max elevation: 469 m (1540 ft)
|
||||
Min elevation: 419 m (1376 ft)
|
||||
Elevation gain: 0 m (0 ft)
|
||||
Recorded: 11/28 18:06]]></description>
|
||||
<TimeStamp>
|
||||
<when>2020-11-28T17:07:20.870Z</when>
|
||||
|
||||
@@ -25,7 +25,6 @@ import android.view.View;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.annotation.Nullable;
|
||||
import androidx.core.view.ViewCompat;
|
||||
import androidx.fragment.app.Fragment;
|
||||
import androidx.fragment.app.FragmentActivity;
|
||||
import androidx.viewpager2.adapter.FragmentStateAdapter;
|
||||
@@ -60,8 +59,6 @@ public class TrackRecordedActivity extends AbstractTrackDeleteActivity implement
|
||||
|
||||
private static final String TAG = TrackRecordedActivity.class.getSimpleName();
|
||||
|
||||
public static final String VIEW_TRACK_ICON = "track_icon";
|
||||
|
||||
public static final String EXTRA_TRACK_ID = "track_id";
|
||||
|
||||
private static final String CURRENT_TAB_TAG_KEY = "current_tab_tag_key";
|
||||
@@ -98,8 +95,6 @@ public class TrackRecordedActivity extends AbstractTrackDeleteActivity implement
|
||||
|
||||
viewBinding.bottomAppBarLayout.bottomAppBar.replaceMenu(R.menu.track_detail);
|
||||
setSupportActionBar(viewBinding.bottomAppBarLayout.bottomAppBar);
|
||||
|
||||
postponeEnterTransition();
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -284,11 +279,6 @@ public class TrackRecordedActivity extends AbstractTrackDeleteActivity implement
|
||||
}
|
||||
}
|
||||
|
||||
public void startPostponedEnterTransitionWith(View viewIcon) {
|
||||
ViewCompat.setTransitionName(viewIcon, TrackRecordedActivity.VIEW_TRACK_ICON);
|
||||
startPostponedEnterTransition();
|
||||
}
|
||||
|
||||
private void onRecordingStatusChanged(RecordingStatus status) {
|
||||
recordingStatus = status;
|
||||
}
|
||||
|
||||
@@ -68,14 +68,14 @@ public class TrackStoppedActivity extends AbstractTrackDeleteActivity implements
|
||||
|
||||
viewBinding.trackEditDescription.setText(track.getDescription());
|
||||
|
||||
viewBinding.time.setText(StringUtils.formatElapsedTime(track.getTrackStatistics().getMovingTime()));
|
||||
viewBinding.time.setText(StringUtils.formatElapsedTime(track.getStatistics().movingTime()));
|
||||
|
||||
{
|
||||
Pair<String, String> parts = SpeedFormatter.Builder()
|
||||
.setUnit(PreferencesUtils.getUnitSystem())
|
||||
.setReportSpeedOrPace(PreferencesUtils.isReportSpeed(track))
|
||||
.build(this)
|
||||
.getSpeedParts(track.getTrackStatistics().getAverageMovingSpeed());
|
||||
.getSpeedParts(track.getStatistics().getAverageMovingSpeed());
|
||||
viewBinding.speed.setText(parts.first);
|
||||
viewBinding.speedUnit.setText(parts.second);
|
||||
}
|
||||
@@ -84,7 +84,7 @@ public class TrackStoppedActivity extends AbstractTrackDeleteActivity implements
|
||||
Pair<String, String> parts = DistanceFormatter.Builder()
|
||||
.setUnit(PreferencesUtils.getUnitSystem())
|
||||
.build(this)
|
||||
.getDistanceParts(track.getTrackStatistics().getTotalDistance());
|
||||
.getDistanceParts(track.getStatistics().totalDistance());
|
||||
viewBinding.distance.setText(parts.first);
|
||||
viewBinding.distanceUnit.setText(parts.second);
|
||||
}
|
||||
|
||||
@@ -33,17 +33,18 @@ import java.util.List;
|
||||
import de.dennisguse.opentracks.R;
|
||||
import de.dennisguse.opentracks.data.TrackDataHub;
|
||||
import de.dennisguse.opentracks.data.models.Marker;
|
||||
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.databinding.ChartBinding;
|
||||
import de.dennisguse.opentracks.settings.PreferencesUtils;
|
||||
import de.dennisguse.opentracks.settings.UnitSystem;
|
||||
import de.dennisguse.opentracks.stats.TrackStatistics;
|
||||
import de.dennisguse.opentracks.stats.SegmentStatisticUpdater;
|
||||
import de.dennisguse.opentracks.stats.TrackStatisticsUpdater;
|
||||
|
||||
/**
|
||||
* A fragment to display track chart to the user.
|
||||
* ChartFragment uses a {@link TrackStatisticsUpdater} internally and recomputes the {@link TrackStatistics} from the beginning.
|
||||
* ChartFragment uses a {@link TrackStatisticsUpdater} internally and recomputes the {@link SegmentStatisticUpdater} from the beginning.
|
||||
*
|
||||
* @author Sandor Dornbush
|
||||
* @author Rodrigo Damazio
|
||||
@@ -202,7 +203,7 @@ public class ChartFragment extends Fragment implements TrackDataHub.Listener {
|
||||
}
|
||||
}
|
||||
|
||||
public void onSampledInTrackPoint(@NonNull TrackPoint trackPoint, @NonNull TrackStatistics trackStatistics) {
|
||||
public void onSampledInTrackPoint(@NonNull TrackPoint trackPoint, @NonNull Statistics trackStatistics) {
|
||||
if (isResumed()) {
|
||||
ChartPoint point = ChartPoint.create(trackStatistics, trackPoint, trackPoint.getSpeed(), chartByDistance, viewBinding.chartView.getUnitSystem());
|
||||
pendingPoints.add(point);
|
||||
|
||||
@@ -4,9 +4,9 @@ import androidx.annotation.NonNull;
|
||||
|
||||
import de.dennisguse.opentracks.data.models.Distance;
|
||||
import de.dennisguse.opentracks.data.models.Speed;
|
||||
import de.dennisguse.opentracks.data.models.Statistics;
|
||||
import de.dennisguse.opentracks.data.models.TrackPoint;
|
||||
import de.dennisguse.opentracks.settings.UnitSystem;
|
||||
import de.dennisguse.opentracks.stats.TrackStatistics;
|
||||
|
||||
public record ChartPoint(
|
||||
//X-axis
|
||||
@@ -22,11 +22,11 @@ public record ChartPoint(
|
||||
) {
|
||||
|
||||
|
||||
public static ChartPoint create(@NonNull TrackStatistics trackStatistics, @NonNull TrackPoint trackPoint, Speed smoothedSpeed, boolean chartByDistance, UnitSystem unitSystem) {
|
||||
public static ChartPoint create(@NonNull Statistics trackStatistics, @NonNull TrackPoint trackPoint, Speed smoothedSpeed, boolean chartByDistance, UnitSystem unitSystem) {
|
||||
return new ChartPoint(
|
||||
chartByDistance
|
||||
? trackStatistics.getTotalDistance().toKM_Miles(unitSystem)
|
||||
: trackStatistics.getTotalTime().toMillis(),
|
||||
? trackStatistics.totalDistance().toKM_Miles(unitSystem)
|
||||
: trackStatistics.totalTime().toMillis(),
|
||||
trackPoint.hasAltitude()
|
||||
? Distance.of(trackPoint.getAltitude().toM()).toM_FT(unitSystem)
|
||||
: null,
|
||||
|
||||
@@ -41,6 +41,8 @@ import java.util.UUID;
|
||||
import de.dennisguse.opentracks.BuildConfig;
|
||||
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;
|
||||
@@ -48,14 +50,15 @@ 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;
|
||||
import de.dennisguse.opentracks.data.models.TrackPoint;
|
||||
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.stats.SensorStatistics;
|
||||
import de.dennisguse.opentracks.stats.TrackStatistics;
|
||||
import de.dennisguse.opentracks.ui.markers.MarkerUtils;
|
||||
import de.dennisguse.opentracks.util.AutoCloseableIterator;
|
||||
import de.dennisguse.opentracks.util.FileUtils;
|
||||
|
||||
/**
|
||||
@@ -116,7 +119,6 @@ public class ContentProviderUtils {
|
||||
int altitudeLossIndex = cursor.getColumnIndexOrThrow(TracksColumns.ALTITUDE_LOSS);
|
||||
|
||||
Track track = new Track(ZoneOffset.ofTotalSeconds(cursor.getInt(startTimeOffsetIndex)));
|
||||
TrackStatistics trackStatistics = track.getTrackStatistics();
|
||||
if (!cursor.isNull(idIndex)) {
|
||||
track.setId(new Track.Id(cursor.getLong(idIndex)));
|
||||
}
|
||||
@@ -136,37 +138,23 @@ public class ContentProviderUtils {
|
||||
track.setActivityTypeLocalized(cursor.getString(activityTypeLocalizedIndex));
|
||||
}
|
||||
|
||||
if (!cursor.isNull(startTimeIndex)) {
|
||||
trackStatistics.setStartTime(Instant.ofEpochMilli(cursor.getLong(startTimeIndex)));
|
||||
}
|
||||
if (!cursor.isNull(stopTimeIndex)) {
|
||||
trackStatistics.setStopTime(Instant.ofEpochMilli(cursor.getLong(stopTimeIndex)));
|
||||
}
|
||||
if (!cursor.isNull(totalDistanceIndex)) {
|
||||
trackStatistics.setTotalDistance(Distance.of(cursor.getFloat(totalDistanceIndex)));
|
||||
}
|
||||
if (!cursor.isNull(totalTimeIndex)) {
|
||||
trackStatistics.setTotalTime(Duration.ofMillis(cursor.getLong(totalTimeIndex)));
|
||||
}
|
||||
if (!cursor.isNull(movingTimeIndex)) {
|
||||
trackStatistics.setMovingTime(Duration.ofMillis(cursor.getLong(movingTimeIndex)));
|
||||
}
|
||||
if (!cursor.isNull(maxSpeedIndex)) {
|
||||
trackStatistics.setMaxSpeed(Speed.of(cursor.getFloat(maxSpeedIndex)));
|
||||
}
|
||||
if (!cursor.isNull(minAltitudeIndex)) {
|
||||
trackStatistics.setMinAltitude(cursor.getFloat(minAltitudeIndex));
|
||||
}
|
||||
if (!cursor.isNull(maxAltitudeIndex)) {
|
||||
trackStatistics.setMaxAltitude(cursor.getFloat(maxAltitudeIndex));
|
||||
}
|
||||
if (!cursor.isNull(altitudeGainIndex)) {
|
||||
trackStatistics.setTotalAltitudeGain(cursor.getFloat(altitudeGainIndex));
|
||||
}
|
||||
if (!cursor.isNull(altitudeLossIndex)) {
|
||||
trackStatistics.setTotalAltitudeLoss(cursor.getFloat(altitudeLossIndex));
|
||||
}
|
||||
Statistics trackStatistics = new Statistics(
|
||||
cursor.isNull(startTimeIndex) ? null : Instant.ofEpochMilli(cursor.getLong(startTimeIndex)),
|
||||
cursor.isNull(stopTimeIndex) ? null : Instant.ofEpochMilli(cursor.getLong(stopTimeIndex)),
|
||||
cursor.isNull(totalTimeIndex) ? null : Duration.ofMillis(cursor.getLong(totalTimeIndex)),
|
||||
cursor.isNull(movingTimeIndex) ? null : Duration.ofMillis(cursor.getLong(movingTimeIndex)),
|
||||
|
||||
cursor.isNull(totalDistanceIndex) ? null : Distance.of(cursor.getFloat(totalDistanceIndex)),
|
||||
|
||||
cursor.isNull(maxSpeedIndex) ? null : Speed.of(cursor.getFloat(maxSpeedIndex)),
|
||||
cursor.isNull(minAltitudeIndex) || cursor.isNull(maxAltitudeIndex) ? null : new AltitudeExtremities(cursor.getFloat(minAltitudeIndex), cursor.getFloat(maxAltitudeIndex)),
|
||||
cursor.isNull(altitudeGainIndex) || cursor.isNull(altitudeLossIndex) ? null : new AltitudeGainLoss(cursor.getFloat(altitudeGainIndex), cursor.getFloat(altitudeLossIndex)),
|
||||
|
||||
null,
|
||||
null
|
||||
);
|
||||
|
||||
track.setStatistics(trackStatistics);
|
||||
return track;
|
||||
}
|
||||
|
||||
@@ -314,7 +302,7 @@ public class ContentProviderUtils {
|
||||
|
||||
private ContentValues createContentValues(Track track) {
|
||||
ContentValues values = new ContentValues();
|
||||
TrackStatistics trackStatistics = track.getTrackStatistics();
|
||||
Statistics trackStatistics = track.getStatistics();
|
||||
|
||||
if (track.getId() != null) {
|
||||
values.put(TracksColumns._ID, track.getId().id());
|
||||
@@ -325,48 +313,56 @@ public class ContentProviderUtils {
|
||||
values.put(TracksColumns.ACTIVITY_TYPE, track.getActivityType() != null ? track.getActivityType().getId() : null);
|
||||
values.put(TracksColumns.ACTIVITY_TYPE_LOCALIZED, track.getActivityTypeLocalized());
|
||||
values.put(TracksColumns.STARTTIME_OFFSET, track.getZoneOffset().getTotalSeconds());
|
||||
if (trackStatistics.getStartTime() != null) {
|
||||
values.put(TracksColumns.STARTTIME, trackStatistics.getStartTime().toEpochMilli());
|
||||
if (trackStatistics.startTime() != null) {
|
||||
values.put(TracksColumns.STARTTIME, trackStatistics.startTime().toEpochMilli());
|
||||
}
|
||||
if (trackStatistics.getStopTime() != null) {
|
||||
values.put(TracksColumns.STOPTIME, trackStatistics.getStopTime().toEpochMilli());
|
||||
if (trackStatistics.stopTime() != null) {
|
||||
values.put(TracksColumns.STOPTIME, trackStatistics.stopTime().toEpochMilli());
|
||||
}
|
||||
values.put(TracksColumns.TOTALDISTANCE, trackStatistics.getTotalDistance().toM());
|
||||
values.put(TracksColumns.TOTALTIME, trackStatistics.getTotalTime().toMillis());
|
||||
values.put(TracksColumns.MOVINGTIME, trackStatistics.getMovingTime().toMillis());
|
||||
values.put(TracksColumns.TOTALDISTANCE, trackStatistics.totalDistance().toM());
|
||||
values.put(TracksColumns.TOTALTIME, trackStatistics.totalTime().toMillis());
|
||||
values.put(TracksColumns.MOVINGTIME, trackStatistics.movingTime().toMillis());
|
||||
values.put(TracksColumns.AVGSPEED, trackStatistics.getAverageSpeed().toMPS());
|
||||
values.put(TracksColumns.AVGMOVINGSPEED, trackStatistics.getAverageMovingSpeed().toMPS());
|
||||
values.put(TracksColumns.MAXSPEED, trackStatistics.getMaxSpeed().toMPS());
|
||||
values.put(TracksColumns.MIN_ALTITUDE, trackStatistics.getMinAltitude());
|
||||
values.put(TracksColumns.MAX_ALTITUDE, trackStatistics.getMaxAltitude());
|
||||
values.put(TracksColumns.ALTITUDE_GAIN, trackStatistics.getTotalAltitudeGain());
|
||||
values.put(TracksColumns.ALTITUDE_LOSS, trackStatistics.getTotalAltitudeLoss());
|
||||
values.put(TracksColumns.MAXSPEED, trackStatistics.maxSpeed().toMPS());
|
||||
|
||||
if (trackStatistics.altitudeExtremities() != null) {
|
||||
values.put(TracksColumns.MIN_ALTITUDE, trackStatistics.getMinAltitude());
|
||||
values.put(TracksColumns.MAX_ALTITUDE, trackStatistics.getMaxAltitude());
|
||||
}
|
||||
|
||||
if (trackStatistics.altitudeGainLoss() != null) {
|
||||
values.put(TracksColumns.ALTITUDE_GAIN, trackStatistics.altitudeGainLoss().gain_m());
|
||||
values.put(TracksColumns.ALTITUDE_LOSS, trackStatistics.altitudeGainLoss().loss_m());
|
||||
}
|
||||
|
||||
return values;
|
||||
}
|
||||
|
||||
public void updateTrackStatistics(@NonNull Track.Id trackId, @NonNull TrackStatistics trackStatistics) {
|
||||
public void updateTrackStatistics(@NonNull Track.Id trackId, @NonNull Statistics trackStatistics) {
|
||||
contentResolver.update(TracksColumns.CONTENT_URI, createContentValues(trackStatistics), TracksColumns._ID + "=?", new String[]{Long.toString(trackId.id())});
|
||||
}
|
||||
|
||||
private ContentValues createContentValues(TrackStatistics trackStatistics) {
|
||||
private ContentValues createContentValues(Statistics trackStatistics) {
|
||||
ContentValues values = new ContentValues();
|
||||
if (trackStatistics.getStartTime() != null) {
|
||||
values.put(TracksColumns.STARTTIME, trackStatistics.getStartTime().toEpochMilli());
|
||||
if (trackStatistics.startTime() != null) {
|
||||
values.put(TracksColumns.STARTTIME, trackStatistics.startTime().toEpochMilli());
|
||||
}
|
||||
if (trackStatistics.getStopTime() != null) {
|
||||
values.put(TracksColumns.STOPTIME, trackStatistics.getStopTime().toEpochMilli());
|
||||
if (trackStatistics.stopTime() != null) {
|
||||
values.put(TracksColumns.STOPTIME, trackStatistics.stopTime().toEpochMilli());
|
||||
}
|
||||
values.put(TracksColumns.TOTALDISTANCE, trackStatistics.getTotalDistance().toM());
|
||||
values.put(TracksColumns.TOTALTIME, trackStatistics.getTotalTime().toMillis());
|
||||
values.put(TracksColumns.MOVINGTIME, trackStatistics.getMovingTime().toMillis());
|
||||
values.put(TracksColumns.TOTALDISTANCE, trackStatistics.totalDistance().toM());
|
||||
values.put(TracksColumns.TOTALTIME, trackStatistics.totalTime().toMillis());
|
||||
values.put(TracksColumns.MOVINGTIME, trackStatistics.movingTime().toMillis());
|
||||
values.put(TracksColumns.AVGSPEED, trackStatistics.getAverageSpeed().toMPS());
|
||||
values.put(TracksColumns.AVGMOVINGSPEED, trackStatistics.getAverageMovingSpeed().toMPS());
|
||||
values.put(TracksColumns.MAXSPEED, trackStatistics.getMaxSpeed().toMPS());
|
||||
values.put(TracksColumns.MAXSPEED, trackStatistics.maxSpeed().toMPS());
|
||||
values.put(TracksColumns.MIN_ALTITUDE, trackStatistics.getMinAltitude());
|
||||
values.put(TracksColumns.MAX_ALTITUDE, trackStatistics.getMaxAltitude());
|
||||
values.put(TracksColumns.ALTITUDE_GAIN, trackStatistics.getTotalAltitudeGain());
|
||||
values.put(TracksColumns.ALTITUDE_LOSS, trackStatistics.getTotalAltitudeLoss());
|
||||
if (trackStatistics.altitudeGainLoss() != null) {
|
||||
values.put(TracksColumns.ALTITUDE_GAIN, trackStatistics.altitudeGainLoss().gain_m());
|
||||
values.put(TracksColumns.ALTITUDE_LOSS, trackStatistics.altitudeGainLoss().loss_m());
|
||||
}
|
||||
return values;
|
||||
}
|
||||
|
||||
@@ -656,11 +652,8 @@ public class ContentProviderUtils {
|
||||
trackPoint.setPower(cursor.getFloat(indexes.sensorPowerIndex));
|
||||
}
|
||||
|
||||
if (!cursor.isNull(indexes.altitudeGainIndex)) {
|
||||
trackPoint.setAltitudeGain(cursor.getFloat(indexes.altitudeGainIndex));
|
||||
}
|
||||
if (!cursor.isNull(indexes.altitudeLossIndex)) {
|
||||
trackPoint.setAltitudeLoss(cursor.getFloat(indexes.altitudeLossIndex));
|
||||
if (!cursor.isNull(indexes.altitudeGainIndex) && !cursor.isNull(indexes.altitudeLossIndex)) {
|
||||
trackPoint.setAltitudeGainLoss(cursor.getFloat(indexes.altitudeGainIndex), cursor.getFloat(indexes.altitudeLossIndex));
|
||||
}
|
||||
|
||||
return trackPoint;
|
||||
@@ -814,13 +807,10 @@ public class ContentProviderUtils {
|
||||
values.put(TrackPointsColumns.SENSOR_POWER, trackPoint.getPower().getW());
|
||||
}
|
||||
|
||||
if (trackPoint.hasAltitudeGain()) {
|
||||
values.put(TrackPointsColumns.ALTITUDE_GAIN, trackPoint.getAltitudeGain());
|
||||
if (trackPoint.hasAltitudeGainLoss()) {
|
||||
values.put(TrackPointsColumns.ALTITUDE_GAIN, trackPoint.getAltitudeGainLoss().gain_m());
|
||||
values.put(TrackPointsColumns.ALTITUDE_LOSS, trackPoint.getAltitudeGainLoss().loss_m());
|
||||
}
|
||||
if (trackPoint.hasAltitudeLoss()) {
|
||||
values.put(TrackPointsColumns.ALTITUDE_LOSS, trackPoint.getAltitudeLoss());
|
||||
}
|
||||
|
||||
return values;
|
||||
}
|
||||
|
||||
@@ -834,7 +824,7 @@ public class ContentProviderUtils {
|
||||
* @param trackId the track id
|
||||
* @param startTrackPointId the starting trackPoint id. `null` to ignore
|
||||
*/
|
||||
public TrackPointIterator getTrackPointLocationIterator(final Track.Id trackId, final TrackPoint.Id startTrackPointId) {
|
||||
public AutoCloseableIterator<TrackPoint> getTrackPointLocationIterator(final Track.Id trackId, final TrackPoint.Id startTrackPointId) {
|
||||
return new TrackPointIterator(this, trackId, startTrackPointId);
|
||||
}
|
||||
|
||||
|
||||
@@ -32,6 +32,7 @@ import java.util.HashSet;
|
||||
import java.util.Set;
|
||||
|
||||
import de.dennisguse.opentracks.data.models.Marker;
|
||||
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.tables.MarkerColumns;
|
||||
@@ -40,8 +41,8 @@ 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.stats.TrackStatistics;
|
||||
import de.dennisguse.opentracks.stats.TrackStatisticsUpdater;
|
||||
import de.dennisguse.opentracks.util.AutoCloseableIterator;
|
||||
|
||||
/**
|
||||
* Track data hub.
|
||||
@@ -330,7 +331,7 @@ public class TrackDataHub {
|
||||
}
|
||||
|
||||
TrackPoint trackPoint = null;
|
||||
try (TrackPointIterator trackPointIterator = contentProviderUtils.getTrackPointLocationIterator(selectedTrackId, next)) {
|
||||
try (AutoCloseableIterator<TrackPoint> trackPointIterator = contentProviderUtils.getTrackPointLocationIterator(selectedTrackId, next)) {
|
||||
|
||||
while (trackPointIterator.hasNext()) {
|
||||
//Prevents a NPE if stop() is happening while notifyTrackPointsTableUpdate()
|
||||
@@ -428,7 +429,7 @@ public class TrackDataHub {
|
||||
*
|
||||
* @param trackPoint the trackPoint
|
||||
*/
|
||||
default void onSampledInTrackPoint(@NonNull TrackPoint trackPoint, @NonNull TrackStatistics trackStatistics) {
|
||||
default void onSampledInTrackPoint(@NonNull TrackPoint trackPoint, @NonNull Statistics trackStatistics) {
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -436,7 +437,7 @@ public class TrackDataHub {
|
||||
*
|
||||
* @param trackPoint the trackPoint
|
||||
*/
|
||||
default void onSampledOutTrackPoint(@NonNull TrackPoint trackPoint, @NonNull TrackStatistics trackStatistics) {
|
||||
default void onSampledOutTrackPoint(@NonNull TrackPoint trackPoint, @NonNull Statistics trackStatistics) {
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -5,16 +5,16 @@ import android.database.Cursor;
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.annotation.VisibleForTesting;
|
||||
|
||||
import java.util.Iterator;
|
||||
import java.util.NoSuchElementException;
|
||||
|
||||
import de.dennisguse.opentracks.data.models.Track;
|
||||
import de.dennisguse.opentracks.data.models.TrackPoint;
|
||||
import de.dennisguse.opentracks.util.AutoCloseableIterator;
|
||||
|
||||
/**
|
||||
* A lightweight wrapper around the original {@link Cursor}.
|
||||
*/
|
||||
public class TrackPointIterator implements Iterator<TrackPoint>, AutoCloseable {
|
||||
class TrackPointIterator extends AutoCloseableIterator<TrackPoint> {
|
||||
|
||||
private static final String TAG = TrackPointIterator.class.getSimpleName();
|
||||
|
||||
@@ -23,7 +23,7 @@ public class TrackPointIterator implements Iterator<TrackPoint>, AutoCloseable {
|
||||
private final CachedTrackPointsIndexes indexes;
|
||||
private Cursor cursor;
|
||||
|
||||
public TrackPointIterator(ContentProviderUtils contentProviderUtils, Track.Id trackId, TrackPoint.Id startTrackPointId) {
|
||||
TrackPointIterator(ContentProviderUtils contentProviderUtils, Track.Id trackId, TrackPoint.Id startTrackPointId) {
|
||||
this.contentProviderUtils = contentProviderUtils;
|
||||
this.trackId = trackId;
|
||||
|
||||
|
||||
@@ -0,0 +1,4 @@
|
||||
package de.dennisguse.opentracks.data.models;
|
||||
|
||||
public record AltitudeExtremities(double min_m, double max_m) {
|
||||
}
|
||||
@@ -26,6 +26,7 @@ public record Speed(double speed_mps) {
|
||||
return of(Distance.ofKilometer(speed_kmh), Duration.ofHours(1));
|
||||
}
|
||||
|
||||
//TODO Like Duration.ZERO
|
||||
public static Speed zero() {
|
||||
return of(0.0);
|
||||
}
|
||||
|
||||
@@ -0,0 +1,61 @@
|
||||
package de.dennisguse.opentracks.data.models;
|
||||
|
||||
import androidx.annotation.Nullable;
|
||||
|
||||
import java.time.Duration;
|
||||
import java.time.Instant;
|
||||
|
||||
public record Statistics(
|
||||
Instant startTime,
|
||||
Instant stopTime,
|
||||
//TODO Rename to totalDuration
|
||||
Duration totalTime,
|
||||
//TODO Rename to movingDuration
|
||||
Duration movingTime, // Based on when we believe the user is traveling
|
||||
|
||||
Distance totalDistance,
|
||||
//TODO Check if this is persisted; if not: remove
|
||||
Speed maxSpeed,
|
||||
|
||||
@Nullable
|
||||
AltitudeExtremities altitudeExtremities,
|
||||
|
||||
@Nullable
|
||||
AltitudeGainLoss altitudeGainLoss,
|
||||
|
||||
//NOTE: The following values are not persisted.
|
||||
@Nullable
|
||||
HeartRate avgHeartRate,
|
||||
@Nullable
|
||||
Power avgPower
|
||||
) {
|
||||
|
||||
public Duration getStoppedTime() {
|
||||
return totalTime.minus(movingTime);
|
||||
}
|
||||
|
||||
public Speed getAverageSpeed() {
|
||||
if (totalTime.isZero()) {
|
||||
return Speed.of(0);
|
||||
}
|
||||
return Speed.of(totalDistance, totalTime);
|
||||
}
|
||||
|
||||
public Speed getAverageMovingSpeed() {
|
||||
return Speed.of(totalDistance, movingTime);
|
||||
}
|
||||
|
||||
@Deprecated
|
||||
//TODO null should be return (need adjusting ContentPtoviderUtils.createContentValues()
|
||||
public double getMinAltitude() {
|
||||
if (altitudeExtremities == null) return 0;
|
||||
return altitudeExtremities.min_m();
|
||||
}
|
||||
|
||||
@Deprecated
|
||||
//TODO null should be return (need adjusting ContentPtoviderUtils.createContentValues()
|
||||
public double getMaxAltitude() {
|
||||
if (altitudeExtremities == null) return 0;
|
||||
return altitudeExtremities.max_m();
|
||||
}
|
||||
}
|
||||
@@ -26,10 +26,9 @@ import androidx.annotation.VisibleForTesting;
|
||||
|
||||
import java.time.OffsetDateTime;
|
||||
import java.time.ZoneOffset;
|
||||
import java.util.Objects;
|
||||
import java.util.UUID;
|
||||
|
||||
import de.dennisguse.opentracks.stats.TrackStatistics;
|
||||
import de.dennisguse.opentracks.stats.SegmentStatisticUpdater;
|
||||
|
||||
/**
|
||||
* A track.
|
||||
@@ -49,17 +48,31 @@ public class Track {
|
||||
|
||||
private ActivityType activityType;
|
||||
|
||||
//TODO Move to Statistics?
|
||||
private ZoneOffset zoneOffset;
|
||||
|
||||
private TrackStatistics trackStatistics = new TrackStatistics();
|
||||
@NonNull
|
||||
private Statistics trackStatistics;
|
||||
|
||||
@Deprecated
|
||||
@VisibleForTesting
|
||||
public Track() {
|
||||
this(ZoneOffset.UTC);
|
||||
}
|
||||
|
||||
@VisibleForTesting
|
||||
public Track(Statistics statistics) {
|
||||
this(ZoneOffset.UTC, statistics);
|
||||
}
|
||||
|
||||
@Deprecated //TODO Remove
|
||||
public Track(@NonNull ZoneOffset zoneOffset) {
|
||||
setZoneOffset(zoneOffset);
|
||||
this(zoneOffset, new SegmentStatisticUpdater().getStatistics());
|
||||
}
|
||||
|
||||
public Track(@NonNull ZoneOffset zoneOffset, @NonNull Statistics trackStatistics) {
|
||||
this.zoneOffset = zoneOffset;
|
||||
this.trackStatistics = trackStatistics;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -129,23 +142,23 @@ public class Track {
|
||||
|
||||
public OffsetDateTime getStartTime() {
|
||||
return trackStatistics
|
||||
.getStartTime().atOffset(zoneOffset);
|
||||
.startTime().atOffset(zoneOffset);
|
||||
}
|
||||
|
||||
public OffsetDateTime getStopTime() {
|
||||
return trackStatistics
|
||||
.getStopTime().atOffset(zoneOffset);
|
||||
.stopTime().atOffset(zoneOffset);
|
||||
}
|
||||
|
||||
@NonNull
|
||||
public TrackStatistics getTrackStatistics() {
|
||||
public Statistics getStatistics() {
|
||||
return trackStatistics;
|
||||
}
|
||||
|
||||
public void setTrackStatistics(@NonNull TrackStatistics trackStatistics) {
|
||||
public void setStatistics(Statistics trackStatistics) {
|
||||
this.trackStatistics = trackStatistics;
|
||||
}
|
||||
|
||||
@NonNull
|
||||
@Override
|
||||
public String toString() {
|
||||
return "Track{" +
|
||||
@@ -156,20 +169,6 @@ public class Track {
|
||||
'}';
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object o) {
|
||||
if (this == o) return true;
|
||||
if (o == null || getClass() != o.getClass()) return false;
|
||||
Track track = (Track) o;
|
||||
return id.equals(track.id);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return Objects.hash(id);
|
||||
}
|
||||
|
||||
|
||||
public record Id(long id) implements Parcelable {
|
||||
|
||||
@Override
|
||||
|
||||
@@ -83,8 +83,7 @@ public class TrackPoint {
|
||||
private HeartRate heartRate = null;
|
||||
private Cadence cadence = null;
|
||||
private Power power = null;
|
||||
private Float altitudeGain_m = null;
|
||||
private Float altitudeLoss_m = null;
|
||||
private AltitudeGainLoss altitudeGainLoss = null;
|
||||
|
||||
public TrackPoint(@Nullable TrackPoint.Id id, @NonNull Type type, @NonNull Position position) {
|
||||
this.id = id;
|
||||
@@ -163,31 +162,21 @@ public class TrackPoint {
|
||||
return getPosition().toLocation();
|
||||
}
|
||||
|
||||
public boolean hasAltitudeGain() {
|
||||
return altitudeGain_m != null;
|
||||
public boolean hasAltitudeGainLoss() {
|
||||
return altitudeGainLoss != null;
|
||||
}
|
||||
|
||||
public float getAltitudeGain() {
|
||||
return altitudeGain_m;
|
||||
public AltitudeGainLoss getAltitudeGainLoss() {
|
||||
return altitudeGainLoss;
|
||||
}
|
||||
|
||||
@Deprecated
|
||||
public TrackPoint setAltitudeGain(Float altitudeGain_m) {
|
||||
this.altitudeGain_m = altitudeGain_m;
|
||||
public TrackPoint setAltitudeGainLoss(AltitudeGainLoss altitudeGainLoss) {
|
||||
this.altitudeGainLoss = altitudeGainLoss;
|
||||
return this;
|
||||
}
|
||||
|
||||
public boolean hasAltitudeLoss() {
|
||||
return altitudeLoss_m != null;
|
||||
}
|
||||
|
||||
public float getAltitudeLoss() {
|
||||
return altitudeLoss_m;
|
||||
}
|
||||
|
||||
@Deprecated
|
||||
public TrackPoint setAltitudeLoss(Float altitudeLoss_m) {
|
||||
this.altitudeLoss_m = altitudeLoss_m;
|
||||
public TrackPoint setAltitudeGainLoss(float altitudeGain_m, float altitudeLoss_m) {
|
||||
setAltitudeGainLoss(new AltitudeGainLoss(altitudeGain_m, altitudeLoss_m));
|
||||
return this;
|
||||
}
|
||||
|
||||
@@ -326,11 +315,10 @@ public class TrackPoint {
|
||||
if (hasSensorDistance() && lastTrackPoint.hasSensorDistance()) {
|
||||
sensorDistance = sensorDistance.minus(lastTrackPoint.getSensorDistance());
|
||||
}
|
||||
if (hasAltitudeGain() && lastTrackPoint.hasAltitudeGain()) {
|
||||
altitudeGain_m -= lastTrackPoint.altitudeGain_m;
|
||||
}
|
||||
if (hasAltitudeLoss() && lastTrackPoint.hasAltitudeLoss()) {
|
||||
altitudeLoss_m -= lastTrackPoint.altitudeLoss_m;
|
||||
if (hasAltitudeGainLoss() && lastTrackPoint.hasAltitudeGainLoss()) {
|
||||
altitudeGainLoss = new AltitudeGainLoss(
|
||||
altitudeGainLoss.gain_m() - lastTrackPoint.altitudeGainLoss.gain_m(),
|
||||
altitudeGainLoss.loss_m() - lastTrackPoint.altitudeGainLoss.loss_m());
|
||||
}
|
||||
return this;
|
||||
}
|
||||
@@ -391,14 +379,13 @@ public class TrackPoint {
|
||||
public String toString() {
|
||||
return "TrackPoint{" +
|
||||
"id=" + id +
|
||||
", type=" + type +
|
||||
", position=" + position +
|
||||
", sensorDistance=" + sensorDistance +
|
||||
", type=" + type +
|
||||
", heartRate=" + heartRate +
|
||||
", cadence=" + cadence +
|
||||
", power=" + power +
|
||||
", altitudeGain_m=" + altitudeGain_m +
|
||||
", altitudeLoss_m=" + altitudeLoss_m +
|
||||
", altitudeGainLoss=" + altitudeGainLoss +
|
||||
'}';
|
||||
}
|
||||
|
||||
|
||||
@@ -30,16 +30,15 @@ import androidx.core.content.ContextCompat;
|
||||
import androidx.fragment.app.Fragment;
|
||||
|
||||
import de.dennisguse.opentracks.R;
|
||||
import de.dennisguse.opentracks.TrackRecordedActivity;
|
||||
import de.dennisguse.opentracks.data.ContentProviderUtils;
|
||||
import de.dennisguse.opentracks.data.models.DistanceFormatter;
|
||||
import de.dennisguse.opentracks.data.models.SpeedFormatter;
|
||||
import de.dennisguse.opentracks.data.models.Statistics;
|
||||
import de.dennisguse.opentracks.data.models.Track;
|
||||
import de.dennisguse.opentracks.databinding.StatisticsRecordedBinding;
|
||||
import de.dennisguse.opentracks.settings.PreferencesUtils;
|
||||
import de.dennisguse.opentracks.settings.UnitSystem;
|
||||
import de.dennisguse.opentracks.stats.SensorStatistics;
|
||||
import de.dennisguse.opentracks.stats.TrackStatistics;
|
||||
import de.dennisguse.opentracks.util.StringUtils;
|
||||
|
||||
/**
|
||||
@@ -157,8 +156,6 @@ public class StatisticsRecordedFragment extends Fragment {
|
||||
loadTrackDescription(track);
|
||||
updateUI();
|
||||
updateSensorUI();
|
||||
|
||||
((TrackRecordedActivity) getActivity()).startPostponedEnterTransitionWith(viewBinding.statsActivityTypeIcon);
|
||||
}
|
||||
});
|
||||
}
|
||||
@@ -171,12 +168,12 @@ public class StatisticsRecordedFragment extends Fragment {
|
||||
}
|
||||
|
||||
private void updateUI() {
|
||||
TrackStatistics trackStatistics = track.getTrackStatistics();
|
||||
Statistics trackStatistics = track.getStatistics();
|
||||
// Set total distance
|
||||
{
|
||||
Pair<String, String> parts = DistanceFormatter.Builder()
|
||||
.setUnit(unitSystem)
|
||||
.build(getContext()).getDistanceParts(trackStatistics.getTotalDistance());
|
||||
.build(getContext()).getDistanceParts(trackStatistics.totalDistance());
|
||||
|
||||
viewBinding.statsDistanceValue.setText(parts.first);
|
||||
viewBinding.statsDistanceUnit.setText(parts.second);
|
||||
@@ -187,8 +184,8 @@ public class StatisticsRecordedFragment extends Fragment {
|
||||
|
||||
// Set time and start datetime
|
||||
{
|
||||
viewBinding.statsMovingTimeValue.setText(StringUtils.formatElapsedTime(trackStatistics.getMovingTime()));
|
||||
viewBinding.statsTotalTimeValue.setText(StringUtils.formatElapsedTime(trackStatistics.getTotalTime()));
|
||||
viewBinding.statsMovingTimeValue.setText(StringUtils.formatElapsedTime(trackStatistics.movingTime()));
|
||||
viewBinding.statsTotalTimeValue.setText(StringUtils.formatElapsedTime(trackStatistics.totalTime()));
|
||||
}
|
||||
|
||||
SpeedFormatter formatter = SpeedFormatter.Builder().setUnit(unitSystem).setReportSpeedOrPace(preferenceReportSpeed).build(getContext());
|
||||
@@ -205,7 +202,7 @@ public class StatisticsRecordedFragment extends Fragment {
|
||||
{
|
||||
viewBinding.statsMaxSpeedLabel.setText(preferenceReportSpeed ? R.string.stats_max_speed : R.string.stats_fastest_pace);
|
||||
|
||||
Pair<String, String> parts = formatter.getSpeedParts(trackStatistics.getMaxSpeed());
|
||||
Pair<String, String> parts = formatter.getSpeedParts(trackStatistics.maxSpeed());
|
||||
viewBinding.statsMaxSpeedValue.setText(parts.first);
|
||||
viewBinding.statsMaxSpeedUnit.setText(parts.second);
|
||||
}
|
||||
@@ -221,8 +218,8 @@ public class StatisticsRecordedFragment extends Fragment {
|
||||
|
||||
// Set altitude gain and loss
|
||||
{
|
||||
Float altitudeGain_m = trackStatistics.getTotalAltitudeGain();
|
||||
Float altitudeLoss_m = trackStatistics.getTotalAltitudeLoss();
|
||||
Float altitudeGain_m = trackStatistics.altitudeGainLoss() != null ? trackStatistics.altitudeGainLoss().gain_m() : null;
|
||||
Float altitudeLoss_m = trackStatistics.altitudeGainLoss() != null ? trackStatistics.altitudeGainLoss().loss_m() : null;
|
||||
|
||||
Pair<String, String> parts;
|
||||
|
||||
|
||||
@@ -11,6 +11,7 @@ import java.util.UUID;
|
||||
import java.util.regex.Matcher;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
import de.dennisguse.opentracks.data.models.Statistics;
|
||||
import de.dennisguse.opentracks.data.models.Track;
|
||||
import de.dennisguse.opentracks.util.FileUtils;
|
||||
|
||||
@@ -91,7 +92,7 @@ public class TrackFilenameGenerator {
|
||||
Track track = new Track();
|
||||
track.setName("Berlin");
|
||||
track.setUuid(UUID.fromString("f81d4fae-7dec-11d0-a765-00a0c91e6bf6"));
|
||||
track.getTrackStatistics().setStartTime(Instant.ofEpochMilli(0));
|
||||
track.setStatistics(new Statistics(Instant.EPOCH, null, null, null, null, null, null, null, null, null));
|
||||
|
||||
return format(track, TrackFileFormat.KMZ_WITH_TRACKDETAIL_AND_SENSORDATA_AND_PICTURES);
|
||||
}
|
||||
|
||||
@@ -28,10 +28,10 @@ import java.util.Locale;
|
||||
import java.util.function.Function;
|
||||
|
||||
import de.dennisguse.opentracks.data.ContentProviderUtils;
|
||||
import de.dennisguse.opentracks.data.TrackPointIterator;
|
||||
import de.dennisguse.opentracks.data.models.Marker;
|
||||
import de.dennisguse.opentracks.data.models.Track;
|
||||
import de.dennisguse.opentracks.data.models.TrackPoint;
|
||||
import de.dennisguse.opentracks.util.AutoCloseableIterator;
|
||||
import de.dennisguse.opentracks.util.StringUtils;
|
||||
|
||||
/**
|
||||
@@ -98,8 +98,8 @@ public class CSVTrackExporter implements TrackExporter {
|
||||
new Column("accuracy_vertical", t -> t.hasVerticalAccuracy() ? DISTANCE_FORMAT.format(t.getVerticalAccuracy().toM()) : ""),
|
||||
|
||||
new Column("speed", t -> t.hasSpeed() ? SPEED_FORMAT.format(t.getSpeed().toKMH()) : ""),
|
||||
new Column("altitude_gain", t -> t.hasAltitudeGain() ? ALTITUDE_FORMAT.format(t.getAltitudeGain()) : ""),
|
||||
new Column("altitude_loss", t -> t.hasAltitudeLoss() ? ALTITUDE_FORMAT.format(t.getAltitudeLoss()) : ""),
|
||||
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()) : ""),
|
||||
@@ -131,7 +131,7 @@ public class CSVTrackExporter implements TrackExporter {
|
||||
}
|
||||
|
||||
private void writeTrackPoints(List<Column> columns, Track track) throws InterruptedException {
|
||||
try (TrackPointIterator trackPointIterator = contentProviderUtils.getTrackPointLocationIterator(track.getId(), null)) {
|
||||
try (AutoCloseableIterator<TrackPoint> trackPointIterator = contentProviderUtils.getTrackPointLocationIterator(track.getId(), null)) {
|
||||
while (trackPointIterator.hasNext()) {
|
||||
if (Thread.interrupted()) throw new InterruptedException();
|
||||
|
||||
|
||||
@@ -44,7 +44,7 @@ public class ExportUtils {
|
||||
WorkRequest exportRequest = new OneTimeWorkRequest.Builder(ExportWorker.class)
|
||||
.setInputData(new Data.Builder()
|
||||
.putLongArray(ExportWorker.TRACKIDS_KEY, new long[]{trackId.id()})
|
||||
.putString(ExportWorker.DIRECTORY_URI_KEY, directory.toString())
|
||||
.putString(ExportWorker.DIRECTORY_URI_KEY, directory.getUri().toString())
|
||||
.putString(ExportWorker.TRACKFILEFORMAT_KEY, trackFileFormat.toString())
|
||||
.putString(ExportWorker.FILENAME_KEY, null)
|
||||
.build())
|
||||
|
||||
@@ -33,13 +33,13 @@ import java.util.function.Function;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
import de.dennisguse.opentracks.data.ContentProviderUtils;
|
||||
import de.dennisguse.opentracks.data.TrackPointIterator;
|
||||
import de.dennisguse.opentracks.data.models.Distance;
|
||||
import de.dennisguse.opentracks.data.models.Marker;
|
||||
import de.dennisguse.opentracks.data.models.Position;
|
||||
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.stats.TrackStatistics;
|
||||
import de.dennisguse.opentracks.util.AutoCloseableIterator;
|
||||
import de.dennisguse.opentracks.util.StringUtils;
|
||||
|
||||
/**
|
||||
@@ -128,7 +128,7 @@ public class GPXTrackExporter implements TrackExporter {
|
||||
|
||||
LinkedList<TrackPoint> sensorPoints = new LinkedList<>();
|
||||
|
||||
try (TrackPointIterator trackPointIterator = contentProviderUtils.getTrackPointLocationIterator(track.getId(), null)) {
|
||||
try (AutoCloseableIterator<TrackPoint> trackPointIterator = contentProviderUtils.getTrackPointLocationIterator(track.getId(), null)) {
|
||||
while (trackPointIterator.hasNext()) {
|
||||
if (Thread.interrupted()) throw new InterruptedException();
|
||||
|
||||
@@ -279,18 +279,16 @@ public class GPXTrackExporter implements TrackExporter {
|
||||
printWriter.println("<opentracks:typeTranslated>" + StringUtils.formatCData(track.getActivityTypeLocalized()) + "</opentracks:typeTranslated>");
|
||||
}
|
||||
|
||||
TrackStatistics trackStatistics = track.getTrackStatistics();
|
||||
Statistics trackStatistics = track.getStatistics();
|
||||
printWriter.println("<gpxtrkx:TrackStatsExtension>");
|
||||
printWriter.println("<gpxtrkx:Distance>" + trackStatistics.getTotalDistance().toM() + "</gpxtrkx:Distance>");
|
||||
printWriter.println("<gpxtrkx:TimerTime>" + trackStatistics.getTotalTime().getSeconds() + "</gpxtrkx:TimerTime>");
|
||||
printWriter.println("<gpxtrkx:MovingTime>" + trackStatistics.getMovingTime().getSeconds() + "</gpxtrkx:MovingTime>");
|
||||
printWriter.println("<gpxtrkx:Distance>" + trackStatistics.totalDistance().toM() + "</gpxtrkx:Distance>");
|
||||
printWriter.println("<gpxtrkx:TimerTime>" + trackStatistics.totalTime().getSeconds() + "</gpxtrkx:TimerTime>");
|
||||
printWriter.println("<gpxtrkx:MovingTime>" + trackStatistics.movingTime().getSeconds() + "</gpxtrkx:MovingTime>");
|
||||
printWriter.println("<gpxtrkx:StoppedTime>" + trackStatistics.getStoppedTime().getSeconds() + "</gpxtrkx:StoppedTime>");
|
||||
printWriter.println("<gpxtrkx:MaxSpeed>" + trackStatistics.getMaxSpeed().toMPS() + "</gpxtrkx:MaxSpeed>");
|
||||
if (trackStatistics.hasTotalAltitudeGain()) {
|
||||
printWriter.println("<gpxtrkx:Ascent>" + trackStatistics.getTotalAltitudeGain() + "</gpxtrkx:Ascent>");
|
||||
}
|
||||
if (trackStatistics.hasTotalAltitudeLoss()) {
|
||||
printWriter.println("<gpxtrkx:Descent>" + trackStatistics.getTotalAltitudeLoss() + "</gpxtrkx:Descent>");
|
||||
printWriter.println("<gpxtrkx:MaxSpeed>" + trackStatistics.maxSpeed().toMPS() + "</gpxtrkx:MaxSpeed>");
|
||||
if (trackStatistics.altitudeGainLoss() != null) {
|
||||
printWriter.println("<gpxtrkx:Ascent>" + trackStatistics.altitudeGainLoss().gain_m() + "</gpxtrkx:Ascent>");
|
||||
printWriter.println("<gpxtrkx:Descent>" + trackStatistics.altitudeGainLoss().loss_m() + "</gpxtrkx:Descent>");
|
||||
}
|
||||
printWriter.println("</gpxtrkx:TrackStatsExtension>");
|
||||
|
||||
@@ -340,12 +338,12 @@ public class GPXTrackExporter implements TrackExporter {
|
||||
extensionContent += "<pwr:PowerInWatts>" + POWER_FORMAT.format(trackPoint.getPower().getW()) + "</pwr:PowerInWatts>\n";
|
||||
}
|
||||
|
||||
Double cumulativeGain = cumulateSensorData(trackPoint, sensorPoints, (tp) -> tp.hasAltitudeGain() ? (double) tp.getAltitudeGain() : null);
|
||||
Double cumulativeGain = cumulateSensorData(trackPoint, sensorPoints, (tp) -> tp.hasAltitudeGainLoss() ? (double) tp.getAltitudeGainLoss().gain_m() : null);
|
||||
if (cumulativeGain != null) {
|
||||
extensionContent += ("<opentracks:gain>" + ALTITUDE_FORMAT.format(cumulativeGain) + "</opentracks:gain>\n");
|
||||
}
|
||||
|
||||
Double cumulativeLoss = cumulateSensorData(trackPoint, sensorPoints, (tp) -> tp.hasAltitudeLoss() ? (double) tp.getAltitudeLoss() : null);
|
||||
Double cumulativeLoss = cumulateSensorData(trackPoint, sensorPoints, (tp) -> tp.hasAltitudeGainLoss() ? (double) tp.getAltitudeGainLoss().loss_m() : null);
|
||||
if (cumulativeLoss != null) {
|
||||
extensionContent += ("<opentracks:loss>" + ALTITUDE_FORMAT.format(cumulativeLoss) + "</opentracks:loss>\n");
|
||||
}
|
||||
|
||||
@@ -36,13 +36,13 @@ import java.util.Objects;
|
||||
|
||||
import de.dennisguse.opentracks.R;
|
||||
import de.dennisguse.opentracks.data.ContentProviderUtils;
|
||||
import de.dennisguse.opentracks.data.TrackPointIterator;
|
||||
import de.dennisguse.opentracks.data.models.ActivityType;
|
||||
import de.dennisguse.opentracks.data.models.Marker;
|
||||
import de.dennisguse.opentracks.data.models.Position;
|
||||
import de.dennisguse.opentracks.data.models.Track;
|
||||
import de.dennisguse.opentracks.data.models.TrackPoint;
|
||||
import de.dennisguse.opentracks.ui.markers.MarkerUtils;
|
||||
import de.dennisguse.opentracks.util.AutoCloseableIterator;
|
||||
import de.dennisguse.opentracks.util.StringUtils;
|
||||
|
||||
/**
|
||||
@@ -162,7 +162,7 @@ public class KMLTrackExporter implements TrackExporter {
|
||||
boolean wroteTrack = false;
|
||||
boolean wroteSegment = false;
|
||||
|
||||
try (TrackPointIterator trackPointIterator = contentProviderUtils.getTrackPointLocationIterator(track.getId(), null)) {
|
||||
try (AutoCloseableIterator<TrackPoint> trackPointIterator = contentProviderUtils.getTrackPointLocationIterator(track.getId(), null)) {
|
||||
while (trackPointIterator.hasNext()) {
|
||||
if (Thread.interrupted()) throw new InterruptedException();
|
||||
|
||||
@@ -384,9 +384,13 @@ public class KMLTrackExporter implements TrackExporter {
|
||||
heartRateList.add(trackPoint.hasHeartRate() ? trackPoint.getHeartRate().getBPM() : null);
|
||||
cadenceList.add(trackPoint.hasCadence() ? trackPoint.getCadence().getRPM() : null);
|
||||
powerList.add(trackPoint.hasPower() ? trackPoint.getPower().getW() : null);
|
||||
|
||||
altitudeGainList.add(trackPoint.hasAltitudeGain() ? trackPoint.getAltitudeGain() : null);
|
||||
altitudeLossList.add(trackPoint.hasAltitudeLoss() ? trackPoint.getAltitudeLoss() : null);
|
||||
if (trackPoint.hasAltitudeGainLoss()) {
|
||||
altitudeGainList.add(trackPoint.getAltitudeGainLoss().loss_m());
|
||||
altitudeLossList.add(trackPoint.getAltitudeGainLoss().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);
|
||||
}
|
||||
|
||||
@@ -38,6 +38,7 @@ import de.dennisguse.opentracks.data.models.Position;
|
||||
import de.dennisguse.opentracks.data.models.Speed;
|
||||
import de.dennisguse.opentracks.data.models.Track;
|
||||
import de.dennisguse.opentracks.data.models.TrackPoint;
|
||||
import de.dennisguse.opentracks.stats.SegmentStatisticUpdater;
|
||||
import de.dennisguse.opentracks.util.StringUtils;
|
||||
|
||||
/**
|
||||
@@ -48,7 +49,7 @@ import de.dennisguse.opentracks.util.StringUtils;
|
||||
* <p>
|
||||
* {@link de.dennisguse.opentracks.io.file.exporter.GPXTrackExporter} does not export information if a segment was started automatic or manually.
|
||||
* Therefore, all segments starts are marked as SEGMENT_START_AUTOMATIC.
|
||||
* Thus, the {@link de.dennisguse.opentracks.stats.TrackStatistics} cannot be restored correctly.
|
||||
* Thus, the {@link SegmentStatisticUpdater} cannot be restored correctly.
|
||||
*
|
||||
* @author Jimmy Shih
|
||||
*/
|
||||
@@ -368,18 +369,11 @@ public class GPXTrackImporter extends DefaultHandler implements XMLImporter.Trac
|
||||
}
|
||||
}
|
||||
|
||||
if (gain != null) {
|
||||
if (gain != null && loss != null) {
|
||||
try {
|
||||
trackPoint.setAltitudeGain(Float.parseFloat(gain));
|
||||
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", gain)), e);
|
||||
}
|
||||
}
|
||||
if (loss != null) {
|
||||
try {
|
||||
trackPoint.setAltitudeLoss(Float.parseFloat(loss));
|
||||
} catch (NumberFormatException e) {
|
||||
throw new ParsingException(createErrorMessage(String.format(Locale.US, "Unable to parse altitude loss: %s", loss)), e);
|
||||
throw new ParsingException(createErrorMessage(String.format(Locale.US, "Unable to parse altitude gain: %s or los: %s", gain, loss)), e);
|
||||
}
|
||||
}
|
||||
if (sensorDistance != null) {
|
||||
|
||||
@@ -342,11 +342,10 @@ public class KMLTrackImporter extends DefaultHandler implements XMLImporter.Trac
|
||||
if (i < sensorPowerList.size() && sensorPowerList.get(i) != null) {
|
||||
trackPoint.setPower(sensorPowerList.get(i));
|
||||
}
|
||||
if (i < altitudeGainList.size()) {
|
||||
trackPoint.setAltitudeGain(altitudeGainList.get(i));
|
||||
}
|
||||
if (i < altitudeLossList.size()) {
|
||||
trackPoint.setAltitudeLoss(altitudeLossList.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 < accuracyHorizontal.size() && accuracyHorizontal.get(i) != null) {
|
||||
trackPoint.setHorizontalAccuracy(Distance.of(accuracyHorizontal.get(i)));
|
||||
|
||||
@@ -147,7 +147,7 @@ public class TrackImporter {
|
||||
|
||||
TrackStatisticsUpdater updater = new TrackStatisticsUpdater();
|
||||
updater.addTrackPoints(trackPoints);
|
||||
track.setTrackStatistics(updater.getTrackStatistics());
|
||||
track.setStatistics(updater.getTrackStatistics());
|
||||
|
||||
Track.Id trackId = contentProviderUtils.insertTrack(track);
|
||||
|
||||
|
||||
@@ -3,6 +3,7 @@ package de.dennisguse.opentracks.sensors;
|
||||
import android.content.Context;
|
||||
import android.content.SharedPreferences;
|
||||
import android.os.Handler;
|
||||
import android.os.PowerManager;
|
||||
import android.util.Log;
|
||||
|
||||
import androidx.annotation.Nullable;
|
||||
@@ -15,6 +16,7 @@ import de.dennisguse.opentracks.sensors.sensorData.Aggregator;
|
||||
import de.dennisguse.opentracks.sensors.sensorData.Raw;
|
||||
import de.dennisguse.opentracks.sensors.sensorData.SensorDataSet;
|
||||
import de.dennisguse.opentracks.services.handlers.TrackPointCreator;
|
||||
import de.dennisguse.opentracks.util.SystemUtils;
|
||||
|
||||
public class SensorManager implements SharedPreferences.OnSharedPreferenceChangeListener {
|
||||
|
||||
@@ -24,6 +26,8 @@ public class SensorManager implements SharedPreferences.OnSharedPreferenceChange
|
||||
@VisibleForTesting
|
||||
public SensorDataSet sensorDataSet;
|
||||
|
||||
private PowerManager.WakeLock wakeLock;
|
||||
|
||||
private final TrackPointCreator observer;
|
||||
|
||||
private final SensorDataChangedObserver listener = new SensorDataChangedObserver() {
|
||||
@@ -71,6 +75,8 @@ public class SensorManager implements SharedPreferences.OnSharedPreferenceChange
|
||||
throw new RuntimeException("SensorManager cannot be started twice; stop first.");
|
||||
}
|
||||
|
||||
wakeLock = SystemUtils.acquireWakeLock(context, wakeLock);
|
||||
|
||||
gpsManager = new GpsManager(observer, listener);
|
||||
altitudeSumManager = new GainManager(listener);
|
||||
bluetoothSensorManager = new BluetoothRemoteSensorManager(context, handler, listener);
|
||||
@@ -83,6 +89,8 @@ public class SensorManager implements SharedPreferences.OnSharedPreferenceChange
|
||||
}
|
||||
|
||||
public void stop(Context context) {
|
||||
wakeLock = SystemUtils.releaseWakeLock(wakeLock);
|
||||
|
||||
bluetoothSensorManager.stop(context);
|
||||
bluetoothSensorManager = null;
|
||||
|
||||
|
||||
@@ -185,8 +185,7 @@ public class SensorDataSet {
|
||||
}
|
||||
|
||||
if (barometer != null && barometer.hasReceivedData()) {
|
||||
trackPoint.setAltitudeGain(barometer.getAggregatedValue(trackPointCreator.createNow()).gain_m());
|
||||
trackPoint.setAltitudeLoss(barometer.getAggregatedValue(trackPointCreator.createNow()).loss_m());
|
||||
trackPoint.setAltitudeGainLoss(barometer.getAggregatedValue(trackPointCreator.createNow()).gain_m(), barometer.getAggregatedValue(trackPointCreator.createNow()).loss_m());
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -2,15 +2,16 @@ package de.dennisguse.opentracks.services;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
|
||||
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.sensors.sensorData.SensorDataSet;
|
||||
import de.dennisguse.opentracks.stats.TrackStatistics;
|
||||
import de.dennisguse.opentracks.stats.SegmentStatisticUpdater;
|
||||
|
||||
/**
|
||||
* {@link Track} and {@link TrackPoint} must be immutable (i.e., their content does not change).
|
||||
*/
|
||||
public record RecordingData(Track track, TrackPoint latestTrackPoint, SensorDataSet sensorDataSet, TrackStatistics currentSegment) {
|
||||
public record RecordingData(Track track, TrackPoint latestTrackPoint, SensorDataSet sensorDataSet, Statistics currentSegment) {
|
||||
public String getTrackCategory() {
|
||||
if (track == null) {
|
||||
return "";
|
||||
@@ -18,22 +19,13 @@ public record RecordingData(Track track, TrackPoint latestTrackPoint, SensorData
|
||||
return track.getActivityTypeLocalized();
|
||||
}
|
||||
|
||||
@Deprecated
|
||||
@NonNull
|
||||
public TrackStatistics getStatisticsTrack() {
|
||||
public Statistics getStatisticsTrack() {
|
||||
if (track == null) {
|
||||
return new TrackStatistics();
|
||||
return new SegmentStatisticUpdater().getStatistics();
|
||||
}
|
||||
|
||||
return track.getTrackStatistics();
|
||||
return track.getStatistics();
|
||||
}
|
||||
|
||||
@NonNull
|
||||
public TrackStatistics getStatisticsCurrentSegment() {
|
||||
if (currentSegment == null) {
|
||||
return new TrackStatistics();
|
||||
}
|
||||
|
||||
return currentSegment;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -17,13 +17,13 @@ import de.dennisguse.opentracks.R;
|
||||
import de.dennisguse.opentracks.data.ContentProviderUtils;
|
||||
import de.dennisguse.opentracks.data.models.ActivityType;
|
||||
import de.dennisguse.opentracks.data.models.Distance;
|
||||
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.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.stats.TrackStatistics;
|
||||
import de.dennisguse.opentracks.stats.TrackStatisticsUpdater;
|
||||
import de.dennisguse.opentracks.util.TrackNameUtils;
|
||||
|
||||
@@ -80,7 +80,7 @@ public class TrackRecordingManager implements SharedPreferences.OnSharedPreferen
|
||||
String activityTypeLocalized = PreferencesUtils.getDefaultActivityTypeLocalized();
|
||||
track.setActivityTypeLocalized(activityTypeLocalized);
|
||||
track.setActivityType(ActivityType.findByLocalizedString(context, activityTypeLocalized));
|
||||
track.setTrackStatistics(trackStatisticsUpdater.getTrackStatistics());
|
||||
track.setStatistics(trackStatisticsUpdater.getTrackStatistics());
|
||||
track.setName(TrackNameUtils.getTrackName(context, trackId, track.getStartTime()));
|
||||
contentProviderUtils.updateTrack(track);
|
||||
|
||||
@@ -98,7 +98,7 @@ public class TrackRecordingManager implements SharedPreferences.OnSharedPreferen
|
||||
return false;
|
||||
}
|
||||
|
||||
trackStatisticsUpdater = new TrackStatisticsUpdater(track.getTrackStatistics());
|
||||
trackStatisticsUpdater = new TrackStatisticsUpdater(track.getStatistics());
|
||||
onNewTrackPoint(trackPointCreator.createSegmentStartManual());
|
||||
|
||||
reset();
|
||||
@@ -124,16 +124,15 @@ public class TrackRecordingManager implements SharedPreferences.OnSharedPreferen
|
||||
|
||||
ALTITUDE_CORRECTION_MANAGER.correctAltitude(context, current.first);
|
||||
|
||||
Track track = contentProviderUtils.getTrack(trackId); //Get copy
|
||||
Track track = contentProviderUtils.getTrack(trackId); //Get copy TODO: Avoid re-loading data from DB
|
||||
if (track == null) {
|
||||
Log.w(TAG, "Requesting data if not recording is taking place, should not be done.");
|
||||
return null;
|
||||
}
|
||||
|
||||
track.setTrackStatistics(tmpTrackStatisticsUpdater.getTrackStatistics());
|
||||
TrackStatistics currentSegment = tmpTrackStatisticsUpdater.getCurrentSegment();
|
||||
track.setStatistics(tmpTrackStatisticsUpdater.getTrackStatistics());
|
||||
|
||||
return new RecordingData(track, current.first, current.second, currentSegment);
|
||||
return new RecordingData(track, current.first, current.second, tmpTrackStatisticsUpdater.getCurrentSegment());
|
||||
}
|
||||
|
||||
@VisibleForTesting(otherwise = VisibleForTesting.PACKAGE_PRIVATE)
|
||||
@@ -225,7 +224,7 @@ public class TrackRecordingManager implements SharedPreferences.OnSharedPreferen
|
||||
handler.postDelayed(ON_IDLE, idleDuration.toMillis());
|
||||
}
|
||||
|
||||
TrackStatistics getTrackStatistics() {
|
||||
Statistics getTrackStatistics() {
|
||||
return trackStatisticsUpdater == null ? null : trackStatisticsUpdater.getTrackStatistics();
|
||||
}
|
||||
|
||||
|
||||
@@ -22,7 +22,6 @@ import android.content.SharedPreferences;
|
||||
import android.content.pm.ServiceInfo;
|
||||
import android.os.Handler;
|
||||
import android.os.Looper;
|
||||
import android.os.PowerManager.WakeLock;
|
||||
import android.util.Log;
|
||||
|
||||
import androidx.annotation.Nullable;
|
||||
@@ -44,7 +43,6 @@ import de.dennisguse.opentracks.sensors.GpsStatusValue;
|
||||
import de.dennisguse.opentracks.services.announcement.VoiceAnnouncementManager;
|
||||
import de.dennisguse.opentracks.services.handlers.TrackPointCreator;
|
||||
import de.dennisguse.opentracks.settings.PreferencesUtils;
|
||||
import de.dennisguse.opentracks.util.SystemUtils;
|
||||
|
||||
public class TrackRecordingService extends Service implements TrackPointCreator.Callback, SharedPreferences.OnSharedPreferenceChangeListener, TrackRecordingManager.IdleObserver {
|
||||
|
||||
@@ -89,7 +87,6 @@ public class TrackRecordingService extends Service implements TrackPointCreator.
|
||||
private MutableLiveData<RecordingData> recordingDataObservable;
|
||||
|
||||
// The following variables are set when recording:
|
||||
private WakeLock wakeLock; //TODO Move to SensorManager
|
||||
private Handler handler;
|
||||
|
||||
private TrackPointCreator trackPointCreator;
|
||||
@@ -125,7 +122,7 @@ public class TrackRecordingService extends Service implements TrackPointCreator.
|
||||
if (isRecording()) {
|
||||
endCurrentTrack();
|
||||
}
|
||||
if (isSensorStarted()) {
|
||||
if (trackPointCreator != null && trackPointCreator.isStarted()) {
|
||||
stopSensors();
|
||||
}
|
||||
|
||||
@@ -200,7 +197,7 @@ public class TrackRecordingService extends Service implements TrackPointCreator.
|
||||
}
|
||||
|
||||
public void tryStartSensors() {
|
||||
if (isSensorStarted()) return;
|
||||
if (trackPointCreator.isStarted()) return;
|
||||
|
||||
Log.i(TAG, "tryStartSensors");
|
||||
|
||||
@@ -208,12 +205,11 @@ public class TrackRecordingService extends Service implements TrackPointCreator.
|
||||
}
|
||||
|
||||
private synchronized void startSensors() {
|
||||
if (isSensorStarted()) {
|
||||
if (trackPointCreator.isStarted()) {
|
||||
Log.i(TAG, "sensors already started; skipping");
|
||||
return;
|
||||
}
|
||||
Log.i(TAG, "startSensors");
|
||||
wakeLock = SystemUtils.acquireWakeLock(this, wakeLock);
|
||||
trackPointCreator.start(this, handler);
|
||||
|
||||
ServiceCompat.startForeground(this, TrackRecordingServiceNotificationManager.NOTIFICATION_ID, notificationManager.onSensorsStarted(this), ServiceInfo.FOREGROUND_SERVICE_TYPE_LOCATION + ServiceInfo.FOREGROUND_SERVICE_TYPE_CONNECTED_DEVICE);
|
||||
@@ -241,7 +237,6 @@ public class TrackRecordingService extends Service implements TrackPointCreator.
|
||||
trackPointCreator.stop();
|
||||
stopForeground(true);
|
||||
notificationManager.stop();
|
||||
wakeLock = SystemUtils.releaseWakeLock(wakeLock);
|
||||
gpsStatusObservable.postValue(STATUS_GPS_DEFAULT);
|
||||
}
|
||||
|
||||
@@ -318,7 +313,7 @@ public class TrackRecordingService extends Service implements TrackPointCreator.
|
||||
// Compute temporary track statistics using sensorData and update time.
|
||||
RecordingData data = trackRecordingManager.getDataForUI();
|
||||
|
||||
voiceAnnouncementManager.announceStatisticsIfNeeded(data.track(), data.sensorDataSet());
|
||||
voiceAnnouncementManager.announceStatisticsIfNeeded(data);
|
||||
|
||||
recordingDataObservable.postValue(data);
|
||||
}
|
||||
@@ -347,9 +342,6 @@ public class TrackRecordingService extends Service implements TrackPointCreator.
|
||||
return recordingStatus.isRecording();
|
||||
}
|
||||
|
||||
private boolean isSensorStarted() {
|
||||
return wakeLock != null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onSharedPreferenceChanged(SharedPreferences sharedPreferences, @Nullable String key) {
|
||||
|
||||
+1
-1
@@ -118,7 +118,7 @@ class TrackRecordingServiceNotificationManager implements SharedPreferences.OnSh
|
||||
previousLocationWasAccurate = currentLocationWasAccurate;
|
||||
}
|
||||
|
||||
notificationBuilder.setContentTitle(context.getString(R.string.track_distance_notification, formatter.formatDistance(recordingData.getStatisticsTrack().getTotalDistance())));
|
||||
notificationBuilder.setContentTitle(context.getString(R.string.track_distance_notification, formatter.formatDistance(recordingData.getStatisticsTrack().totalDistance())));
|
||||
String formattedSpeed = SpeedFormatter.Builder().setUnit(unitSystem).setReportSpeedOrPace(true).build(context).formatSpeed(recordingData.latestTrackPoint().getSpeed());
|
||||
notificationBuilder.setContentText(context.getString(R.string.track_speed_notification, formattedSpeed));
|
||||
notificationBuilder.setSubText(context.getString(R.string.track_recording_notification_accuracy, formattedAccuracy));
|
||||
|
||||
+29
-29
@@ -30,15 +30,14 @@ import java.util.List;
|
||||
|
||||
import de.dennisguse.opentracks.R;
|
||||
import de.dennisguse.opentracks.data.ContentProviderUtils;
|
||||
import de.dennisguse.opentracks.data.TrackPointIterator;
|
||||
import de.dennisguse.opentracks.data.models.Distance;
|
||||
import de.dennisguse.opentracks.data.models.Track;
|
||||
import de.dennisguse.opentracks.data.models.Statistics;
|
||||
import de.dennisguse.opentracks.data.models.TrackPoint;
|
||||
import de.dennisguse.opentracks.sensors.sensorData.SensorDataSet;
|
||||
import de.dennisguse.opentracks.services.RecordingData;
|
||||
import de.dennisguse.opentracks.settings.PreferencesUtils;
|
||||
import de.dennisguse.opentracks.stats.SensorStatistics;
|
||||
import de.dennisguse.opentracks.stats.TrackStatistics;
|
||||
import de.dennisguse.opentracks.ui.intervals.IntervalStatistics;
|
||||
import de.dennisguse.opentracks.ui.intervals.IntervalStatisticsUpdater;
|
||||
import de.dennisguse.opentracks.util.AutoCloseableIterator;
|
||||
|
||||
/**
|
||||
* Execute a periodic task on a time or distance schedule.
|
||||
@@ -53,7 +52,7 @@ public class VoiceAnnouncementManager implements SharedPreferences.OnSharedPrefe
|
||||
|
||||
private TTSManager voiceAnnouncement;
|
||||
|
||||
private TrackStatistics trackStatistics;
|
||||
private Statistics trackStatistics;
|
||||
|
||||
private static final Distance DISTANCE_OFF = Distance.of(Double.MAX_VALUE);
|
||||
private Distance distanceFrequency = DISTANCE_OFF;
|
||||
@@ -69,23 +68,23 @@ public class VoiceAnnouncementManager implements SharedPreferences.OnSharedPrefe
|
||||
private final ContentProviderUtils contentProviderUtils;
|
||||
private TrackPoint.Id startTrackPointId = null;
|
||||
|
||||
private IntervalStatistics intervalStatistics;
|
||||
private IntervalStatisticsUpdater intervalStatistics;
|
||||
private Distance intervalDistance;
|
||||
|
||||
public VoiceAnnouncementManager(@NonNull Context context) {
|
||||
this.context = context;
|
||||
contentProviderUtils = new ContentProviderUtils(context);
|
||||
intervalDistance = PreferencesUtils.getVoiceAnnouncementDistance();
|
||||
intervalStatistics = new IntervalStatistics(intervalDistance);
|
||||
intervalStatistics = new IntervalStatisticsUpdater(intervalDistance);
|
||||
}
|
||||
|
||||
public void start(@Nullable TrackStatistics trackStatistics) {
|
||||
public void start(@Nullable Statistics trackStatistics) {
|
||||
voiceAnnouncement = new TTSManager(context);
|
||||
voiceAnnouncement.start();
|
||||
update(trackStatistics);
|
||||
}
|
||||
|
||||
void update(@Nullable TrackStatistics trackStatistics) {
|
||||
void update(@Nullable Statistics trackStatistics) {
|
||||
this.trackStatistics = trackStatistics;
|
||||
updateNextDuration();
|
||||
updateNextTaskDistance();
|
||||
@@ -120,44 +119,46 @@ public class VoiceAnnouncementManager implements SharedPreferences.OnSharedPrefe
|
||||
voiceAnnouncement.speak(VoiceAnnouncementUtils.createIdle(context));
|
||||
}
|
||||
|
||||
public void announceStatisticsIfNeeded(@NonNull Track track, @NonNull SensorDataSet sensorDataSet) {
|
||||
public void announceStatisticsIfNeeded(@NonNull RecordingData data) {
|
||||
if (shouldNotAnnounce()) {
|
||||
return;
|
||||
}
|
||||
|
||||
boolean announce = false;
|
||||
this.trackStatistics = track.getTrackStatistics();
|
||||
if (trackStatistics.getTotalDistance().greaterThan(nextTotalDistance)) {
|
||||
this.trackStatistics = data.track().getStatistics();
|
||||
if (trackStatistics.totalDistance().greaterThan(nextTotalDistance)) {
|
||||
updateNextTaskDistance();
|
||||
announce = true;
|
||||
}
|
||||
if (!trackStatistics.getTotalTime().minus(nextTotalTime).isNegative()) {
|
||||
if (!trackStatistics.totalTime().minus(nextTotalTime).isNegative()) {
|
||||
updateNextDuration();
|
||||
announce = true;
|
||||
}
|
||||
|
||||
if (announce) {
|
||||
voiceAnnouncement.speak(createAnnouncement(track, sensorDataSet));
|
||||
voiceAnnouncement.speak(createAnnouncement(data));
|
||||
}
|
||||
}
|
||||
|
||||
private Spannable createAnnouncement(Track track, SensorDataSet sensorDataSet) {
|
||||
private Spannable createAnnouncement(RecordingData data) {
|
||||
Distance currentIntervalDistance = PreferencesUtils.getVoiceAnnouncementDistance();
|
||||
if (currentIntervalDistance != intervalDistance) {
|
||||
intervalStatistics = new IntervalStatistics(currentIntervalDistance);
|
||||
intervalStatistics = new IntervalStatisticsUpdater(currentIntervalDistance);
|
||||
intervalDistance = currentIntervalDistance;
|
||||
startTrackPointId = null;
|
||||
}
|
||||
|
||||
TrackPointIterator trackPointIterator = new TrackPointIterator(contentProviderUtils, track.getId(), startTrackPointId);
|
||||
startTrackPointId = intervalStatistics.addTrackPoints(trackPointIterator);
|
||||
IntervalStatistics.Interval lastInterval = intervalStatistics.getLastInterval();
|
||||
SensorStatistics sensorStatistics = null;
|
||||
if (track.getId() != null) {
|
||||
sensorStatistics = contentProviderUtils.getSensorStats(track.getId());
|
||||
}
|
||||
//TODO Avoid reloading (some) TrackPoints just to announce the last interval.
|
||||
try (AutoCloseableIterator<TrackPoint> trackPointIterator = contentProviderUtils.getTrackPointLocationIterator(data.track().getId(), startTrackPointId)) {
|
||||
startTrackPointId = intervalStatistics.addTrackPoints(trackPointIterator);
|
||||
Statistics lastInterval = intervalStatistics.getLastInterval();
|
||||
SensorStatistics sensorStatistics = null;
|
||||
if (data.track().getId() != null) {
|
||||
sensorStatistics = contentProviderUtils.getSensorStats(data.track().getId());
|
||||
}
|
||||
|
||||
return VoiceAnnouncementUtils.createStatistics(context, track, sensorDataSet, PreferencesUtils.getUnitSystem(), PreferencesUtils.isReportSpeed(track), lastInterval, sensorStatistics);
|
||||
return VoiceAnnouncementUtils.createStatistics(context, data.track(), data.sensorDataSet(), PreferencesUtils.getUnitSystem(), PreferencesUtils.isReportSpeed(data.track()), lastInterval, sensorStatistics);
|
||||
}
|
||||
}
|
||||
|
||||
public void stop() {
|
||||
@@ -182,7 +183,7 @@ public class VoiceAnnouncementManager implements SharedPreferences.OnSharedPrefe
|
||||
nextTotalDistance = DISTANCE_OFF;
|
||||
} else {
|
||||
|
||||
Distance distance = trackStatistics.getTotalDistance();
|
||||
Distance distance = trackStatistics.totalDistance();
|
||||
|
||||
int index = (int) (distance.dividedBy(distanceFrequency));
|
||||
nextTotalDistance = distanceFrequency.multipliedBy(index + 1);
|
||||
@@ -195,10 +196,9 @@ public class VoiceAnnouncementManager implements SharedPreferences.OnSharedPrefe
|
||||
nextTotalTime = TOTALTIME_OFF;
|
||||
} else {
|
||||
|
||||
Duration totalTime = trackStatistics.getTotalTime();
|
||||
Duration intervalMod = Duration.ofMillis(trackStatistics.getTotalTime().toMillis() % totalTimeFrequency.toMillis());
|
||||
Duration intervalMod = Duration.ofMillis(trackStatistics.totalTime().toMillis() % totalTimeFrequency.toMillis());
|
||||
|
||||
nextTotalTime = totalTime.plus(totalTimeFrequency.minus(intervalMod));
|
||||
nextTotalTime = trackStatistics.totalTime().plus(totalTimeFrequency.minus(intervalMod));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+10
-11
@@ -33,12 +33,11 @@ import de.dennisguse.opentracks.R;
|
||||
import de.dennisguse.opentracks.data.models.Distance;
|
||||
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.sensors.sensorData.SensorDataSet;
|
||||
import de.dennisguse.opentracks.settings.UnitSystem;
|
||||
import de.dennisguse.opentracks.stats.SensorStatistics;
|
||||
import de.dennisguse.opentracks.stats.TrackStatistics;
|
||||
import de.dennisguse.opentracks.ui.intervals.IntervalStatistics;
|
||||
import de.dennisguse.opentracks.util.StringUtils;
|
||||
|
||||
class VoiceAnnouncementUtils {
|
||||
@@ -51,8 +50,8 @@ class VoiceAnnouncementUtils {
|
||||
.append(context.getString(R.string.voiceIdle));
|
||||
}
|
||||
|
||||
static Spannable createStatistics(Context context, Track track, SensorDataSet sensorDataSet, UnitSystem unitSystem, boolean isReportSpeed, @Nullable IntervalStatistics.Interval currentInterval, @Nullable SensorStatistics sensorStatistics) {
|
||||
TrackStatistics trackStatistics = track.getTrackStatistics();
|
||||
static Spannable createStatistics(Context context, Track track, SensorDataSet sensorDataSet, UnitSystem unitSystem, boolean isReportSpeed, @Nullable Statistics currentInterval, @Nullable SensorStatistics sensorStatistics) {
|
||||
Statistics trackStatistics = track.getStatistics();
|
||||
|
||||
SpannableStringBuilder builder = new SpannableStringBuilder();
|
||||
|
||||
@@ -60,9 +59,9 @@ class VoiceAnnouncementUtils {
|
||||
appendTime(builder, track.getStopTime());
|
||||
}
|
||||
|
||||
Distance totalDistance = trackStatistics.getTotalDistance();
|
||||
Distance totalDistance = trackStatistics.totalDistance();
|
||||
Speed averageMovingSpeed = trackStatistics.getAverageMovingSpeed();
|
||||
Speed currentDistancePerTime = currentInterval != null ? currentInterval.getSpeed() : null;
|
||||
Speed currentDistancePerTime = currentInterval != null ? currentInterval.getAverageSpeed() : null;
|
||||
|
||||
int perUnitStringId = R.string.empty;
|
||||
int distanceId = R.string.voiceDistance;
|
||||
@@ -112,7 +111,7 @@ class VoiceAnnouncementUtils {
|
||||
boolean hasTravelledDistance = !totalDistance.isZero();
|
||||
|
||||
// Announce time
|
||||
Duration movingTime = trackStatistics.getMovingTime();
|
||||
Duration movingTime = trackStatistics.movingTime();
|
||||
if (shouldVoiceAnnounceMovingTime() && hasTravelledDistance && !movingTime.isZero()) {
|
||||
appendDuration(context, builder, movingTime);
|
||||
builder.append(".");
|
||||
@@ -179,8 +178,8 @@ class VoiceAnnouncementUtils {
|
||||
appendCardinal(builder, context.getString(R.string.sensor_state_heart_rate_value, averageHeartRate), averageHeartRate);
|
||||
builder.append(".");
|
||||
}
|
||||
if (shouldVoiceAnnounceLapHeartRate() && currentInterval != null && currentInterval.hasAverageHeartRate()) {
|
||||
int currentHeartRate = Math.round(currentInterval.getAverageHeartRate().getBPM());
|
||||
if (shouldVoiceAnnounceLapHeartRate() && currentInterval != null && currentInterval.avgHeartRate() != null) {
|
||||
int currentHeartRate = Math.round(currentInterval.avgHeartRate().getBPM());
|
||||
|
||||
builder.append(" ")
|
||||
.append(context.getString(R.string.lap_heart_rate));
|
||||
@@ -188,8 +187,8 @@ class VoiceAnnouncementUtils {
|
||||
builder.append(".");
|
||||
}
|
||||
|
||||
if (shouldVoiceAnnounceLapPower() && currentInterval != null && currentInterval.hasAveragePower()) {
|
||||
int currentPower = Math.round(currentInterval.getAveragePower().getW());
|
||||
if (shouldVoiceAnnounceLapPower() && currentInterval != null && currentInterval.avgPower() != null) {
|
||||
int currentPower = Math.round(currentInterval.avgPower().getW());
|
||||
if (shouldVoiceAnnounceUnit()) {
|
||||
String template = context.getResources().getString(R.string.power_x_watt);
|
||||
builder.append(" ")
|
||||
|
||||
@@ -47,7 +47,7 @@ public class TrackPointCreator implements SharedPreferences.OnSharedPreferenceCh
|
||||
sensorManager.start(context, handler);
|
||||
}
|
||||
|
||||
private boolean isStarted() {
|
||||
public boolean isStarted() {
|
||||
return context != null;
|
||||
}
|
||||
|
||||
|
||||
@@ -829,7 +829,8 @@ public class PreferencesUtils {
|
||||
R.array.stats_custom_layout_fields_default_value_16,
|
||||
R.array.stats_custom_layout_fields_default_value_17,
|
||||
R.array.stats_custom_layout_fields_default_value_18,
|
||||
R.array.stats_custom_layout_fields_default_value_19
|
||||
R.array.stats_custom_layout_fields_default_value_19,
|
||||
R.array.stats_custom_layout_fields_default_value_20
|
||||
).map(id -> resources.obtainTypedArray(id))
|
||||
.toList();
|
||||
}
|
||||
|
||||
@@ -4,6 +4,7 @@ import android.os.Bundle;
|
||||
import android.util.Log;
|
||||
import android.widget.Toast;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.fragment.app.DialogFragment;
|
||||
import androidx.fragment.app.FragmentActivity;
|
||||
import androidx.preference.EditTextPreference;
|
||||
@@ -71,7 +72,7 @@ public class SensorsSettingsFragment extends PreferenceFragmentCompat {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onDisplayPreferenceDialog(Preference preference) {
|
||||
public void onDisplayPreferenceDialog(@NonNull Preference preference) {
|
||||
if (preference instanceof BluetoothLeSensorPreference) {
|
||||
if(!PermissionRequester.BLUETOOTH.hasPermission(preference.getContext())) {
|
||||
// TODO: maybe use requestPermissionsIfNeeded here or disable preferences at all
|
||||
@@ -81,6 +82,8 @@ public class SensorsSettingsFragment extends PreferenceFragmentCompat {
|
||||
}
|
||||
|
||||
DialogFragment dialogFragment = ((BluetoothLeSensorPreference) preference).createInstance();
|
||||
|
||||
// Deprecation not fixed upstream: https://issuetracker.google.com/issues/181793702
|
||||
dialogFragment.setTargetFragment(this, 0);
|
||||
dialogFragment.show(getParentFragmentManager(), getClass().getSimpleName());
|
||||
return;
|
||||
|
||||
@@ -5,6 +5,7 @@ import android.content.SharedPreferences;
|
||||
import android.os.Bundle;
|
||||
import android.widget.Toast;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.fragment.app.DialogFragment;
|
||||
import androidx.preference.Preference;
|
||||
import androidx.preference.PreferenceFragmentCompat;
|
||||
@@ -53,7 +54,7 @@ public class UserInterfaceSettingsFragment extends PreferenceFragmentCompat {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onDisplayPreferenceDialog(Preference preference) {
|
||||
public void onDisplayPreferenceDialog(@NonNull Preference preference) {
|
||||
DialogFragment dialogFragment = null;
|
||||
|
||||
if (preference instanceof ResetDialogPreference) {
|
||||
|
||||
@@ -29,9 +29,9 @@ import de.dennisguse.opentracks.R;
|
||||
import de.dennisguse.opentracks.data.models.Distance;
|
||||
import de.dennisguse.opentracks.data.models.Speed;
|
||||
import de.dennisguse.opentracks.data.models.SpeedFormatter;
|
||||
import de.dennisguse.opentracks.data.models.Statistics;
|
||||
import de.dennisguse.opentracks.data.models.Track;
|
||||
import de.dennisguse.opentracks.settings.UnitSystem;
|
||||
import de.dennisguse.opentracks.stats.TrackStatistics;
|
||||
import de.dennisguse.opentracks.util.StringUtils;
|
||||
|
||||
/**
|
||||
@@ -65,7 +65,7 @@ public class TrackShareSummaryGenerator {
|
||||
writeString(track.getName(), builder, R.string.generic_name_line);
|
||||
writeString(track.getActivityTypeLocalized(), builder, R.string.description_activity_type);
|
||||
writeString(track.getDescription(), builder, R.string.generic_description_line);
|
||||
builder.append(generateTrackStatisticsDescription(track.getTrackStatistics()));
|
||||
builder.append(generateTrackStatisticsDescription(track.getStatistics()));
|
||||
|
||||
return builder.toString();
|
||||
}
|
||||
@@ -78,17 +78,17 @@ public class TrackShareSummaryGenerator {
|
||||
builder.append(TrackShareSummaryGenerator.TEXT_LINE_BREAK);
|
||||
}
|
||||
|
||||
private String generateTrackStatisticsDescription(TrackStatistics stats) {
|
||||
private String generateTrackStatisticsDescription(Statistics stats) {
|
||||
StringBuilder builder = new StringBuilder();
|
||||
|
||||
// Total distance
|
||||
writeDistance(stats.getTotalDistance(), builder, R.string.description_total_distance);
|
||||
writeDistance(stats.totalDistance(), builder, R.string.description_total_distance);
|
||||
|
||||
// Total time
|
||||
writeTime(stats.getTotalTime(), builder, R.string.description_total_time);
|
||||
writeTime(stats.totalTime(), builder, R.string.description_total_time);
|
||||
|
||||
// Moving time
|
||||
writeTime(stats.getMovingTime(), builder, R.string.description_moving_time);
|
||||
writeTime(stats.movingTime(), builder, R.string.description_moving_time);
|
||||
|
||||
// Average speed
|
||||
writeSpeed(stats.getAverageSpeed(), builder, R.string.description_average_speed);
|
||||
@@ -97,7 +97,7 @@ public class TrackShareSummaryGenerator {
|
||||
writeSpeed(stats.getAverageMovingSpeed(), builder, R.string.description_average_moving_speed);
|
||||
|
||||
// Max speed
|
||||
writeSpeed(stats.getMaxSpeed(), builder, R.string.description_max_speed);
|
||||
writeSpeed(stats.maxSpeed(), builder, R.string.description_max_speed);
|
||||
|
||||
// Average pace
|
||||
writePace(stats.getAverageSpeed(), builder, R.string.description_average_pace_in_minute);
|
||||
@@ -106,26 +106,22 @@ public class TrackShareSummaryGenerator {
|
||||
writePace(stats.getAverageMovingSpeed(), builder, R.string.description_average_moving_pace_in_minute);
|
||||
|
||||
// Fastest pace
|
||||
writePace(stats.getMaxSpeed(), builder, R.string.description_fastest_pace_in_minute);
|
||||
writePace(stats.maxSpeed(), builder, R.string.description_fastest_pace_in_minute);
|
||||
|
||||
// Min and Max altitude
|
||||
if (stats.hasAltitudeExtremities()) {
|
||||
writeAltitude(stats.getMaxAltitude(), builder, R.string.description_max_altitude);
|
||||
writeAltitude(stats.getMinAltitude(), builder, R.string.description_min_altitude);
|
||||
if (stats.altitudeExtremities() != null) {
|
||||
writeAltitude(stats.altitudeExtremities().max_m(), builder, R.string.description_max_altitude);
|
||||
writeAltitude(stats.altitudeExtremities().min_m(), builder, R.string.description_min_altitude);
|
||||
}
|
||||
|
||||
// Altitude gain
|
||||
if (stats.hasTotalAltitudeGain()) {
|
||||
writeAltitude(stats.getTotalAltitudeGain(), builder, R.string.description_altitude_gain);
|
||||
}
|
||||
|
||||
// Altitude loss
|
||||
if (stats.hasTotalAltitudeLoss()) {
|
||||
writeAltitude(stats.getTotalAltitudeLoss(), builder, R.string.description_altitude_loss);
|
||||
// Altitude gain & loss
|
||||
if (stats.altitudeGainLoss() != null) {
|
||||
writeAltitude(stats.altitudeGainLoss().gain_m(), builder, R.string.description_altitude_gain);
|
||||
writeAltitude(stats.altitudeGainLoss().loss_m(), builder, R.string.description_altitude_loss);
|
||||
}
|
||||
|
||||
// Recorded time
|
||||
builder.append(context.getString(R.string.description_recorded_time, StringUtils.formatDateTimeWithOffset(OffsetDateTime.ofInstant(stats.getStartTime(), ZoneId.systemDefault()))));
|
||||
builder.append(context.getString(R.string.description_recorded_time, StringUtils.formatDateTimeWithOffset(OffsetDateTime.ofInstant(stats.startTime(), ZoneId.systemDefault()))));
|
||||
builder.append(TEXT_LINE_BREAK);
|
||||
|
||||
return builder.toString();
|
||||
|
||||
@@ -100,14 +100,15 @@ public class ExtremityMonitor {
|
||||
* @param min the minimum value
|
||||
* @param max the maximum value
|
||||
*/
|
||||
public void set(double min, double max) {
|
||||
public void set(Double min, Double max) {
|
||||
if (min == null || max == null) {
|
||||
reset();
|
||||
return;
|
||||
}
|
||||
this.min = min;
|
||||
this.max = max;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true if has data.
|
||||
*/
|
||||
public boolean hasData() {
|
||||
return min != Double.POSITIVE_INFINITY && max != Double.NEGATIVE_INFINITY;
|
||||
}
|
||||
|
||||
+67
-91
@@ -24,10 +24,13 @@ import java.time.Duration;
|
||||
import java.time.Instant;
|
||||
|
||||
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.Distance;
|
||||
import de.dennisguse.opentracks.data.models.HeartRate;
|
||||
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.TrackPoint;
|
||||
|
||||
@@ -38,8 +41,7 @@ import de.dennisguse.opentracks.data.models.TrackPoint;
|
||||
* @author Rodrigo Damazio
|
||||
*/
|
||||
//TODO Check that data ranges are valid (not less than zero etc.)
|
||||
//TODO Should be a Java record
|
||||
public class TrackStatistics {
|
||||
public class SegmentStatisticUpdater {
|
||||
|
||||
// The min and max altitude (meters) seen on this track.
|
||||
private final ExtremityMonitor altitudeExtremities = new ExtremityMonitor();
|
||||
@@ -50,7 +52,11 @@ public class TrackStatistics {
|
||||
private Instant stopTime; //TODO Should never be null!
|
||||
|
||||
private Distance totalDistance;
|
||||
// Updated when new points are received, may be stale.
|
||||
/**
|
||||
* Updated when new points are received, may be stale.
|
||||
* This statistic is only updated when a new point is added to the statistics, so it may be off.
|
||||
* If you need to calculate the proper totalTime, use startTime with the current time.
|
||||
*/
|
||||
private Duration totalTime;
|
||||
// Based on when we believe the user is traveling.
|
||||
private Duration movingTime;
|
||||
@@ -64,7 +70,7 @@ public class TrackStatistics {
|
||||
|
||||
private boolean isIdle;
|
||||
|
||||
public TrackStatistics() {
|
||||
public SegmentStatisticUpdater() {
|
||||
reset();
|
||||
}
|
||||
|
||||
@@ -73,31 +79,27 @@ public class TrackStatistics {
|
||||
*
|
||||
* @param other another statistics data object to copy from
|
||||
*/
|
||||
public TrackStatistics(TrackStatistics other) {
|
||||
startTime = other.startTime;
|
||||
stopTime = other.stopTime;
|
||||
totalDistance = other.totalDistance;
|
||||
totalTime = other.totalTime;
|
||||
movingTime = other.movingTime;
|
||||
maxSpeed = other.maxSpeed;
|
||||
altitudeExtremities.set(other.altitudeExtremities.getMin(), other.altitudeExtremities.getMax());
|
||||
totalAltitudeGain_m = other.totalAltitudeGain_m;
|
||||
totalAltitudeLoss_m = other.totalAltitudeLoss_m;
|
||||
avgHeartRate = other.avgHeartRate;
|
||||
avgPower = other.avgPower;
|
||||
isIdle = other.isIdle;
|
||||
public SegmentStatisticUpdater(SegmentStatisticUpdater other) {
|
||||
this(other.getStatistics());
|
||||
this.isIdle = other.isIdle();
|
||||
}
|
||||
|
||||
@VisibleForTesting
|
||||
public TrackStatistics(String startTime, String stopTime, double totalDistance_m, int totalTime_s, int movingTime_s, float maxSpeed_mps, Float totalAltitudeGain_m, Float totalAltitudeLoss_m) {
|
||||
this.startTime = Instant.parse(startTime);
|
||||
this.stopTime = Instant.parse(stopTime);
|
||||
this.totalDistance = Distance.of(totalDistance_m);
|
||||
this.totalTime = Duration.ofSeconds(totalTime_s);
|
||||
this.movingTime = Duration.ofSeconds(movingTime_s);
|
||||
this.maxSpeed = Speed.of(maxSpeed_mps);
|
||||
this.totalAltitudeGain_m = totalAltitudeGain_m;
|
||||
this.totalAltitudeLoss_m = totalAltitudeLoss_m;
|
||||
public SegmentStatisticUpdater(Statistics statistics) {
|
||||
startTime = statistics.startTime();
|
||||
stopTime = statistics.stopTime();
|
||||
totalDistance = statistics.totalDistance();
|
||||
totalTime = statistics.totalTime();
|
||||
movingTime = statistics.movingTime();
|
||||
maxSpeed = statistics.maxSpeed();
|
||||
if (statistics.altitudeExtremities() != null) {
|
||||
altitudeExtremities.set(statistics.altitudeExtremities().min_m(), statistics.altitudeExtremities().max_m());
|
||||
}
|
||||
if (statistics.altitudeGainLoss() != null) {
|
||||
totalAltitudeGain_m = statistics.altitudeGainLoss().gain_m();
|
||||
totalAltitudeLoss_m = statistics.altitudeGainLoss().loss_m();
|
||||
}
|
||||
avgHeartRate = statistics.avgHeartRate();
|
||||
avgPower = statistics.avgPower();
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -107,7 +109,7 @@ public class TrackStatistics {
|
||||
* @param other another statistics data object
|
||||
*/
|
||||
//TODO Should be refactored to append only [mainly due to isIdle] (NOTE: This requires to use a custom value object for AggregatedStatistics; this is anyhow recommended).
|
||||
public void merge(TrackStatistics other) {
|
||||
public void merge(SegmentStatisticUpdater other) {
|
||||
if (startTime == null) {
|
||||
startTime = other.startTime;
|
||||
} else {
|
||||
@@ -183,12 +185,12 @@ public class TrackStatistics {
|
||||
startTime = null;
|
||||
stopTime = null;
|
||||
|
||||
setTotalDistance(Distance.of(0));
|
||||
setTotalTime(Duration.ofSeconds(0));
|
||||
setMovingTime(Duration.ofSeconds(0));
|
||||
setMaxSpeed(Speed.zero());
|
||||
setTotalAltitudeGain(null);
|
||||
setTotalAltitudeLoss(null);
|
||||
totalTime = Duration.ofSeconds(0);
|
||||
movingTime = Duration.ofSeconds(0);
|
||||
totalDistance = Distance.of(0);
|
||||
maxSpeed = Speed.zero();
|
||||
totalAltitudeGain_m = null;
|
||||
totalAltitudeLoss_m = null;
|
||||
|
||||
isIdle = false;
|
||||
}
|
||||
@@ -198,6 +200,22 @@ public class TrackStatistics {
|
||||
setStartTime(startTime);
|
||||
}
|
||||
|
||||
public Statistics getStatistics() {
|
||||
return new Statistics(
|
||||
startTime,
|
||||
stopTime,
|
||||
totalTime,
|
||||
movingTime,
|
||||
|
||||
totalDistance,
|
||||
getMaxSpeed(),
|
||||
altitudeExtremities.hasData() ? new AltitudeExtremities(altitudeExtremities.getMin(), altitudeExtremities.getMax()) : null,
|
||||
totalAltitudeGain_m != null && totalAltitudeLoss_m != null ? new AltitudeGainLoss(totalAltitudeGain_m, totalAltitudeLoss_m) : null,
|
||||
avgHeartRate,
|
||||
avgPower
|
||||
);
|
||||
}
|
||||
|
||||
public Instant getStartTime() {
|
||||
return startTime;
|
||||
}
|
||||
@@ -226,6 +244,7 @@ public class TrackStatistics {
|
||||
return totalDistance;
|
||||
}
|
||||
|
||||
@VisibleForTesting
|
||||
public void setTotalDistance(Distance totalDistance_m) {
|
||||
this.totalDistance = totalDistance_m;
|
||||
}
|
||||
@@ -234,11 +253,6 @@ public class TrackStatistics {
|
||||
totalDistance = totalDistance.plus(distance_m);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the total time in milliseconds that this track has been active.
|
||||
* This statistic is only updated when a new point is added to the statistics, so it may be off.
|
||||
* If you need to calculate the proper total time, use {@link #getStartTime} with the current time.
|
||||
*/
|
||||
public Duration getTotalTime() {
|
||||
return totalTime;
|
||||
}
|
||||
@@ -267,10 +281,6 @@ public class TrackStatistics {
|
||||
movingTime = movingTime.plus(time);
|
||||
}
|
||||
|
||||
public Duration getStoppedTime() {
|
||||
return totalTime.minus(movingTime);
|
||||
}
|
||||
|
||||
public boolean isIdle() {
|
||||
return isIdle;
|
||||
}
|
||||
@@ -279,19 +289,11 @@ public class TrackStatistics {
|
||||
isIdle = idle;
|
||||
}
|
||||
|
||||
public boolean hasAverageHeartRate() {
|
||||
return avgHeartRate != null;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public HeartRate getAverageHeartRate() {
|
||||
return avgHeartRate;
|
||||
}
|
||||
|
||||
public boolean hasPower() {
|
||||
return avgPower != null;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public Power getAveragePower() {
|
||||
return avgPower;
|
||||
@@ -320,10 +322,6 @@ public class TrackStatistics {
|
||||
this.maxSpeed = maxSpeed;
|
||||
}
|
||||
|
||||
public boolean hasAltitudeExtremities() {
|
||||
return altitudeExtremities.hasData();
|
||||
}
|
||||
|
||||
public double getMinAltitude() {
|
||||
return altitudeExtremities.getMin();
|
||||
}
|
||||
@@ -362,64 +360,42 @@ public class TrackStatistics {
|
||||
}
|
||||
}
|
||||
|
||||
public boolean hasTotalAltitudeGain() {
|
||||
return totalAltitudeGain_m != null;
|
||||
}
|
||||
|
||||
@VisibleForTesting
|
||||
@Deprecated
|
||||
@Nullable
|
||||
public Float getTotalAltitudeGain() {
|
||||
return totalAltitudeGain_m;
|
||||
}
|
||||
|
||||
@VisibleForTesting
|
||||
@Deprecated
|
||||
public void setTotalAltitudeGain(Float totalAltitudeGain_m) {
|
||||
this.totalAltitudeGain_m = totalAltitudeGain_m;
|
||||
}
|
||||
|
||||
@VisibleForTesting(otherwise = VisibleForTesting.PACKAGE_PRIVATE)
|
||||
public void addTotalAltitudeGain(float gain_m) {
|
||||
if (totalAltitudeGain_m == null) {
|
||||
totalAltitudeGain_m = 0f;
|
||||
}
|
||||
totalAltitudeGain_m += gain_m;
|
||||
}
|
||||
|
||||
public boolean hasTotalAltitudeLoss() {
|
||||
return totalAltitudeLoss_m != null;
|
||||
}
|
||||
|
||||
@VisibleForTesting
|
||||
@Deprecated
|
||||
@Nullable
|
||||
public Float getTotalAltitudeLoss() {
|
||||
return totalAltitudeLoss_m;
|
||||
}
|
||||
|
||||
public void setTotalAltitudeLoss(Float totalAltitudeLoss_m) {
|
||||
this.totalAltitudeLoss_m = totalAltitudeLoss_m;
|
||||
}
|
||||
|
||||
@VisibleForTesting(otherwise = VisibleForTesting.PACKAGE_PRIVATE)
|
||||
public void addTotalAltitudeLoss(float loss_m) {
|
||||
public void addTotalAltitudeGainLoss(AltitudeGainLoss altitudeGainLoss) {
|
||||
if (totalAltitudeGain_m == null) {
|
||||
totalAltitudeGain_m = 0f;
|
||||
}
|
||||
totalAltitudeGain_m += altitudeGainLoss.gain_m();
|
||||
|
||||
if (totalAltitudeLoss_m == null) {
|
||||
totalAltitudeLoss_m = 0f;
|
||||
}
|
||||
totalAltitudeLoss_m += loss_m;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object o) {
|
||||
if (this == o) return true;
|
||||
if (!(o instanceof TrackStatistics)) return false;
|
||||
|
||||
return toString().equals(o.toString());
|
||||
totalAltitudeLoss_m += altitudeGainLoss.loss_m();
|
||||
}
|
||||
|
||||
@NonNull
|
||||
@Override
|
||||
public String toString() {
|
||||
return "TrackStatistics { Start Time: " + getStartTime() + "; Stop Time: " + getStopTime()
|
||||
+ "; Total Distance: " + getTotalDistance() + "; Total Time: " + getTotalTime()
|
||||
+ "; Moving Time: " + getMovingTime() + "; Max Speed: " + getMaxSpeed()
|
||||
+ "; Min Altitude: " + getMinAltitude() + "; Max Altitude: " + getMaxAltitude()
|
||||
+ "; Altitude Gain: " + getTotalAltitudeGain()
|
||||
+ "; Altitude Loss: " + getTotalAltitudeLoss() + "}";
|
||||
return "SegmentStatisticUpdater{" + getStatistics() + "}";
|
||||
}
|
||||
}
|
||||
@@ -17,6 +17,7 @@
|
||||
package de.dennisguse.opentracks.stats;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.annotation.VisibleForTesting;
|
||||
|
||||
import java.time.Duration;
|
||||
import java.util.List;
|
||||
@@ -25,12 +26,13 @@ import de.dennisguse.opentracks.data.models.Distance;
|
||||
import de.dennisguse.opentracks.data.models.HeartRate;
|
||||
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.TrackPoint;
|
||||
import de.dennisguse.opentracks.settings.PreferencesUtils;
|
||||
|
||||
/**
|
||||
* Updater for {@link TrackStatistics}.
|
||||
* For updating track {@link TrackStatistics} as new {@link TrackPoint}s are added.
|
||||
* Updater for {@link SegmentStatisticUpdater}.
|
||||
* For updating track {@link SegmentStatisticUpdater} as new {@link TrackPoint}s are added.
|
||||
* NOTE: Some of the locations represent pause/resume separator.
|
||||
* NOTE: Has still support for segments (at the moment unused).
|
||||
*
|
||||
@@ -41,7 +43,7 @@ public class TrackStatisticsUpdater {
|
||||
|
||||
private static final String TAG = TrackStatisticsUpdater.class.getSimpleName();
|
||||
|
||||
private final TrackStatistics trackStatistics;
|
||||
private final SegmentStatisticUpdater segmentStatisticUpdater;
|
||||
|
||||
private float averageHeartRateBPM;
|
||||
private Duration totalHeartRateDuration = Duration.ZERO;
|
||||
@@ -49,38 +51,49 @@ public class TrackStatisticsUpdater {
|
||||
private Duration totalPowerDuration = Duration.ZERO;
|
||||
|
||||
// The current segment's statistics
|
||||
private final TrackStatistics currentSegment;
|
||||
private final SegmentStatisticUpdater currentSegment;
|
||||
// Current segment's last trackPoint
|
||||
private TrackPoint lastTrackPoint;
|
||||
|
||||
public TrackStatisticsUpdater() {
|
||||
this(new TrackStatistics());
|
||||
this(new SegmentStatisticUpdater());
|
||||
}
|
||||
|
||||
public TrackStatisticsUpdater(TrackStatistics trackStatistics) {
|
||||
this.trackStatistics = trackStatistics;
|
||||
this.currentSegment = new TrackStatistics();
|
||||
public TrackStatisticsUpdater(Statistics statistics) {
|
||||
this(new SegmentStatisticUpdater(statistics));
|
||||
}
|
||||
|
||||
@Deprecated
|
||||
private TrackStatisticsUpdater(SegmentStatisticUpdater segmentStatisticUpdater) {
|
||||
this.segmentStatisticUpdater = segmentStatisticUpdater;
|
||||
this.currentSegment = new SegmentStatisticUpdater();
|
||||
|
||||
resetAverageHeartRate();
|
||||
}
|
||||
|
||||
public TrackStatisticsUpdater(TrackStatisticsUpdater toCopy) {
|
||||
this.currentSegment = new TrackStatistics(toCopy.currentSegment);
|
||||
this.trackStatistics = new TrackStatistics(toCopy.trackStatistics);
|
||||
this.currentSegment = new SegmentStatisticUpdater(toCopy.currentSegment);
|
||||
this.segmentStatisticUpdater = new SegmentStatisticUpdater(toCopy.segmentStatisticUpdater);
|
||||
|
||||
this.lastTrackPoint = toCopy.lastTrackPoint;
|
||||
resetAverageHeartRate();
|
||||
}
|
||||
|
||||
public TrackStatistics getTrackStatistics() {
|
||||
@Deprecated //TODO Remove
|
||||
@VisibleForTesting
|
||||
public SegmentStatisticUpdater getSegmentStatisticUpdater() {
|
||||
// Take a snapshot - we don't want anyone messing with our trackStatistics
|
||||
TrackStatistics stats = new TrackStatistics(trackStatistics);
|
||||
SegmentStatisticUpdater stats = new SegmentStatisticUpdater(segmentStatisticUpdater);
|
||||
stats.merge(currentSegment);
|
||||
return stats;
|
||||
}
|
||||
public TrackStatistics getCurrentSegment() {
|
||||
// Take a snapshot - we don't want anyone messing with our currentSegment
|
||||
return new TrackStatistics(currentSegment);
|
||||
|
||||
public Statistics getTrackStatistics() {
|
||||
return getSegmentStatisticUpdater().getStatistics();
|
||||
}
|
||||
|
||||
public Statistics getCurrentSegment() {
|
||||
return currentSegment.getStatistics();
|
||||
}
|
||||
|
||||
public void addTrackPoints(List<TrackPoint> trackPoints) {
|
||||
@@ -101,12 +114,8 @@ public class TrackStatisticsUpdater {
|
||||
currentSegment.setTotalTime(Duration.between(currentSegment.getStartTime(), trackPoint.getTime()));
|
||||
|
||||
// Process sensor data: barometer
|
||||
if (trackPoint.hasAltitudeGain()) {
|
||||
currentSegment.addTotalAltitudeGain(trackPoint.getAltitudeGain());
|
||||
}
|
||||
|
||||
if (trackPoint.hasAltitudeLoss()) {
|
||||
currentSegment.addTotalAltitudeLoss(trackPoint.getAltitudeLoss());
|
||||
if (trackPoint.hasAltitudeGainLoss()) {
|
||||
currentSegment.addTotalAltitudeGainLoss(trackPoint.getAltitudeGainLoss());
|
||||
}
|
||||
|
||||
//Update absolute (GPS-based) altitude
|
||||
@@ -182,7 +191,7 @@ public class TrackStatisticsUpdater {
|
||||
|
||||
private void reset(TrackPoint trackPoint) {
|
||||
if (currentSegment.isInitialized()) {
|
||||
trackStatistics.merge(currentSegment);
|
||||
segmentStatisticUpdater.merge(currentSegment);
|
||||
}
|
||||
currentSegment.reset(trackPoint.getTime());
|
||||
|
||||
@@ -209,7 +218,7 @@ public class TrackStatisticsUpdater {
|
||||
@Override
|
||||
public String toString() {
|
||||
return "TrackStatisticsUpdater{" +
|
||||
"trackStatistics=" + trackStatistics +
|
||||
"trackStatistics=" + segmentStatisticUpdater +
|
||||
'}';
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,9 +1,7 @@
|
||||
package de.dennisguse.opentracks.ui;
|
||||
|
||||
import android.app.ActivityOptions;
|
||||
import android.content.Intent;
|
||||
import android.database.Cursor;
|
||||
import android.util.Pair;
|
||||
import android.util.SparseBooleanArray;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.Menu;
|
||||
@@ -269,10 +267,7 @@ public class TrackListAdapter extends RecyclerView.Adapter<RecyclerView.ViewHold
|
||||
// Not recording -> open detail activity.
|
||||
Intent newIntent = IntentUtils.newIntent(context, TrackRecordedActivity.class)
|
||||
.putExtra(TrackRecordedActivity.EXTRA_TRACK_ID, trackId);
|
||||
ActivityOptions activityOptions = ActivityOptions.makeSceneTransitionAnimation(
|
||||
context,
|
||||
new Pair<>(viewBinding.getRoot().findViewById(R.id.track_list_item_icon), TrackRecordedActivity.VIEW_TRACK_ICON));
|
||||
context.startActivity(newIntent, activityOptions.toBundle());
|
||||
context.startActivity(newIntent);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+10
-9
@@ -8,8 +8,9 @@ import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import de.dennisguse.opentracks.data.models.Statistics;
|
||||
import de.dennisguse.opentracks.data.models.Track;
|
||||
import de.dennisguse.opentracks.stats.TrackStatistics;
|
||||
import de.dennisguse.opentracks.stats.SegmentStatisticUpdater;
|
||||
|
||||
public class AggregatedStatistics {
|
||||
|
||||
@@ -35,9 +36,9 @@ public class AggregatedStatistics {
|
||||
public void aggregate(@NonNull Track track) {
|
||||
String activityTypeLocalized = track.getActivityTypeLocalized();
|
||||
if (dataMap.containsKey(activityTypeLocalized)) {
|
||||
dataMap.get(activityTypeLocalized).add(track.getTrackStatistics());
|
||||
dataMap.get(activityTypeLocalized).add(track.getStatistics());
|
||||
} else {
|
||||
dataMap.put(activityTypeLocalized, new AggregatedStatistic(activityTypeLocalized, track.getTrackStatistics()));
|
||||
dataMap.put(activityTypeLocalized, new AggregatedStatistic(activityTypeLocalized, track.getStatistics()));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -55,19 +56,19 @@ public class AggregatedStatistics {
|
||||
|
||||
public static class AggregatedStatistic {
|
||||
private final String activityTypeLocalized;
|
||||
private final TrackStatistics trackStatistics;
|
||||
private final SegmentStatisticUpdater trackStatistics;
|
||||
private int countTracks = 1;
|
||||
|
||||
public AggregatedStatistic(String activityTypeLocalized, TrackStatistics trackStatistics) {
|
||||
public AggregatedStatistic(String activityTypeLocalized, Statistics trackStatistics) {
|
||||
this.activityTypeLocalized = activityTypeLocalized;
|
||||
this.trackStatistics = trackStatistics;
|
||||
this.trackStatistics = new SegmentStatisticUpdater(trackStatistics);
|
||||
}
|
||||
|
||||
public String getActivityTypeLocalized() {
|
||||
return activityTypeLocalized;
|
||||
}
|
||||
|
||||
public TrackStatistics getTrackStatistics() {
|
||||
public SegmentStatisticUpdater getTrackStatistics() {
|
||||
return trackStatistics;
|
||||
}
|
||||
|
||||
@@ -75,8 +76,8 @@ public class AggregatedStatistics {
|
||||
return countTracks;
|
||||
}
|
||||
|
||||
void add(TrackStatistics statistics) {
|
||||
trackStatistics.merge(statistics);
|
||||
void add(Statistics statistics) {
|
||||
trackStatistics.merge(new SegmentStatisticUpdater(statistics));
|
||||
countTracks++;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -11,6 +11,7 @@ import java.util.stream.Collectors;
|
||||
import de.dennisguse.opentracks.R;
|
||||
import de.dennisguse.opentracks.settings.PreferencesUtils;
|
||||
|
||||
//TODO Migrate to DataStore.
|
||||
public class RecordingLayoutIO {
|
||||
|
||||
private static final String TAG = RecordingLayout.class.getSimpleName();
|
||||
|
||||
+17
-11
@@ -19,15 +19,16 @@ import java.util.concurrent.Callable;
|
||||
|
||||
import de.dennisguse.opentracks.R;
|
||||
import de.dennisguse.opentracks.data.models.Altitude;
|
||||
import de.dennisguse.opentracks.data.models.AltitudeGainLoss;
|
||||
import de.dennisguse.opentracks.data.models.Distance;
|
||||
import de.dennisguse.opentracks.data.models.Position;
|
||||
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.databinding.CustomStatsItemBinding;
|
||||
import de.dennisguse.opentracks.services.RecordingData;
|
||||
import de.dennisguse.opentracks.settings.UnitSystem;
|
||||
import de.dennisguse.opentracks.stats.TrackStatistics;
|
||||
import de.dennisguse.opentracks.viewmodels.Mapping;
|
||||
import de.dennisguse.opentracks.viewmodels.StatisticViewHolder;
|
||||
|
||||
@@ -38,17 +39,22 @@ public class SettingsCustomLayoutEditAdapter extends RecyclerView.Adapter<Recycl
|
||||
private static final RecordingData demoData;
|
||||
|
||||
static {
|
||||
TrackStatistics trackStatistics = new TrackStatistics();
|
||||
trackStatistics.setStartTime(Instant.ofEpochMilli(0));
|
||||
trackStatistics.setMovingTime(Duration.ofMinutes(0));
|
||||
trackStatistics.setTotalTime(Duration.ofMinutes(0));
|
||||
Statistics statistics = new Statistics(
|
||||
Instant.ofEpochMilli(0),
|
||||
Instant.ofEpochMilli(0),
|
||||
Duration.ofMinutes(0),
|
||||
Duration.ofMinutes(0),
|
||||
|
||||
trackStatistics.setTotalDistance(Distance.of(0));
|
||||
Distance.of(0),
|
||||
Speed.of(0),
|
||||
null,
|
||||
|
||||
trackStatistics.setTotalAltitudeGain(0f);
|
||||
trackStatistics.setTotalAltitudeLoss(0f);
|
||||
Track track = new Track(ZoneOffset.UTC);
|
||||
track.setTrackStatistics(trackStatistics);
|
||||
new AltitudeGainLoss(0f, 0f),
|
||||
|
||||
null,
|
||||
null
|
||||
);
|
||||
Track track = new Track(ZoneOffset.UTC); //TODO Pass statistics as well
|
||||
|
||||
Position position = new Position(
|
||||
Instant.ofEpochMilli(0),
|
||||
@@ -61,7 +67,7 @@ public class SettingsCustomLayoutEditAdapter extends RecyclerView.Adapter<Recycl
|
||||
Speed.zero()
|
||||
);
|
||||
|
||||
demoData = new RecordingData(track, new TrackPoint(TrackPoint.Type.TRACKPOINT, position), null, trackStatistics) ;
|
||||
demoData = new RecordingData(track, new TrackPoint(TrackPoint.Type.TRACKPOINT, position), null, statistics);
|
||||
}
|
||||
|
||||
private RecordingLayout recordingLayout;
|
||||
|
||||
+12
-17
@@ -7,18 +7,20 @@ import android.view.ViewGroup;
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.recyclerview.widget.RecyclerView;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import de.dennisguse.opentracks.data.models.Distance;
|
||||
import de.dennisguse.opentracks.data.models.DistanceFormatter;
|
||||
import de.dennisguse.opentracks.data.models.SpeedFormatter;
|
||||
import de.dennisguse.opentracks.data.models.Statistics;
|
||||
import de.dennisguse.opentracks.databinding.IntervalStatsListItemBinding;
|
||||
import de.dennisguse.opentracks.settings.UnitSystem;
|
||||
import de.dennisguse.opentracks.util.StringUtils;
|
||||
|
||||
public class IntervalStatisticsAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolder> {
|
||||
|
||||
private List<IntervalStatistics.Interval> intervalList;
|
||||
private List<Statistics> intervalList = new ArrayList<>();
|
||||
private final Context context;
|
||||
private final StackMode stackMode;
|
||||
private UnitSystem unitSystem;
|
||||
@@ -43,46 +45,39 @@ public class IntervalStatisticsAdapter extends RecyclerView.Adapter<RecyclerView
|
||||
int nextPosition = actualPosition + 1;
|
||||
boolean isLast = actualPosition == getItemCount() - 1;
|
||||
IntervalStatisticsAdapter.ViewHolder viewHolder = (IntervalStatisticsAdapter.ViewHolder) holder;
|
||||
IntervalStatistics.Interval interval = intervalList.get(actualPosition);
|
||||
Statistics interval = intervalList.get(actualPosition);
|
||||
viewHolder.itemView.setTag(actualPosition);
|
||||
|
||||
Distance sumDistance;
|
||||
if (isLast && actualPosition > 0) {
|
||||
sumDistance = intervalList.get(actualPosition - 1).getDistance()
|
||||
sumDistance = intervalList.get(actualPosition - 1).totalDistance()
|
||||
.multipliedBy(actualPosition)
|
||||
.plus(interval.getDistance());
|
||||
.plus(interval.totalDistance());
|
||||
} else {
|
||||
sumDistance = interval.getDistance().multipliedBy(nextPosition);
|
||||
sumDistance = interval.totalDistance().multipliedBy(nextPosition);
|
||||
}
|
||||
viewHolder.viewBinding.intervalItemDistance.setText(DistanceFormatter.Builder()
|
||||
.setUnit(unitSystem)
|
||||
.build(context).formatDistance(sumDistance));
|
||||
|
||||
SpeedFormatter formatter = SpeedFormatter.Builder().setUnit(unitSystem).setReportSpeedOrPace(isReportSpeed).build(context);
|
||||
viewHolder.viewBinding.intervalItemRate.setText(formatter.formatSpeed(interval.getSpeed()));
|
||||
viewHolder.viewBinding.intervalItemRate.setText(formatter.formatSpeed(interval.getAverageSpeed()));
|
||||
|
||||
viewHolder.viewBinding.intervalItemGain.setText(StringUtils.formatAltitude(context, interval.getGain_m(), unitSystem));
|
||||
viewHolder.viewBinding.intervalItemLoss.setText(StringUtils.formatAltitude(context, interval.getLoss_m(), unitSystem));
|
||||
viewHolder.viewBinding.intervalItemGain.setText(StringUtils.formatAltitude(context, interval.altitudeGainLoss() != null ? interval.altitudeGainLoss().gain_m() : null, unitSystem));
|
||||
viewHolder.viewBinding.intervalItemLoss.setText(StringUtils.formatAltitude(context, interval.altitudeGainLoss() != null ? interval.altitudeGainLoss().loss_m() : null, unitSystem));
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getItemCount() {
|
||||
if (intervalList == null) {
|
||||
return 0;
|
||||
}
|
||||
return intervalList.size();
|
||||
}
|
||||
|
||||
public List<IntervalStatistics.Interval> swapData(List<IntervalStatistics.Interval> data, UnitSystem unitSystem, boolean isReportSpeed) {
|
||||
public void swapData(@NonNull List<Statistics> data, UnitSystem unitSystem, boolean isReportSpeed) {
|
||||
this.unitSystem = unitSystem;
|
||||
this.isReportSpeed = isReportSpeed;
|
||||
intervalList = data;
|
||||
|
||||
if (data != null) {
|
||||
this.notifyDataSetChanged();
|
||||
}
|
||||
|
||||
return data;
|
||||
this.notifyDataSetChanged();
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -17,12 +17,13 @@ import java.util.concurrent.Executor;
|
||||
import java.util.concurrent.Executors;
|
||||
|
||||
import de.dennisguse.opentracks.data.ContentProviderUtils;
|
||||
import de.dennisguse.opentracks.data.TrackPointIterator;
|
||||
import de.dennisguse.opentracks.data.models.Distance;
|
||||
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.tables.TrackPointsColumns;
|
||||
import de.dennisguse.opentracks.settings.UnitSystem;
|
||||
import de.dennisguse.opentracks.util.AutoCloseableIterator;
|
||||
|
||||
/**
|
||||
* This model is used to load intervals for a track.
|
||||
@@ -32,8 +33,8 @@ public class IntervalStatisticsModel extends AndroidViewModel {
|
||||
|
||||
private static final String TAG = IntervalStatisticsModel.class.getSimpleName();
|
||||
|
||||
private MutableLiveData<List<IntervalStatistics.Interval>> intervalsLiveData;
|
||||
private IntervalStatistics intervalStatistics;
|
||||
private MutableLiveData<List<Statistics>> intervalsLiveData;
|
||||
private IntervalStatisticsUpdater intervalStatistics;
|
||||
private Distance distanceInterval;
|
||||
private final ContentResolver contentResolver;
|
||||
private ContentObserver trackPointsTableObserver;
|
||||
@@ -65,7 +66,7 @@ public class IntervalStatisticsModel extends AndroidViewModel {
|
||||
handler = null;
|
||||
}
|
||||
|
||||
public MutableLiveData<List<IntervalStatistics.Interval>> getIntervalStats(Track.Id trackId, UnitSystem unitSystem, @Nullable IntervalOption interval) {
|
||||
public MutableLiveData<List<Statistics>> getIntervalStats(Track.Id trackId, UnitSystem unitSystem, @Nullable IntervalOption interval) {
|
||||
if (intervalsLiveData == null) {
|
||||
if (interval == null) {
|
||||
interval = IntervalOption.OPTION_1;
|
||||
@@ -73,7 +74,7 @@ public class IntervalStatisticsModel extends AndroidViewModel {
|
||||
|
||||
intervalsLiveData = new MutableLiveData<>();
|
||||
distanceInterval = interval.getDistance(unitSystem);
|
||||
intervalStatistics = new IntervalStatistics(distanceInterval);
|
||||
intervalStatistics = new IntervalStatisticsUpdater(distanceInterval);
|
||||
|
||||
loadIntervalStatistics(trackId);
|
||||
}
|
||||
@@ -92,7 +93,7 @@ public class IntervalStatisticsModel extends AndroidViewModel {
|
||||
private void loadIntervalStatistics(Track.Id trackId) {
|
||||
executor.execute(() -> {
|
||||
ContentProviderUtils contentProviderUtils = new ContentProviderUtils(getApplication());
|
||||
try (TrackPointIterator trackPointIterator = contentProviderUtils.getTrackPointLocationIterator(trackId, lastTrackPointId)) {
|
||||
try (AutoCloseableIterator<TrackPoint> trackPointIterator = contentProviderUtils.getTrackPointLocationIterator(trackId, lastTrackPointId)) {
|
||||
lastTrackPointId = intervalStatistics.addTrackPoints(trackPointIterator);
|
||||
intervalsLiveData.postValue(intervalStatistics.getIntervalList());
|
||||
}
|
||||
@@ -112,7 +113,7 @@ public class IntervalStatisticsModel extends AndroidViewModel {
|
||||
|
||||
lastTrackPointId = null;
|
||||
distanceInterval = interval.getDistance(unitSystem);
|
||||
intervalStatistics = new IntervalStatistics(distanceInterval);
|
||||
intervalStatistics = new IntervalStatisticsUpdater(distanceInterval);
|
||||
loadIntervalStatistics(trackId);
|
||||
}
|
||||
|
||||
|
||||
+51
-42
@@ -4,18 +4,19 @@ import androidx.annotation.Nullable;
|
||||
|
||||
import java.time.Duration;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
|
||||
import de.dennisguse.opentracks.data.TrackPointIterator;
|
||||
import de.dennisguse.opentracks.data.models.AltitudeGainLoss;
|
||||
import de.dennisguse.opentracks.data.models.Distance;
|
||||
import de.dennisguse.opentracks.data.models.HeartRate;
|
||||
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.TrackPoint;
|
||||
import de.dennisguse.opentracks.stats.TrackStatistics;
|
||||
import de.dennisguse.opentracks.stats.TrackStatisticsUpdater;
|
||||
|
||||
public class IntervalStatistics {
|
||||
public class IntervalStatisticsUpdater {
|
||||
private TrackStatisticsUpdater trackStatisticsUpdater = new TrackStatisticsUpdater();
|
||||
private final List<Interval> intervalList;
|
||||
private final Distance distanceInterval;
|
||||
@@ -24,7 +25,7 @@ public class IntervalStatistics {
|
||||
/**
|
||||
* @param distanceInterval distance of every interval.
|
||||
*/
|
||||
public IntervalStatistics(Distance distanceInterval) {
|
||||
public IntervalStatisticsUpdater(Distance distanceInterval) {
|
||||
this.distanceInterval = distanceInterval;
|
||||
|
||||
interval = new Interval();
|
||||
@@ -38,7 +39,7 @@ public class IntervalStatistics {
|
||||
*
|
||||
* @return the last track point's id used to compute the intervals.
|
||||
*/
|
||||
public TrackPoint.Id addTrackPoints(TrackPointIterator trackPointIterator) {
|
||||
public TrackPoint.Id addTrackPoints(Iterator<TrackPoint> trackPointIterator) {
|
||||
boolean newIntervalAdded = false;
|
||||
TrackPoint trackPoint = null;
|
||||
|
||||
@@ -46,7 +47,7 @@ public class IntervalStatistics {
|
||||
trackPoint = trackPointIterator.next();
|
||||
trackStatisticsUpdater.addTrackPoint(trackPoint);
|
||||
|
||||
if (trackStatisticsUpdater.getTrackStatistics().getTotalDistance().plus(interval.distance).greaterOrEqualThan(distanceInterval)) {
|
||||
if (trackStatisticsUpdater.getTrackStatistics().totalDistance().plus(interval.distance).greaterOrEqualThan(distanceInterval)) {
|
||||
interval.add(trackStatisticsUpdater.getTrackStatistics(), trackPoint);
|
||||
|
||||
double adjustFactor = distanceInterval.dividedBy(interval.distance);
|
||||
@@ -74,8 +75,8 @@ public class IntervalStatistics {
|
||||
return trackPoint != null ? trackPoint.getId() : null;
|
||||
}
|
||||
|
||||
public List<Interval> getIntervalList() {
|
||||
return intervalList;
|
||||
public List<Statistics> getIntervalList() {
|
||||
return intervalList.stream().map(Interval::toStatistics).toList();
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -84,21 +85,24 @@ public class IntervalStatistics {
|
||||
*
|
||||
* @return the interval object or null if any interval is completed.
|
||||
*/
|
||||
public Interval getLastInterval() {
|
||||
@Nullable
|
||||
public Statistics getLastInterval() {
|
||||
if (intervalList.size() == 1 && intervalList.get(0).getDistance().lessThan(distanceInterval)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
for (int i = intervalList.size() - 1; i >= 0; i--) {
|
||||
if (intervalList.get(i).getDistance().greaterOrEqualThan(distanceInterval)) {
|
||||
return this.intervalList.get(i);
|
||||
return this.intervalList.get(i).toStatistics();
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
public static class Interval {
|
||||
//TODO Could be replaced with Statistics?
|
||||
@Deprecated
|
||||
private static class Interval {
|
||||
private Distance distance = Distance.of(0);
|
||||
private Duration time = Duration.ofSeconds(0);
|
||||
private Float gain_m;
|
||||
@@ -144,18 +148,6 @@ public class IntervalStatistics {
|
||||
return gain_m != null;
|
||||
}
|
||||
|
||||
public Float getGain_m() {
|
||||
return gain_m;
|
||||
}
|
||||
|
||||
public boolean hasLoss() {
|
||||
return loss_m != null;
|
||||
}
|
||||
|
||||
public Float getLoss_m() {
|
||||
return loss_m;
|
||||
}
|
||||
|
||||
public boolean hasAverageHeartRate() {
|
||||
return avgHeartRate != null;
|
||||
}
|
||||
@@ -172,31 +164,48 @@ public class IntervalStatistics {
|
||||
return avgPower;
|
||||
}
|
||||
|
||||
private void add(TrackStatistics trackStatistics, @Nullable TrackPoint lastTrackPoint) {
|
||||
distance = distance.plus(trackStatistics.getTotalDistance());
|
||||
time = time.plus(trackStatistics.getTotalTime());
|
||||
gain_m = trackStatistics.hasTotalAltitudeGain() ? trackStatistics.getTotalAltitudeGain() : gain_m;
|
||||
loss_m = trackStatistics.hasTotalAltitudeLoss() ? trackStatistics.getTotalAltitudeLoss() : loss_m;
|
||||
avgHeartRate = trackStatistics.getAverageHeartRate();
|
||||
avgPower = trackStatistics.getAveragePower();
|
||||
private void add(Statistics trackStatistics, @Nullable TrackPoint lastTrackPoint) {
|
||||
distance = distance.plus(trackStatistics.totalDistance());
|
||||
time = time.plus(trackStatistics.totalTime());
|
||||
if (trackStatistics.altitudeGainLoss() != null) {
|
||||
gain_m = trackStatistics.altitudeGainLoss().gain_m();
|
||||
loss_m = trackStatistics.altitudeGainLoss().loss_m();
|
||||
}
|
||||
avgHeartRate = trackStatistics.avgHeartRate();
|
||||
avgPower = trackStatistics.avgPower();
|
||||
if (lastTrackPoint == null) {
|
||||
return;
|
||||
}
|
||||
if (hasGain() && lastTrackPoint.hasAltitudeGain()) {
|
||||
gain_m = gain_m - lastTrackPoint.getAltitudeGain();
|
||||
}
|
||||
if (hasLoss() && lastTrackPoint.hasAltitudeLoss()) {
|
||||
loss_m = loss_m - lastTrackPoint.getAltitudeLoss();
|
||||
if (hasGain() && lastTrackPoint.hasAltitudeGainLoss()) {
|
||||
gain_m = gain_m - lastTrackPoint.getAltitudeGainLoss().gain_m();
|
||||
loss_m = loss_m - lastTrackPoint.getAltitudeGainLoss().loss_m();
|
||||
}
|
||||
}
|
||||
|
||||
private void set(TrackStatistics trackStatistics) {
|
||||
distance = trackStatistics.getTotalDistance();
|
||||
time = trackStatistics.getTotalTime();
|
||||
gain_m = trackStatistics.hasTotalAltitudeGain() ? trackStatistics.getTotalAltitudeGain() : gain_m;
|
||||
loss_m = trackStatistics.hasTotalAltitudeLoss() ? trackStatistics.getTotalAltitudeLoss() : loss_m;
|
||||
avgHeartRate = trackStatistics.getAverageHeartRate();
|
||||
avgPower = trackStatistics.getAveragePower();
|
||||
private void set(Statistics trackStatistics) {
|
||||
distance = trackStatistics.totalDistance();
|
||||
time = trackStatistics.totalTime();
|
||||
if (trackStatistics.altitudeGainLoss() != null) {
|
||||
gain_m = trackStatistics.altitudeGainLoss().gain_m();
|
||||
loss_m = trackStatistics.altitudeGainLoss().loss_m();
|
||||
}
|
||||
avgHeartRate = trackStatistics.avgHeartRate();
|
||||
avgPower = trackStatistics.avgPower();
|
||||
}
|
||||
|
||||
private Statistics toStatistics() {
|
||||
return new Statistics(
|
||||
null,
|
||||
null,
|
||||
time,
|
||||
null,
|
||||
distance,
|
||||
null,
|
||||
null,
|
||||
gain_m != null ? new AltitudeGainLoss(gain_m, loss_m) : null,
|
||||
avgHeartRate,
|
||||
avgPower
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -21,6 +21,7 @@ import java.util.Objects;
|
||||
import de.dennisguse.opentracks.R;
|
||||
import de.dennisguse.opentracks.data.ContentProviderUtils;
|
||||
import de.dennisguse.opentracks.data.models.DistanceFormatter;
|
||||
import de.dennisguse.opentracks.data.models.Statistics;
|
||||
import de.dennisguse.opentracks.data.models.Track;
|
||||
import de.dennisguse.opentracks.databinding.IntervalListViewBinding;
|
||||
import de.dennisguse.opentracks.settings.PreferencesUtils;
|
||||
@@ -192,7 +193,7 @@ public class IntervalsFragment extends Fragment {
|
||||
}
|
||||
|
||||
viewBinding.intervalRate.setText(isReportSpeed ? getString(R.string.stats_speed) : getString(R.string.stats_pace));
|
||||
LiveData<List<IntervalStatistics.Interval>> liveData = viewModel.getIntervalStats(trackId, unitSystem, selectedInterval);
|
||||
LiveData<List<Statistics>> liveData = viewModel.getIntervalStats(trackId, unitSystem, selectedInterval);
|
||||
liveData.observe(getActivity(), intervalList -> adapter.swapData(intervalList, unitSystem, isReportSpeed));
|
||||
|
||||
setIntervalsDropdownText();
|
||||
|
||||
@@ -0,0 +1,11 @@
|
||||
package de.dennisguse.opentracks.util;
|
||||
|
||||
import java.util.Iterator;
|
||||
|
||||
public abstract class AutoCloseableIterator<T> implements Iterator<T>, AutoCloseable {
|
||||
|
||||
/**
|
||||
* Override: AutoClosable.close() throws Exception.
|
||||
*/
|
||||
public abstract void close();
|
||||
}
|
||||
@@ -34,7 +34,7 @@ public abstract class GenericStatisticsViewHolder extends StatisticViewHolder<St
|
||||
public void onChanged(UnitSystem unitSystem, RecordingData data) {
|
||||
Pair<String, String> valueAndUnit = DistanceFormatter.Builder()
|
||||
.setUnit(unitSystem)
|
||||
.build(getContext()).getDistanceParts(data.getStatisticsTrack().getTotalDistance());
|
||||
.build(getContext()).getDistanceParts(data.getStatisticsTrack().totalDistance());
|
||||
|
||||
getBinding().statsValue.setText(valueAndUnit.first);
|
||||
getBinding().statsUnit.setText(valueAndUnit.second);
|
||||
@@ -46,7 +46,7 @@ public abstract class GenericStatisticsViewHolder extends StatisticViewHolder<St
|
||||
|
||||
@Override
|
||||
public void onChanged(UnitSystem unitSystem, RecordingData data) {
|
||||
Pair<String, String> valueAndUnit = new Pair<>(StringUtils.formatElapsedTime(data.getStatisticsTrack().getTotalTime()), null);
|
||||
Pair<String, String> valueAndUnit = new Pair<>(StringUtils.formatElapsedTime(data.getStatisticsTrack().totalTime()), null);
|
||||
|
||||
getBinding().statsValue.setText(valueAndUnit.first);
|
||||
getBinding().statsUnit.setText(valueAndUnit.second);
|
||||
@@ -58,7 +58,7 @@ public abstract class GenericStatisticsViewHolder extends StatisticViewHolder<St
|
||||
|
||||
@Override
|
||||
public void onChanged(UnitSystem unitSystem, RecordingData data) {
|
||||
String value = StringUtils.formatElapsedTime(data.getStatisticsTrack().getMovingTime());
|
||||
String value = StringUtils.formatElapsedTime(data.getStatisticsTrack().movingTime());
|
||||
|
||||
getBinding().statsValue.setText(value);
|
||||
getBinding().statsDescriptionMain.setText(getContext().getString(R.string.stats_moving_time));
|
||||
@@ -157,7 +157,7 @@ public abstract class GenericStatisticsViewHolder extends StatisticViewHolder<St
|
||||
.setReportSpeedOrPace(true)
|
||||
.build(getContext());
|
||||
|
||||
Pair<String, String> valueAndUnit = speedFormatterSpeed.getSpeedParts(data.getStatisticsTrack().getMaxSpeed());
|
||||
Pair<String, String> valueAndUnit = speedFormatterSpeed.getSpeedParts(data.getStatisticsTrack().maxSpeed());
|
||||
|
||||
getBinding().statsValue.setText(valueAndUnit.first);
|
||||
getBinding().statsUnit.setText(valueAndUnit.second);
|
||||
@@ -208,7 +208,7 @@ public abstract class GenericStatisticsViewHolder extends StatisticViewHolder<St
|
||||
.setReportSpeedOrPace(false)
|
||||
.build(getContext());
|
||||
|
||||
Pair<String, String> valueAndUnit = speedFormatterSpeed.getSpeedParts(data.getStatisticsCurrentSegment().getAverageMovingSpeed());
|
||||
Pair<String, String> valueAndUnit = speedFormatterSpeed.getSpeedParts(data.currentSegment().getAverageMovingSpeed());
|
||||
|
||||
getBinding().statsValue.setText(valueAndUnit.first);
|
||||
getBinding().statsUnit.setText(valueAndUnit.second);
|
||||
@@ -216,6 +216,17 @@ public abstract class GenericStatisticsViewHolder extends StatisticViewHolder<St
|
||||
}
|
||||
}
|
||||
|
||||
public static class ElapsedSegmentTime extends GenericStatisticsViewHolder {
|
||||
|
||||
@Override
|
||||
public void onChanged(UnitSystem unitSystem, RecordingData data) {
|
||||
String value = StringUtils.formatElapsedTime(data.currentSegment().totalTime());
|
||||
|
||||
getBinding().statsValue.setText(value);
|
||||
getBinding().statsDescriptionMain.setText(getContext().getString(R.string.stats_elapsed_segment_time));
|
||||
}
|
||||
}
|
||||
|
||||
public static class FastestPace extends GenericStatisticsViewHolder {
|
||||
|
||||
@Override
|
||||
@@ -225,7 +236,7 @@ public abstract class GenericStatisticsViewHolder extends StatisticViewHolder<St
|
||||
.setReportSpeedOrPace(false)
|
||||
.build(getContext());
|
||||
|
||||
Pair<String, String> valueAndUnit = speedFormatterSpeed.getSpeedParts(data.getStatisticsTrack().getMaxSpeed());
|
||||
Pair<String, String> valueAndUnit = speedFormatterSpeed.getSpeedParts(data.getStatisticsTrack().maxSpeed());
|
||||
|
||||
getBinding().statsValue.setText(valueAndUnit.first);
|
||||
getBinding().statsUnit.setText(valueAndUnit.second);
|
||||
@@ -253,8 +264,9 @@ public abstract class GenericStatisticsViewHolder extends StatisticViewHolder<St
|
||||
|
||||
@Override
|
||||
public void onChanged(UnitSystem unitSystem, RecordingData data) {
|
||||
Float gain = data.getStatisticsTrack().altitudeGainLoss() != null ? data.getStatisticsTrack().altitudeGainLoss().gain_m() : null;
|
||||
|
||||
Pair<String, String> valueAndUnit = StringUtils.getAltitudeParts(getContext(), data.getStatisticsTrack().getTotalAltitudeGain(), unitSystem);
|
||||
Pair<String, String> valueAndUnit = StringUtils.getAltitudeParts(getContext(), gain, unitSystem);
|
||||
|
||||
getBinding().statsValue.setText(valueAndUnit.first);
|
||||
getBinding().statsUnit.setText(valueAndUnit.second);
|
||||
@@ -266,8 +278,9 @@ public abstract class GenericStatisticsViewHolder extends StatisticViewHolder<St
|
||||
|
||||
@Override
|
||||
public void onChanged(UnitSystem unitSystem, RecordingData data) {
|
||||
Float loss = data.getStatisticsTrack().altitudeGainLoss() != null ? data.getStatisticsTrack().altitudeGainLoss().loss_m() : null;
|
||||
|
||||
Pair<String, String> valueAndUnit = StringUtils.getAltitudeParts(getContext(), data.getStatisticsTrack().getTotalAltitudeLoss(), unitSystem);
|
||||
Pair<String, String> valueAndUnit = StringUtils.getAltitudeParts(getContext(), loss, unitSystem);
|
||||
|
||||
getBinding().statsValue.setText(valueAndUnit.first);
|
||||
getBinding().statsUnit.setText(valueAndUnit.second);
|
||||
|
||||
@@ -25,6 +25,7 @@ public class Mapping {
|
||||
m.put(context.getString(R.string.stats_custom_layout_average_moving_pace_key), GenericStatisticsViewHolder.AverageMovingPace::new);
|
||||
m.put(context.getString(R.string.stats_custom_layout_average_pace_key), GenericStatisticsViewHolder.AveragePace::new);
|
||||
m.put(context.getString(R.string.stats_custom_layout_average_segment_pace_key), GenericStatisticsViewHolder.AverageSegmentPace::new);
|
||||
m.put(context.getString(R.string.stats_custom_layout_elapsed_segment_time_key), GenericStatisticsViewHolder.ElapsedSegmentTime::new);
|
||||
m.put(context.getString(R.string.stats_custom_layout_fastest_pace_key), GenericStatisticsViewHolder.FastestPace::new);
|
||||
|
||||
m.put(context.getString(R.string.stats_custom_layout_altitude_key), GenericStatisticsViewHolder.Altitude::new);
|
||||
|
||||
@@ -30,27 +30,25 @@ limitations under the License.
|
||||
\n* funciona sen precisar conexión á rede
|
||||
\n* mostra as rutas nun mapa (ver abaixo)</string>
|
||||
<string name="help_track_map_title">Como mostrar a ruta no mapa\?</string>
|
||||
<string name="help_track_map_content">Usa a icona do mapa pequeno. \n a) (actualizacións en directo) Instala <a href="https://codeberg.org/OpenTracksApp/OSMDashboard">OSMDashboard</a>. Proporciona mapas en liña ou sen conexión grazas a <a href="https://www.openstreetmap.org">OpenStreetMap</a>. \n En realidade, OSMDashboard é unha aplicación independente que só pode acceder (temporalmente) aos datos das pistas que se amosarán. \n b) A través dunha aplicación de mapa normal (por exemplo, <a href="http://osmand.net/">OsmAnd</a> e <a href="https://maps.me/">MAPS.ME</a>). Estas aplicacións precisan admitir o formato de ficheiro KMZ (só localizacións e marcas de tempo). De feito, unha aplicación ten que admitir <i>ACTION_VIEW</i> con MIME <i>application/vnd.google-earth.kmz</i>.</string>
|
||||
<string name="help_track_map_content">Usa a icona pequena do mapa (abaixo). \n a) (actualizacións no momento) Instala <a href="https://codeberg.org/OpenTracksApp/OSMDashboard">OSMDashboard</a>. Proporciona mapas en liña ou sen conexión grazas a <a href="https://www.openstreetmap.org">OpenStreetMap</a>. \n En realidade, OSMDashboard é unha aplicación independente que só pode acceder (temporalmente) aos datos das rutas que se van mostrar. \n b) Usando dunha aplicación de mapa normal (por exemplo, <a href="http://osmand.net/">OsmAnd</a> e <a href="https://maps.me/">MAPS.ME</a>). Estas aplicacións precisan admitir o formato de ficheiro KMZ (só localizacións e marcas de tempo). De feito, unha aplicación ten que admitir <i>ACTION_VIEW</i> con MIME <i>application/vnd.google-earth.kmz</i>.</string>
|
||||
<string name="help_storage_pictures_title">De que xeito garda OpenTracks as fotos tomadas como marcadores\?</string>
|
||||
<string name="help_storage_pictures_content">Gárdanse na almacenaxe interna da app (só accesible desde OpenTracks).</string>
|
||||
<string name="help_track_export_title">De que xeito exporto/importo as rutas gravadas\?</string>
|
||||
<string name="help_track_export_content"> As rutas pódense exportar desde os Axustes como KMZ, KML ou GPX. KMZ/KML inclúe todos os datos; GPX só localización. As rutas compartidas a través da <i>icona compartir</i> compártense como KMZ. </string>
|
||||
<string name="help_track_export_content">As rutas pódense exportar desde os Axustes como KMZ, KML ou GPX. KMZ/KML inclúe todos os datos; No formato GPX só inclúe a localización. As rutas compartidas a través da <i>icona compartir</i> compártense como KMZ.</string>
|
||||
<string name="help_data_google_play_services_title">OpenTracks precisa/utiliza Google Play Service\?</string>
|
||||
<string name="help_recording_title">Como grava OpenTracks os datos\?</string>
|
||||
<string name="help_recording_content">
|
||||
OpenTracks usa o sensor GPS do dispositivo para gravar a localización de xeito contínuo.
|
||||
Se o GPS informa sobre datos pouco precisos (ex.: localización, velocidade, elevación), OpenTracks non pode facer nada ao respecto.</string>
|
||||
<string name="help_recording_content">OpenTracks usa o sensor GPS do dispositivo para gravar a localización de xeito contínuo. Se o sensor GPS envía datos pouco precisos (ex.: localización, velocidade, elevación), OpenTracks non pode facer nada ao respecto.</string>
|
||||
<string name="help_data_storage_title">De que xeito garda OpenTracks os datos dos sensores\?</string>
|
||||
<string name="help_data_storage_content">Os datos dos sensores gárdanse asociados á localización. Respecto do ritmo cardíaco, significa que só se garda <i>unha medida</i> asociada a unha localización. Se te detés ou estás en interiores, non se gardarán datos do sensor (mais serán mostrados).</string>
|
||||
<string name="help_data_storage_content">Os datos dos sensores gárdanse asociados á localización. Respecto do ritmo cardíaco, significa que só se garda <i>unha medida</i> asociada a unha localización. Se te detés ou estás en interiores, non se gardarán datos do sensor (mais serán mostrados no momento).</string>
|
||||
<string name="help_gps_troubleshooting_title">OpenTracks non grava as localizacións\?</string>
|
||||
<string name="help_gps_troubleshooting_content">Podes intentar: \n a) mira que o GPS esté activado, \n b) mellora a recepción GPS (ex. sae ao exterior), \n c) comprobaa coa app <a href="https://f-droid.org/en/packages/com.android.gpstest.osmdroid/">GPSTest</a>, e \n d) desactiva o aforro de enerxía. \n \n Se isto non axuda, infórmanos do problema.</string>
|
||||
<string name="help_gps_troubleshooting_content">Podes intentar: \n a) comproba que o GPS esté activado, \n b) mellora a recepción GPS (ex. sae ao exterior), \n c) comprobaa coa app <a href="https://f-droid.org/en/packages/com.android.gpstest.osmdroid/">GPSTest</a>, e \n d) desactiva o aforro de enerxía. \n \n Se isto non axuda, infórmanos do problema.</string>
|
||||
<string name="help_support_title">Como podo axudar a OpenTracks\?</string>
|
||||
<string name="help_support_content">Podes: \n a) falar de OpenTracks coas túas amizades, \n b) <a href="https://codeberg.org/OpenTracksApp/OpenTracks/issues">propoñer novas funcións</a>, \n c) <a href="https://codeberg.org/OpenTracksApp/OpenTracks/issues">informar de fallos</a>, \n d) axudar no desenvolvemento, \n e) mercar a app na <a href="https://play.google.com/store/apps/details?id=de.dennisguse.opentracks.playstore">Google Play Store</a>, \n f) facer unha doazón, e \n g) calquera cousa que estimes axuda a OpenTracks.</string>
|
||||
<string name="open_track_as_trackfileformat">Abrir como %1$s</string>
|
||||
<!-- Activity Type -->
|
||||
<string name="activity_type_airplane">avión</string>
|
||||
<string name="activity_type_atv">Quad</string>
|
||||
<string name="activity_type_biking">ir en bici</string>
|
||||
<string name="activity_type_biking">ciclismo recreativo</string>
|
||||
<string name="activity_type_blimp">dirixible</string>
|
||||
<string name="activity_type_boat">barco</string>
|
||||
<string name="activity_type_climbing">escalada</string>
|
||||
@@ -73,7 +71,7 @@ Se o GPS informa sobre datos pouco precisos (ex.: localización, velocidade, ele
|
||||
<string name="activity_type_hot_air_balloon">globo aerostático</string>
|
||||
<string name="activity_type_ice_sailing">navegación polo xeo</string>
|
||||
<string name="activity_type_inline_skating">patinaxe en liña</string>
|
||||
<string name="activity_type_kayaking">kayak</string>
|
||||
<string name="activity_type_kayaking">caiac</string>
|
||||
<string name="activity_type_kite_surfing">kitesurf</string>
|
||||
<string name="activity_type_land_sailing">carrovelismo</string>
|
||||
<string name="activity_type_mixed_type">tipo mixto</string>
|
||||
@@ -82,7 +80,7 @@ Se o GPS informa sobre datos pouco precisos (ex.: localización, velocidade, ele
|
||||
<string name="activity_type_mountain_biking">ciclismo de montaña</string>
|
||||
<string name="activity_type_off_trail_hiking">sendeirismo libre</string>
|
||||
<string name="activity_type_other">outra</string>
|
||||
<string name="activity_type_paddling">remo</string>
|
||||
<string name="activity_type_paddling">padexar</string>
|
||||
<string name="activity_type_para_gliding">parapente</string>
|
||||
<string name="activity_type_rc_airplane">Avión de radiocontrol</string>
|
||||
<string name="activity_type_rc_boat">Barco de radiocontrol</string>
|
||||
@@ -107,7 +105,7 @@ Se o GPS informa sobre datos pouco precisos (ex.: localización, velocidade, ele
|
||||
<string name="activity_type_track_cycling">ciclismo en pista</string>
|
||||
<string name="activity_type_track_running">atletismo</string>
|
||||
<string name="activity_type_trail_hiking">sendeirismo</string>
|
||||
<string name="activity_type_trail_running">correr polas montañas</string>
|
||||
<string name="activity_type_trail_running">correr por camiños</string>
|
||||
<string name="activity_type_train">tren</string>
|
||||
<string name="activity_type_ultimate_frisbee">ultimate frisbee</string>
|
||||
<string name="activity_type_unknown">descoñecida</string>
|
||||
@@ -117,24 +115,24 @@ Se o GPS informa sobre datos pouco precisos (ex.: localización, velocidade, ele
|
||||
<string name="activity_type_wind_surfing">windsurf</string>
|
||||
<!-- Description -->
|
||||
<string name="description_activity_type">Tipo de actividade: %1$s</string>
|
||||
<string name="description_average_moving_pace_in_minute">Ritmo de desprazamento medio: %1$s min/km (%2$s min/mi)</string>
|
||||
<string name="description_average_moving_speed">Velocidade de desprazamento media: %1$.2f km/h (%2$.1f mi/h)</string>
|
||||
<string name="description_average_moving_pace_in_minute">Ritmo medio en movemento: %1$s min/km (%2$s min/mi)</string>
|
||||
<string name="description_average_moving_speed">Velocidade media en movemento: %1$.2f km/h (%2$.1f mi/h)</string>
|
||||
<string name="description_average_pace_in_minute">Ritmo medio: %1$s min/km (%2$s min/mi)</string>
|
||||
<string name="description_average_speed">Velocidade media: %1$.2f km/h (%2$.1f min/mi)</string>
|
||||
<string name="description_altitude_gain">Desnivel: %1$d m (%2$d pés)</string>
|
||||
<string name="description_altitude_imperial">Elevación (pés)</string>
|
||||
<string name="description_altitude_metric">Elevación (m)</string>
|
||||
<string name="description_altitude_gain">Desnivel+ : %1$d m (%2$d ft)</string>
|
||||
<string name="description_altitude_imperial">Atitude (ft)</string>
|
||||
<string name="description_altitude_metric">Altitude (m)</string>
|
||||
<string name="description_fastest_pace_in_minute">Ritmo máis rápido: %1$s min/km (%2$s min/mi)</string>
|
||||
<string name="description_max_altitude">Elevación máxima: %1$d m (%2$d pés)</string>
|
||||
<string name="description_max_altitude">Altitude máxima: %1$d m (%2$d ft)</string>
|
||||
<string name="description_max_speed">Velocidade máxima: %1$.2f km/h (%2$.1f mi/h)</string>
|
||||
<string name="description_min_altitude">Elevación mínima: %1$d m (%2$d pés)</string>
|
||||
<string name="description_moving_time">Tempo de desprazamento: %1$s</string>
|
||||
<string name="description_min_altitude">Altitude mínima: %1$d m (%2$d ft)</string>
|
||||
<string name="description_moving_time">Tempo en movemento: %1$s</string>
|
||||
<string name="description_pace_imperial">Ritmo (min/mi)</string>
|
||||
<string name="description_pace_metric">Ritmo (min/km)</string>
|
||||
<string name="description_recorded_time">Hora da gravación: %1$s</string>
|
||||
<string name="description_speed_ms">Velocidade (m/s)</string>
|
||||
<string name="description_sensor_cadence">Cadencia (rpm)</string>
|
||||
<string name="description_sensor_heart_rate">Pulso cardíaco (latexos por minuto)</string>
|
||||
<string name="description_sensor_heart_rate">Pulso cardíaco (por minuto)</string>
|
||||
<string name="description_sensor_power">Potencia (W)</string>
|
||||
<string name="description_default_speed_or_pace">Por tipo de actividade</string>
|
||||
<string name="description_speed_imperial">Velocidade (mi/h)</string>
|
||||
@@ -169,9 +167,9 @@ Se o GPS informa sobre datos pouco precisos (ex.: localización, velocidade, ele
|
||||
<string name="image_photo_marker">Marcador de fotos</string>
|
||||
<string name="image_record">Gravar</string>
|
||||
<string name="image_stop">Deter</string>
|
||||
<string name="image_track">Traxecto</string>
|
||||
<string name="image_track">Ruta</string>
|
||||
<!-- Import -->
|
||||
<string name="import_progress_message">Importando de %1$s…</string>
|
||||
<string name="import_progress_message">Importando desde %1$s…</string>
|
||||
<!-- Marker -->
|
||||
<string name="marker_add_error">Non se pode inserir un marcador. Non hai sinal de GPS. Téntao de novo.</string>
|
||||
<string name="marker_add_success">Inseriuse un marcador</string>
|
||||
@@ -183,23 +181,23 @@ Se o GPS informa sobre datos pouco precisos (ex.: localización, velocidade, ele
|
||||
<string name="marker_name_format">Marcador %1$d</string>
|
||||
<!-- Marker Detail -->
|
||||
<!-- Marker Edit -->
|
||||
<string name="marker_edit_marker_type">Tipo de marcador (por exemplo, fervenza)</string>
|
||||
<string name="marker_edit_marker_type">Tipo de marcador (ex.: fervenza)</string>
|
||||
<!-- Marker List -->
|
||||
<string name="marker_list_empty_message">Non tes marcadores</string>
|
||||
<!-- Menu -->
|
||||
<string name="menu_aggregated_statistics">Estatísticas globais</string>
|
||||
<string name="menu_aggregated_statistics">Estatísticas totais</string>
|
||||
<string name="menu_delete">Eliminar</string>
|
||||
<string name="menu_edit">Editar</string>
|
||||
<string name="menu_export_all">Exportar todo (un ficheiro por ruta)</string>
|
||||
<string name="menu_help">Axuda</string>
|
||||
<string name="menu_import_all">Importar todo</string>
|
||||
<string name="menu_insert_marker">Inserir marcador</string>
|
||||
<string name="menu_insert_photo">Inserir foto</string>
|
||||
<string name="menu_insert_gallery_img">Insertar imaxe da galería</string>
|
||||
<string name="menu_insert_marker">Engadir marcador</string>
|
||||
<string name="menu_insert_photo">Engadir foto</string>
|
||||
<string name="menu_insert_gallery_img">Engadir imaxe da galería</string>
|
||||
<string name="menu_markers">Marcadores</string>
|
||||
<string name="menu_search">Busca</string>
|
||||
<string name="menu_select_all">Seleccionar todo</string>
|
||||
<string name="menu_settings">Configuración</string>
|
||||
<string name="menu_settings">Axustes</string>
|
||||
<string name="menu_share">Compartir</string>
|
||||
<string name="menu_show_on_map">Amosar no mapa</string>
|
||||
<string name="menu_resume_track">Retomar gravación</string>
|
||||
@@ -229,7 +227,7 @@ Se o GPS informa sobre datos pouco precisos (ex.: localización, velocidade, ele
|
||||
<string name="settings_recording_fullscreen_on_while_recording_summary">Mentras grava, mostrar a pantalla completa.</string>
|
||||
<string name="settings_recording_default_activity_title">Tipo de actividade predeterminada</string>
|
||||
<string name="settings_recording_location_frequency_summary">%1$s entre comprobacións da posición</string>
|
||||
<string name="settings_recording_max_recording_distance_summary">Créase un novo segmento se a distancia entre as posicións é superior a %1$s</string>
|
||||
<string name="settings_recording_max_recording_distance_summary">Créase un novo tramo se a distancia entre as posicións é superior a %1$s</string>
|
||||
<string name="settings_recording_max_recording_distance_title">Distancia máxima</string>
|
||||
<string name="settings_recording_min_recording_distance_title">Intervalo da distancia para gravar</string>
|
||||
<string name="settings_recording_min_recording_interval_title">Tempo entre posicións</string>
|
||||
@@ -246,22 +244,22 @@ Se o GPS informa sobre datos pouco precisos (ex.: localización, velocidade, ele
|
||||
<string name="settings_sensor_wheel_circumference">Circunferencia da roda (mm)</string>
|
||||
<!-- Settings Stats -->
|
||||
<string name="settings_stats_rate_title">Indicador preferido</string>
|
||||
<string name="settings_stats_units_imperial">Imperiais (millas, pés)</string>
|
||||
<string name="settings_stats_units_metric">Unidades (km, m)</string>
|
||||
<string name="settings_stats_units_imperial">Imperial (mi, ft)</string>
|
||||
<string name="settings_stats_units_metric">Métrico (km, m)</string>
|
||||
<string name="settings_stats_units_title">Unidades preferidas</string>
|
||||
<!-- Share Track -->
|
||||
<string name="share_track_share_file_body">Creo que che pode interesar esta ruta.
|
||||
\n
|
||||
\n%1$s</string>
|
||||
<string name="share_track_subject">Gustaríame compartir un traxecto contigo</string>
|
||||
<string name="share_track_subject">Gustaríame compartir unha ruta contigo</string>
|
||||
<!-- Stats -->
|
||||
<string name="stats_average_moving_pace">Ritmo de desprazamento medio</string>
|
||||
<string name="stats_average_moving_speed">Velocidade de desprazamento media</string>
|
||||
<string name="stats_average_moving_pace">Ritmo medio en movemento</string>
|
||||
<string name="stats_average_moving_speed">Velocidade media en movemento</string>
|
||||
<string name="stats_average_pace">Ritmo medio</string>
|
||||
<string name="stats_average_speed">Velocidade media</string>
|
||||
<string name="stats_altitude">Elevación</string>
|
||||
<string name="stats_altitude">Altitude</string>
|
||||
<string name="stats_fastest_pace">Ritmo máis rápido</string>
|
||||
<string name="stats_gain">Desnivel</string>
|
||||
<string name="stats_gain">Gañancia</string>
|
||||
<string name="stats_max_speed">Velocidade máxima</string>
|
||||
<string name="stats_moving_time">Tempo en movemento</string>
|
||||
<string name="stats_pace">Ritmo</string>
|
||||
@@ -280,26 +278,26 @@ Se o GPS informa sobre datos pouco precisos (ex.: localización, velocidade, ele
|
||||
<!-- Track Detail -->
|
||||
<string name="track_detail_stats_tab">Estatísticas</string>
|
||||
<!-- Track Edit -->
|
||||
<string name="track_edit_activity_type">Tipo de actividade (por exemplo, sendeirismo)</string>
|
||||
<string name="track_edit_activity_type">Tipo de actividade (ex: sendeirismo)</string>
|
||||
<string name="track_edit_activity_type_hint">Tipo de actividade</string>
|
||||
<!-- Track List -->
|
||||
<string name="track_list_empty_message">Empeza a gravar aquí a túa próxima aventura</string>
|
||||
<string name="track_list_empty_message">Grava aquí a túa próxima aventura</string>
|
||||
<!-- Aggregated Statistics -->
|
||||
<string name="aggregated_stats_empty_message">Grava a túa primeira ruta para ver estatísticas </string>
|
||||
<!-- Track Widget -->
|
||||
<!-- Tracks -->
|
||||
<!-- Unit -->
|
||||
<string name="unit_feet">m</string>
|
||||
<string name="unit_feet">ft</string>
|
||||
<string name="unit_kilometer">km</string>
|
||||
<string name="unit_kilometer_per_hour">km/h</string>
|
||||
<string name="unit_meter">m</string>
|
||||
<string name="unit_mile">km</string>
|
||||
<string name="unit_mile">mi</string>
|
||||
<string name="unit_mile_per_hour">mph</string>
|
||||
<string name="unit_minute_per_kilometer">min/km</string>
|
||||
<string name="unit_minute_per_mile">min/km</string>
|
||||
<string name="unit_minute_per_mile">min/mi</string>
|
||||
<!-- Value -->
|
||||
<string name="value_integer_feet_excellent_gps">%1$d m (GPS excelente)</string>
|
||||
<string name="value_integer_feet_recommended">%1$d m (recomendado)</string>
|
||||
<string name="value_integer_feet_excellent_gps">%1$d ft (GPS excelente)</string>
|
||||
<string name="value_integer_feet_recommended">%1$d ft (recomendado)</string>
|
||||
<string name="value_integer_kilometer_hour">%1$d km/h</string>
|
||||
<string name="value_integer_meter_excellent_gps">%1$d m (GPS excelente)</string>
|
||||
<string name="value_integer_meter_poor_gps">%1$d m (GPS deficiente)</string>
|
||||
@@ -322,7 +320,7 @@ Se o GPS informa sobre datos pouco precisos (ex.: localización, velocidade, ele
|
||||
<string name="waypoint_type_campground">área de cámping</string>
|
||||
<string name="waypoint_type_church">igrexa</string>
|
||||
<string name="waypoint_type_coffee_house">cafetaría</string>
|
||||
<string name="waypoint_type_convenience_store">tenda de barrio</string>
|
||||
<string name="waypoint_type_convenience_store">tenda de proximidade</string>
|
||||
<string name="waypoint_type_drinking_water">auga potable</string>
|
||||
<string name="waypoint_type_falling_rocks">desprendementos</string>
|
||||
<string name="waypoint_type_ferry">transbordador</string>
|
||||
@@ -343,37 +341,37 @@ Se o GPS informa sobre datos pouco precisos (ex.: localización, velocidade, ele
|
||||
<string name="waypoint_type_market">mercado</string>
|
||||
<string name="waypoint_type_meeting_point">punto de encontro</string>
|
||||
<string name="waypoint_type_opera">ópera</string>
|
||||
<string name="waypoint_type_parking">aparcadoiro</string>
|
||||
<string name="waypoint_type_parking">estacionamento</string>
|
||||
<string name="waypoint_type_police_station">comisaría de policía</string>
|
||||
<string name="waypoint_type_post_office">oficina de correos</string>
|
||||
<string name="waypoint_type_pothole">cova</string>
|
||||
<string name="waypoint_type_railway_crossing">paso a nivel</string>
|
||||
<string name="waypoint_type_restaurant">restaurante</string>
|
||||
<string name="waypoint_type_shopping">compras</string>
|
||||
<string name="waypoint_type_shopping">mercas</string>
|
||||
<string name="waypoint_type_sight">vista</string>
|
||||
<string name="waypoint_type_store">tenda</string>
|
||||
<string name="waypoint_type_swimming_pool">piscina</string>
|
||||
<string name="waypoint_type_telephone">teléfono</string>
|
||||
<string name="waypoint_type_theater">teatro</string>
|
||||
<string name="waypoint_type_toilet">lavabo</string>
|
||||
<string name="waypoint_type_toilet">aseo</string>
|
||||
<string name="waypoint_type_tourist_information">información turística</string>
|
||||
<string name="waypoint_type_tree">árbore</string>
|
||||
<string name="waypoint_type_tunnel">túnel</string>
|
||||
<string name="waypoint_type_waterfall">fervenza</string>
|
||||
<string name="waypoint_type_water_fountain">fonte de auga</string>
|
||||
<string name="waypoint_type_waypoint">Punto de referencia</string>
|
||||
<string name="waypoint_type_waypoint">punto de referencia</string>
|
||||
<!-- Share image -->
|
||||
<string name="share_image_subject">Quixera compartir unha imaxe contigo</string>
|
||||
<string name="share_image_body">Creo que che podería gustar esta imaxe.</string>
|
||||
<string name="hold_to_stop">Mantén preso para deter</string>
|
||||
<string name="hold_to_stop">Mantén premido para deter</string>
|
||||
<string name="export_all_do_it_for_all">facelo para todos os conflitos</string>
|
||||
<string name="export_track_errors">Non se exportaron ficheiros</string>
|
||||
<string name="generic_skip">Omitir</string>
|
||||
<string name="generic_separator_done_total">/</string>
|
||||
<string name="generic_show_errors">Mostrar erros</string>
|
||||
<string name="generic_completed">Completado</string>
|
||||
<string name="generic_click_twice_cancel">Preme ir atrás para cancelar</string>
|
||||
<string name="import_unsupported_format">Formato do ficheiro non soportado</string>
|
||||
<string name="generic_click_twice_cancel">Preme en Volver para cancelar</string>
|
||||
<string name="import_unsupported_format">Formato do ficheiro incompatible</string>
|
||||
<string name="import_parser_error">Erro do intérprete: %1$s</string>
|
||||
<string name="import_unable_to_import_file">Non se puido importar: %1$s</string>
|
||||
<string name="import_error_info">%1$s: %2$s</string>
|
||||
@@ -383,7 +381,7 @@ Se o GPS informa sobre datos pouco precisos (ex.: localización, velocidade, ele
|
||||
<string name="settings_night_mode_option_night">Noite</string>
|
||||
<string name="stats_split_interval">Intervalo parcial</string>
|
||||
<string name="track_detail_intervals_tab">Intervalos</string>
|
||||
<string name="instant_export_enabled_title">Exportación instantánea tras o adestramento</string>
|
||||
<string name="instant_export_enabled_title">Exportación instantánea após a actividade</string>
|
||||
<string name="settings_default_export_uri_title">Directorio onde exportar as rutas</string>
|
||||
<string name="export_track_already_exists_msg">O ficheiro %1$s xa existe no directorio de destino.</string>
|
||||
<string name="generic_overwrite">Sobrescribir</string>
|
||||
@@ -391,7 +389,7 @@ Se o GPS informa sobre datos pouco precisos (ex.: localización, velocidade, ele
|
||||
<item quantity="one">Completado cun erro</item>
|
||||
<item quantity="other">Completado con %1$d erros</item>
|
||||
</plurals>
|
||||
<string name="settings_night_mode_title">Decorado IU</string>
|
||||
<string name="settings_night_mode_title">Decorado da interface</string>
|
||||
<string name="settings_night_mode_option_system">Sistema</string>
|
||||
<string name="interval_list_empty_message">Aínda non hai intervalos</string>
|
||||
<string name="instant_export_enabled_summary">Rematou a exportación da ruta á almacenaxe tras a gravación</string>
|
||||
@@ -399,28 +397,28 @@ Se o GPS informa sobre datos pouco precisos (ex.: localización, velocidade, ele
|
||||
<string name="export_progress_summary_new_msg">Exportados os novos ficheiros</string>
|
||||
<string name="import_no_kml_file_found">Non se atopou ficheiro kml en KMZ</string>
|
||||
<string name="stats_loss">Perda</string>
|
||||
<string name="description_altitude_loss">Perda de altitude: %1$d m (%2$d ft)</string>
|
||||
<string name="description_altitude_loss">Desnivel- : %1$d m (%2$d ft)</string>
|
||||
<string name="export_error_post_workout">Fallou a exportación, comproba o directorio de exportación.</string>
|
||||
<string name="settings_prevent_reimport_tracks_title">Previr a reimportación de rutas</string>
|
||||
<string name="settings_prevent_reimport_tracks_title">Evitar a reimportación de rutas</string>
|
||||
<string name="export_progress_summary_overwrite_msg">Ficheiros sobrescritos</string>
|
||||
<string name="export_progress_summary_skip_msg">Ficheiros omitidos</string>
|
||||
<string name="export_progress_summary_errors_msg">Erros</string>
|
||||
<string name="generic_open_track">Abrir a Ruta</string>
|
||||
<string name="import_prevent_reimport">Estableceches previr a reimportación da ruta</string>
|
||||
<string name="import_prevent_reimport">Configuraches evitar a reimportación da ruta</string>
|
||||
<string name="import_progress_summary_ok_msg">Ficheiros importados</string>
|
||||
<string name="import_progress_summary_exists_msg">Xa existe</string>
|
||||
<string name="import_progress_summary_errors_msg">Erros</string>
|
||||
<string name="sensor_state_cadence_avg">Cadencia media</string>
|
||||
<string name="sensor_state_cadence_max">Max Cadencia</string>
|
||||
<string name="sensor_state_cadence_max">Cadencia máx</string>
|
||||
<string name="sensor_state_heart_rate_avg">Pulso medio</string>
|
||||
<string name="sensor_state_heart_rate_max">Pulso máximo</string>
|
||||
<string name="sensor_state_power_avg">Potencia media</string>
|
||||
<string name="track_distance_notification">Distancia: %1$s</string>
|
||||
<string name="track_pace_notification">Ritmo: %1$s</string>
|
||||
<string name="track_speed_notification">Velocidade: %1$s</string>
|
||||
<string name="about_third_party_data">Datos de terceiros alleos</string>
|
||||
<string name="about_third_party_data">Datos de terceiras partes</string>
|
||||
<string name="menu_voice_rate">Velocidade da voz</string>
|
||||
<string name="track_delete_not_recording">Esta pista non se pode eliminar porque está a funcionar a gravación.</string>
|
||||
<string name="track_delete_not_recording">Esta pista non se pode eliminar porque aínda está gravando.</string>
|
||||
<string name="settings_sensor_cycling_distance_speed">Distancia e Velocidade</string>
|
||||
<string name="settings_sensor_cycling_cadence">Cadencia</string>
|
||||
<string name="settings_sensor_running_speed_and_cadence">Distancia, Velocidade e Cadencia</string>
|
||||
@@ -453,16 +451,16 @@ Se o GPS informa sobre datos pouco precisos (ex.: localización, velocidade, ele
|
||||
<string name="permission_bluetooth_failed_rejected">Tes que permitirlle a OpenTracks nos axustes do sistema que acceda a Dispositivos Próximos.</string>
|
||||
<string name="value_float_kilometer_hour">%1$.1f km/h</string>
|
||||
<string name="value_float_mile_hour">%1$.1f km/h</string>
|
||||
<string name="value_float_mile_hour_recommended">%1$.1f km/h (recomendado)</string>
|
||||
<string name="value_float_mile_hour_recommended">%1$.1f mi/h (recomendado)</string>
|
||||
<string name="value_float_kilometer_hour_recommended">%1$.1f km/h (recomendado)</string>
|
||||
<string name="stats_clock">Reloxo</string>
|
||||
<string name="settings_defaults_summary">Unidades preferidas, Actividade por defecto, Nome da ruta</string>
|
||||
<string name="settings_announcements_summary">A Distancia, Tempo, Voz Velocidade</string>
|
||||
<string name="settings_announcements_summary">Distancia, Tempo, Velocidade da voz</string>
|
||||
<string name="settings_defaults_title">Unidades e Actividade por defecto</string>
|
||||
<string name="settings_ui_title">Interface da app</string>
|
||||
<string name="settings_ui_summary">Aparencia, Gravación, Deseño, Tema</string>
|
||||
<string name="settings_ui_summary">Aparencia, Disposición na gravación, Decorado</string>
|
||||
<string name="settings_gps_sensor">GPS</string>
|
||||
<string name="settings_gps_summary">Intervalo de tempo, Distancia intervalo, Max Distancia, Precisión</string>
|
||||
<string name="settings_gps_summary">Intervalo de tempo, Intervalo de distancia, Max Distancia, Precisión</string>
|
||||
<string name="settings_sensors_title">Sensores</string>
|
||||
<string name="settings_sensors_summary">Sensores de ciclismo, Circunferencia da roda, Sensores de carreira</string>
|
||||
<string name="settings_announcements_statistics_title">Indicacións por Voz</string>
|
||||
@@ -470,7 +468,7 @@ Se o GPS informa sobre datos pouco precisos (ex.: localización, velocidade, ele
|
||||
<string name="settings_import_export_summary">Importación, Exportación, Auto Exportación</string>
|
||||
<string name="settings_units_category">Unidades</string>
|
||||
<string name="settings_activity_defaults_category">Valores por defecto</string>
|
||||
<string name="settings_reset_summary">Restablecer Axustes dos Valores</string>
|
||||
<string name="settings_reset_summary">Restablecer Axustes por defecto</string>
|
||||
<string name="menu_select_layout">Elixe disposición</string>
|
||||
<string name="custom_layout_list_title">Disposicións</string>
|
||||
<string name="custom_layout_list_edit_name">Escribe o nome da nova disposición</string>
|
||||
@@ -508,7 +506,7 @@ Se o GPS informa sobre datos pouco precisos (ex.: localización, velocidade, ele
|
||||
<string name="speed">Vel. media en movemento</string>
|
||||
<string name="average_heart_rate">Pulso medio</string>
|
||||
<string name="current_heart_rate">Pulso actual</string>
|
||||
<string name="activity_type_swimming">nadando</string>
|
||||
<string name="activity_type_swimming">nadar</string>
|
||||
<string name="activity_type_swimming_open">nadar en augas abertas</string>
|
||||
<string name="activity_type_workout">adestramento</string>
|
||||
<string name="generic_dismiss">Desbotar</string>
|
||||
@@ -517,14 +515,14 @@ Se o GPS informa sobre datos pouco precisos (ex.: localización, velocidade, ele
|
||||
<string name="settings_export_filename_title">Formato do nome do ficheiro</string>
|
||||
<string name="settings_announcements_lap_heart_rate">Pulso durante a volta</string>
|
||||
<string name="settings_announcements_average_heart_rate">Pulso cardíaco medio</string>
|
||||
<string name="image_discard">Descartar</string>
|
||||
<string name="image_discard">Desbotar</string>
|
||||
<string name="settings_announcements_average_speed_pace">Velocidade/ritmo medio</string>
|
||||
<string name="settings_announcements_lap_speed_pace">Velocidade/ritmo da volta</string>
|
||||
<string name="track_discarding">Desbotando Ruta</string>
|
||||
<string name="track_discarding">Desbotando a Ruta</string>
|
||||
<string name="share_crash_report">Comparte o informe do fallo</string>
|
||||
<string name="description_pace_nautical">Ritmo (min/NM)</string>
|
||||
<string name="description_speed_nautical">Velocidade (nós)</string>
|
||||
<string name="settings_stats_units_nautical">Náutica (NM, ft)</string>
|
||||
<string name="settings_stats_units_nautical">Náutico (NM, ft)</string>
|
||||
<string name="unit_nautical_mile">NM</string>
|
||||
<string name="unit_knots">kn</string>
|
||||
<string name="unit_minute_per_nautical_mile">min/NM</string>
|
||||
@@ -532,7 +530,7 @@ Se o GPS informa sobre datos pouco precisos (ex.: localización, velocidade, ele
|
||||
<string name="value_float_knots">%1$.1f nós</string>
|
||||
<string name="value_float_knots_recommended">%1$.1f nós (recomendado)</string>
|
||||
<string name="voice_per_nautical_mile">por milla náutica</string>
|
||||
<string name="show_on_dashboard_not_installed">Non hai ningunha aplicación instalada con soporte para a API de OpenTracks.</string>
|
||||
<string name="show_on_dashboard_not_installed">Non hai ningunha aplicación instalada compatible coa API de OpenTracks.</string>
|
||||
<string name="always">Sempre</string>
|
||||
<string name="error_crash_title">%1$s fallou</string>
|
||||
<string name="report_bug">Informar do problema</string>
|
||||
@@ -544,9 +542,9 @@ Se o GPS informa sobre datos pouco precisos (ex.: localización, velocidade, ele
|
||||
<string name="settings_api_summary">Control remoto e API de estatísticas para outras apps</string>
|
||||
<string name="voice_on_device_speaker_title">Usar o altofalante do dispositivo</string>
|
||||
<string name="menu_export_all_one_file">Exportar todo (un ficheiro con todas as rutas)</string>
|
||||
<string name="no_compatible_gallery_installed">Non hai unha aplicación de galería compatible instalada</string>
|
||||
<string name="no_compatible_gallery_installed">Non hai unha aplicación de galería compatible dispoñible</string>
|
||||
<string name="settings_recording_min_required_accuracy_summary">Ignórase a localización se a exactitude é maior de %1$s</string>
|
||||
<string name="no_compatible_camera_installed">Non hai unha aplicación de cámara compatible</string>
|
||||
<string name="no_compatible_camera_installed">Non hai unha aplicación de cámara compatible dispoñible</string>
|
||||
<string name="settings_sensor_bluetooth_service_filter_title">Filtrar Dispositivos Bluetooth</string>
|
||||
<string name="settings_sensor_bluetooth_service_filter_off">Mostrar todos os dispositivos Bluetooth dispoñibles</string>
|
||||
<string name="settings_stats_units_imperial_meter">Imperial (mi, m)</string>
|
||||
@@ -559,16 +557,16 @@ Se o GPS informa sobre datos pouco precisos (ex.: localización, velocidade, ele
|
||||
<string name="voiceSecondsPlural">{n, plural, =1 {1 segundo} other {{n,number} segundos} }</string>
|
||||
<string name="voiceSpeedMilesPerHourPlural">{n, plural, =1 {1 milla por hora} other {{n,number,#.0} millas por hora} }</string>
|
||||
<string name="voiceDistanceNauticalMilesPlural">{n, plural, =1 {1 milla náutica} other {{n,number,#.0} millas náuticas} }</string>
|
||||
<string name="sensor_state_cadence_unit">ppm</string>
|
||||
<string name="sensor_state_cadence_unit">rpm</string>
|
||||
<string name="sensor_state_heart_rate_unit">lpm</string>
|
||||
<string name="sensor_state_power_unit">W</string>
|
||||
<string name="show_on_dashboard">Taboleiro da API OpenTracks</string>
|
||||
<string name="sensor_state_power_max">Potencia Máx</string>
|
||||
<string name="activity_type_kickscooter">vespino</string>
|
||||
<string name="settings_announcements_idle_category_title">Anuncios de Parada</string>
|
||||
<string name="voiceIdle">Deterse.</string>
|
||||
<string name="voiceIdle">Deténdose.</string>
|
||||
<string name="settings_announcements_idle_title">Parada</string>
|
||||
<string name="settings_recording_idle_timeout_title">Límite da parada</string>
|
||||
<string name="settings_recording_idle_timeout_title">Limiar da parada</string>
|
||||
<string name="value_int_seconds">%1$d s (recomendado)</string>
|
||||
<string name="settings_ui_dynamic_colors_title">Cores Dinámicas</string>
|
||||
<string name="settings_recording_title">Ao gravar</string>
|
||||
@@ -590,19 +588,22 @@ Se o GPS informa sobre datos pouco precisos (ex.: localización, velocidade, ele
|
||||
<string name="create_marker_error">Non se creou o marcador. Inténtao outra vez.</string>
|
||||
<string name="settings_locale_title">Idioma</string>
|
||||
<string name="settings_locale_system_default">Predeterminado no sistema</string>
|
||||
<string name="stats_gain_loss">Ganancia e Perda</string>
|
||||
<string name="stats_gain_loss">Gañancia e Perda</string>
|
||||
<string name="lap_heart_rate">Pulso medio na volta</string>
|
||||
<string name="settings_chart_display_elevation_title">Mostrar elevación</string>
|
||||
<string name="settings_chart_display_elevation_title">Mostrar altitude</string>
|
||||
<string name="settings_chart_display_pace_or_speed_title">Mostrar velocidade ou ritmo</string>
|
||||
<string name="settings_chart_display_heart_rate_title">Mostrar ritmo cardíaco</string>
|
||||
<string name="settings_chart_display_heart_rate_title">Mostrar frecuencia cardíaca</string>
|
||||
<string name="settings_charts_title">Gráficos</string>
|
||||
<string name="settings_recording_show_gps_disabled_warning_title">Aviso do GPS desactivado</string>
|
||||
<string name="settings_recording_show_gps_disabled_warning_summary">Ao gravar, mostrar aviso se o GPS está desactivado.</string>
|
||||
<string name="marker_detail_title">%1$d de %2$d: %3$s</string>
|
||||
<string name="track_edit_time_offset">Diferenza de Zona horaria</string>
|
||||
<string name="settings_night_mode_option_system_dynamic">Sistema (DynamicColor)</string>
|
||||
<string name="settings_night_mode_option_day_dynamic">Día (DynamicColor)</string>
|
||||
<string name="settings_night_mode_option_night_dynamic">Noite (DynamicColor)</string>
|
||||
<string name="settings_night_mode_option_night_oled_dynamic">para OLED (DynamicColor)</string>
|
||||
<string name="settings_night_mode_option_system_dynamic">Sistema (Cores Dinámicas)</string>
|
||||
<string name="settings_night_mode_option_day_dynamic">Día (Cores Dinámicas)</string>
|
||||
<string name="settings_night_mode_option_night_dynamic">Noite (Cores Dinámicas)</string>
|
||||
<string name="settings_night_mode_option_night_oled_dynamic">para OLED (Cores Dinámicas)</string>
|
||||
<string name="voice_announce_lap_power_key">voiceAnnounceLapPower</string>
|
||||
<string name="settings_announcements_segment_average_speed_pace">Velocidade/Ritmo medios (Tramo)</string>
|
||||
<string name="stats_average_segment_pace">Ritmo medio no Tramo</string>
|
||||
<string name="stats_elapsed_segment_time">Tempo transcurrido no tramo</string>
|
||||
</resources>
|
||||
|
||||
@@ -621,4 +621,6 @@ Infatti, un\'applicazione deve supportare <i>ACTION_VIEW</i> con MIME <i>applica
|
||||
<string name="settings_night_mode_option_night_dynamic">Notte (colore dinamico)</string>
|
||||
<string name="settings_night_mode_option_night_oled_dynamic">Adatto per OLED (colore dinamico)</string>
|
||||
<string name="voiceIdle">Passaggio a inattivo.</string>
|
||||
<string name="settings_announcements_segment_average_speed_pace">Velocità/Ritmo medi (segmento)</string>
|
||||
<string name="stats_average_segment_pace">Ritmo medio nel segmento</string>
|
||||
</resources>
|
||||
|
||||
@@ -506,7 +506,7 @@ limitations under the License.
|
||||
<string name="error_crash_title">%1$s krasjet</string>
|
||||
<string name="share_crash_report">Del krasjrapport</string>
|
||||
<string name="report_bug">Rapporter feil</string>
|
||||
<string name="show_on_dashboard">OpenTracks-API for oversikten</string>
|
||||
<string name="show_on_dashboard">OpenTracks dashbord API</string>
|
||||
<string name="always">Alltid</string>
|
||||
<string name="settings_announcements_average_heart_rate">Gjennomsnittlig puls</string>
|
||||
<string name="settings_announcements_lap_heart_rate">Runde-puls</string>
|
||||
@@ -585,4 +585,6 @@ limitations under the License.
|
||||
<string name="settings_recording_show_gps_disabled_warning_summary">Vis en advarsel hvis GPS er avslått under opptak.</string>
|
||||
<string name="marker_detail_title">%1$d av %2$d: %3$s</string>
|
||||
<string name="track_edit_time_offset">Tidssoneforskyvning</string>
|
||||
<string name="stats_average_segment_pace">Gjen. segmenttempo</string>
|
||||
<string name="settings_announcements_segment_average_speed_pace">Gjennomsnittlig hastighet/tempo (Segment)</string>
|
||||
</resources>
|
||||
|
||||
@@ -573,4 +573,21 @@ limitations under the License.
|
||||
<string name="settings_sensor_bluetooth_service_filter_off">Pokaż wszystkie urządzenia bluetooth</string>
|
||||
<string name="settings_public_api_enabled_title">Publiczne API</string>
|
||||
<string name="settings_announcements_idle_category_title">Powiadomienia o przerwach</string>
|
||||
<string name="pace">Tempo</string>
|
||||
<string name="always">Zawsze</string>
|
||||
<string name="settings_night_mode_option_system_dynamic">Systemowy (dynamiczne kolory)</string>
|
||||
<string name="settings_night_mode_option_day_dynamic">Dzień (dynamiczne kolory)</string>
|
||||
<string name="settings_night_mode_option_night_dynamic">Noc (dynamiczne kolory)</string>
|
||||
<string name="track_edit_time_offset">Przesunięcie strefy czasowej</string>
|
||||
<string name="voice_per_kilometer">na kilometr</string>
|
||||
<string name="voice_per_mile">na milę</string>
|
||||
<string name="lap_time">Czas okrążenia</string>
|
||||
<string name="total_distance">Całkowity dystans</string>
|
||||
<string name="report_bug">Zgłoś błąd</string>
|
||||
<string name="settings_recording_show_gps_disabled_warning_title">Ostrzeżenie o wyłączonym GPS</string>
|
||||
<string name="settings_night_mode_option_night_oled_dynamic">Przyjazne OLED (dynamiczne kolory)</string>
|
||||
<string name="speed">Średnia prędkość poruszania się</string>
|
||||
<string name="show_on_dashboard">API panelu OpenTracks</string>
|
||||
<string name="marker_detail_title">%1$d z %2$d: %3$s</string>
|
||||
<string name="voice_on_device_speaker_title">Użyj wbudowanego głośnika</string>
|
||||
</resources>
|
||||
|
||||
@@ -122,8 +122,8 @@ limitations under the License.
|
||||
<string name="generic_tracks">Шляхи</string>
|
||||
<!-- Gps -->
|
||||
<string name="gps_starting">Запуск GPS</string>
|
||||
<string name="gps_wait_for_better_signal">Очікується кращий GPS-сигналу</string>
|
||||
<string name="gps_wait_for_signal">Очікується GPS-сигнал</string>
|
||||
<string name="gps_wait_for_better_signal">Очікування кращого сигналу GPS</string>
|
||||
<string name="gps_wait_for_signal">Очікування сигналу GPS</string>
|
||||
<!-- Image -->
|
||||
<string name="image_arrow">Стрілка</string>
|
||||
<string name="image_marker_count">Кількість маркерів</string>
|
||||
@@ -134,9 +134,9 @@ limitations under the License.
|
||||
<!-- Import -->
|
||||
<string name="import_progress_message">Імпорт із %1$s…</string>
|
||||
<!-- Marker -->
|
||||
<string name="marker_add_error">Неможливо вставити маркер, оскільки відсутній сигнал GPS. Повторіть спробу.</string>
|
||||
<string name="marker_add_error">Неможливо вставити маркер, оскільки відсутній сигнал GPS. Спробуйте ще раз.</string>
|
||||
<string name="marker_add_success">Маркер вставлено</string>
|
||||
<string name="marker_add_canceled">Скасовано, не додано маркер</string>
|
||||
<string name="marker_add_canceled">Скасовано, маркер не додано</string>
|
||||
<string name="marker_delete_one_confirm_message">Маркер буде назавжди видалено з пам’яті пристрою</string>
|
||||
<string name="marker_delete_one_confirm_title">Видалити маркер?</string>
|
||||
<string name="marker_delete_multiple_confirm_message">Вибрані маркери буде назавжди видалено з пам’яті пристрою</string>
|
||||
@@ -195,9 +195,7 @@ limitations under the License.
|
||||
<string name="settings_stats_units_metric">Метрична (км, м)</string>
|
||||
<string name="settings_stats_units_title">Одиниці вимірювання</string>
|
||||
<!-- Share Track -->
|
||||
<string name="share_track_share_file_body">Цей шлях може вас зацікавити.
|
||||
\n
|
||||
\n%1$s</string>
|
||||
<string name="share_track_share_file_body">Гадаю, цей маршрут може вас зацікавити. \n \n%1$s</string>
|
||||
<string name="share_track_subject">Я хочу поділитися з вами інформацією про шлях</string>
|
||||
<!-- Stats -->
|
||||
<string name="stats_average_moving_pace">Середній темп руху</string>
|
||||
@@ -215,7 +213,7 @@ limitations under the License.
|
||||
<string name="stats_total_time">Загальний час</string>
|
||||
<!-- Track -->
|
||||
<string name="track_name_format">Шлях %1$d</string>
|
||||
<string name="track_markers">Маркери шляху \"%1$s\"</string>
|
||||
<string name="track_markers">Маркери треку «%1$s»</string>
|
||||
<!-- Track Delete -->
|
||||
<string name="track_delete_one_confirm_message">Шлях і його маркери буде остаточно видалено з пристрою.</string>
|
||||
<string name="track_delete_one_confirm_title">Видалити шлях?</string>
|
||||
@@ -292,9 +290,9 @@ limitations under the License.
|
||||
<string name="waypoint_type_railway_crossing">залізничний переїзд</string>
|
||||
<string name="waypoint_type_restaurant">ресторан</string>
|
||||
<string name="waypoint_type_shopping">покупки</string>
|
||||
<string name="waypoint_type_sight">місце</string>
|
||||
<string name="waypoint_type_sight">пам’ятка</string>
|
||||
<string name="waypoint_type_store">магазин</string>
|
||||
<string name="waypoint_type_swimming_pool">плавальний басейн</string>
|
||||
<string name="waypoint_type_swimming_pool">басейн</string>
|
||||
<string name="waypoint_type_telephone">телефон</string>
|
||||
<string name="waypoint_type_theater">театр</string>
|
||||
<string name="waypoint_type_toilet">туалет</string>
|
||||
@@ -313,7 +311,7 @@ limitations under the License.
|
||||
<string name="track_recording_notification_accuracy">Точність місцеположення: %1$s</string>
|
||||
<string name="track_detail_intervals_tab">Інтервали</string>
|
||||
<string name="interval_list_empty_message">Ще немає інтервалу</string>
|
||||
<string name="share_image_body">Думаю, вам може бути цікаво це зображення.</string>
|
||||
<string name="share_image_body">Гадаю, це зображення може вас зацікавити.</string>
|
||||
<string name="instant_export_enabled_summary">Експорт треку в сховище після завершення запису</string>
|
||||
<string name="instant_export_enabled_title">Миттєвий експорт після тренування</string>
|
||||
<string name="help_gps_troubleshooting_content"> Що ви можете зробити:
|
||||
@@ -323,19 +321,19 @@ limitations under the License.
|
||||
\n d) вимкніть енергоощадження.
|
||||
\n
|
||||
\n Якщо нічого не допомагає, будь ласка, повідомте нам про помилку. </string>
|
||||
<string name="about_preference_title">Про</string>
|
||||
<string name="about_url">Веб-сторінка проекту: <a href="%1$s">%1$s</a></string>
|
||||
<string name="about_preference_title">Про OpenTracks</string>
|
||||
<string name="about_url">Вебсторінка проєкту: <a href="%1$s">%1$s</a></string>
|
||||
<string name="about_version_name">Версія: %1$s</string>
|
||||
<string name="about_version_code">Код версії: %1$d</string>
|
||||
<string name="about_libraries">Бібліотеки</string>
|
||||
<string name="help_feature_title">OpenTracks має такі особливості:</string>
|
||||
<string name="help_storage_pictures_title">Як OpenTracks зберігає зображення зроблені за допомогою маркувань\?</string>
|
||||
<string name="help_storage_pictures_content">Вони зберігаються у внутрішній пам\'яті застосунку (тобто, доступні тільки для OpenTracks).</string>
|
||||
<string name="help_data_google_play_services_title">Чи потрібен/використовує OpenTrack Google Play Service\?</string>
|
||||
<string name="help_feature_title">OpenTracks забезпечує такі можливості:</string>
|
||||
<string name="help_storage_pictures_title">Як OpenTracks зберігає зображення, використані в якості маркерів?</string>
|
||||
<string name="help_storage_pictures_content">Вони зберігаються у внутрішній пам’яті застосунку (тобто, доступні тільки для OpenTracks).</string>
|
||||
<string name="help_data_google_play_services_title">Чи потребує/використовує OpenTracks службу Google Play?</string>
|
||||
<string name="help_track_map_title">Як показати трек на мапі\?</string>
|
||||
<string name="help_data_storage_title">Як OpenTracks зберігає дані з датчиків\?</string>
|
||||
<string name="help_recording_title">Як OpenTracks записує дані\?</string>
|
||||
<string name="help_track_export_title">Як я можу експортувати/поширювати записані треки\?</string>
|
||||
<string name="help_track_export_title">Як я можу експортувати записані треки і ділитися ними?</string>
|
||||
<string name="settings_night_mode_title">Тема UI</string>
|
||||
<string name="help_gps_troubleshooting_title">OpenTracks не записує моє місцеположення\?</string>
|
||||
<string name="help_support_title">Як я можу підтримати OpenTracks\?</string>
|
||||
@@ -370,22 +368,17 @@ limitations under the License.
|
||||
<string name="import_parser_error">Помилка парсера: %1$s</string>
|
||||
<string name="import_no_kml_file_found">Файл kml не знайдено в KMZ</string>
|
||||
<string name="import_error_list_dialog_title">Файли не імпортовано</string>
|
||||
<string name="import_error_info">%1$s:%2$s</string>
|
||||
<string name="marker_add_photo_canceled">Скасовано, фото-маркування не додано</string>
|
||||
<string name="import_error_info">%1$s: %2$s</string>
|
||||
<string name="marker_add_photo_canceled">Скасовано, фотомаркер не додано</string>
|
||||
<string name="menu_insert_gallery_img">Додати зображення з галереї</string>
|
||||
<string name="menu_resume_track">Відновити трек</string>
|
||||
<string name="permission_gps_failed">OpenTracks потребує дозволу на використання GPS.</string>
|
||||
<string name="settings_night_mode_option_system">Системна</string>
|
||||
<string name="about_description"><i>OpenTracks</i> - це застосунок для відстеження спортивної діяльності, який поважає вашу приватність.</string>
|
||||
<string name="about_history">OpenTracks це відгалуження застосунку <a href="https://code.google.com/archive/p/mytracks/"><i>Google\'s MyTracks</i></a> (закритого в 2016р).</string>
|
||||
<string name="help_feature_content">* створення GPS треків
|
||||
\n* використання датчика серцевого ритму (тільки Bluetooth LE)
|
||||
\n* голосовий путівник (за часом або відстанню)
|
||||
\n* створення маркувань (включаючи зображення)
|
||||
\n* повноцінний автономний режим
|
||||
\n* відображення треків на мапі (див. нижче)</string>
|
||||
<string name="help_track_export_content"> Треки можна експортувати з налаштувань у форматах KMZ, KML або GPX. KMZ/KML всі дані, а GPX тільки розташування. Треки, опубліковані за допомогою <i>значка поширення</i>, експортуються як KMZ. </string>
|
||||
<string name="help_recording_content">OpenTracks використовує GPS датчик пристрою для безперервного запису місцезнаходження. Якщо GPS пристрій повідомляє неточні дані (наприклад, місцеперебування, швидкість, висота), OpenTracks нічого не може з цим вдіяти.</string>
|
||||
<string name="about_description"><i>OpenTracks</i> — застосунок для відстеження спортивної діяльності, який поважає вашу конфіденційність.</string>
|
||||
<string name="about_history">OpenTracks — це відгалуження застосунку <a href="https://code.google.com/archive/p/mytracks/"><i>Google My Tracks</i></a>, закритого в 2016 р.</string>
|
||||
<string name="help_feature_content">* створення GPS-треків\n* використання датчика серцевого ритму (тільки Bluetooth LE)\n* голосові сповіщення (за часом або відстанню)\n* створення маркерів (у т.ч. із зображеннями)\n* повноцінний автономний режим\n* відображення треків на мапі (див. нижче)</string>
|
||||
<string name="help_track_export_content">Треки можна експортувати з Налаштувань у форматах KMZ, KML або GPX (KMZ/KML — усі дані, а GPX — тільки розташування). При використанні <i>значка «Поділитися»</i> треки експортуються у форматі KMZ.</string>
|
||||
<string name="help_recording_content">OpenTracks використовує GPS-датчик пристрою для безперервного запису місцезнаходження. Якщо GPS-пристрій повідомляє неточні дані (наприклад, місцезнаходження, швидкість, висоту), OpenTracks нічого не може з цим вдіяти.</string>
|
||||
<string name="help_data_storage_content"> Дані датчика зиписуються в кожному зареєстрованому місці. Що стосується датчика частоти серцевих скорочень для кожного місцезнаходження зберігається тільки <i>одне</i> вимірювання частоти. Якщо ви зупинилися або перебуваєте в приміщенні, дані датчика не будуть записані (але будуть відображатися). </string>
|
||||
<string name="help_support_content"> Ви можете:
|
||||
\n а) поширювати інформацію про OpenTracks,
|
||||
@@ -407,7 +400,7 @@ limitations under the License.
|
||||
<string name="settings_default_trackfileformat">Формат файла експорту/обміну</string>
|
||||
<string name="settings_recording_show_on_lockscreen_while_recording_summary">Під час запису показувати статистику без розблокування пристрою.</string>
|
||||
<string name="stats_loss">Втрачено</string>
|
||||
<string name="share_image_subject">Я хотів би поділитися з вами зображенням</string>
|
||||
<string name="share_image_subject">Я хочу поділитися з вами зображенням</string>
|
||||
<string name="settings_default_export_uri_title">Каталог експорту треків</string>
|
||||
<string name="help_track_map_content"> Використовуйте маленьку піктограму мапи.
|
||||
\n a) (оновлення в реальному часі) Встановіть застосунок <a href="https://codeberg.org/OpenTracksApp/OSMDashboard">OSMDashboard</a>. Він дозволяє дивитися карти онлайн чи офлайн завдяки <a href="https://www.openstreetmap.org">OpenStreetMap</a>.
|
||||
@@ -461,8 +454,8 @@ limitations under the License.
|
||||
<string name="stats_custom_layout_default_layout">Макет за замовчуванням</string>
|
||||
<string name="custom_layout_select_layout">Виберіть макет</string>
|
||||
<string name="custom_layout_list_layout_removed">Макет видалено</string>
|
||||
<string name="custom_layout_list_edit_already_exists">Ім\'я макета вже існує</string>
|
||||
<string name="custom_layout_list_edit_name">Введіть ім\'я макета для додавання</string>
|
||||
<string name="custom_layout_list_edit_already_exists">Назва макета вже існує</string>
|
||||
<string name="custom_layout_list_edit_name">Уведіть назву макета для додавання</string>
|
||||
<string name="settings_announcements_distance_frequency">Довжина кола</string>
|
||||
<string name="settings_announcements_totaltime_frequency">Часовий інтервал</string>
|
||||
<string name="settings_running_sensor">Біг</string>
|
||||
@@ -582,4 +575,11 @@ limitations under the License.
|
||||
<string name="voiceDistance">{n, plural, =1 {1 } other {{n,number,#.0}} }</string>
|
||||
<string name="power_x_watt">Потужність {x, plural, =1 {1 ват} few {# вати} other {# ват} }</string>
|
||||
<string name="error_crash_title">%1$s аварійно завершив роботу</string>
|
||||
<string name="settings_announcements_segment_average_speed_pace">Середня швидкість/темп (відрізок)</string>
|
||||
<string name="stats_average_segment_pace">Середній темп відрізка</string>
|
||||
<string name="share_crash_report">Надіcлати звіт про збій</string>
|
||||
<string name="lap_heart_rate">Пульс за коло</string>
|
||||
<string name="settings_announcements_lap_power">Потужність за коло</string>
|
||||
<string name="bluetooth_sensor_summary">%1$s (%2$s)</string>
|
||||
<string name="create_marker_error">Неможливо створити маркер. Спробуйте ще раз.</string>
|
||||
</resources>
|
||||
|
||||
@@ -589,8 +589,8 @@ limitations under the License.
|
||||
<string name="settings_recording_min_required_accuracy_summary">精度不足 %1$s 的位置会被忽略</string>
|
||||
<string name="settings_sensor_heart_rate_max">心率区间:最大心率(bpm)</string>
|
||||
<string name="settings_api_summary">给其他应用使用的远程控制和统计 API</string>
|
||||
<string name="sensor_state_cadence_unit">rpm</string>
|
||||
<string name="sensor_state_heart_rate_unit">bpm</string>
|
||||
<string name="sensor_state_cadence_unit">RPM</string>
|
||||
<string name="sensor_state_heart_rate_unit">BPM</string>
|
||||
<string name="sensor_state_power_unit">瓦</string>
|
||||
<string name="settings_sensor_bluetooth_service_filter_off">显示所有可发现的蓝牙设备</string>
|
||||
<string name="help_data_google_play_services_content">不使用</string>
|
||||
@@ -636,4 +636,7 @@ limitations under the License.
|
||||
<string name="settings_night_mode_option_night_dynamic">夜间模式(动态配色)</string>
|
||||
<string name="settings_night_mode_option_night_oled_dynamic">OLED 优化模式(动态配色)</string>
|
||||
<string name="track_edit_time_offset">时区偏移</string>
|
||||
<string name="settings_announcements_segment_average_speed_pace">平均速度/配速(路段)</string>
|
||||
<string name="stats_average_segment_pace">平均路段配速</string>
|
||||
<string name="stats_elapsed_segment_time">已用分段时间</string>
|
||||
</resources>
|
||||
|
||||
@@ -356,6 +356,7 @@
|
||||
<string name="stats_custom_layout_average_moving_pace_key" translatable="false">average_moving_pace</string>
|
||||
<string name="stats_custom_layout_average_pace_key" translatable="false">average_pace</string>
|
||||
<string name="stats_custom_layout_average_segment_pace_key" translatable="false">average_segment_pace</string>
|
||||
<string name="stats_custom_layout_elapsed_segment_time_key" translatable="false">elapsed_segment_time</string>
|
||||
<string name="stats_custom_layout_fastest_pace_key" translatable="false">fastest_pace</string>
|
||||
<string name="stats_custom_layout_coordinates_key" translatable="false">coordinates</string>
|
||||
<string name="stats_custom_layout_heart_rate_key" translatable="false">heart_rate</string>
|
||||
@@ -427,26 +428,30 @@
|
||||
<item>0,0,0</item>
|
||||
</string-array>
|
||||
<string-array name="stats_custom_layout_fields_default_value_14">
|
||||
<item>@string/stats_custom_layout_fastest_pace_key</item>
|
||||
<item>@string/stats_custom_layout_elapsed_segment_time_key</item>
|
||||
<item>0,0,0</item>
|
||||
</string-array>
|
||||
<string-array name="stats_custom_layout_fields_default_value_15">
|
||||
<item>@string/stats_custom_layout_fastest_pace_key</item>
|
||||
<item>0,0,0</item>
|
||||
</string-array>
|
||||
<string-array name="stats_custom_layout_fields_default_value_16">
|
||||
<item>@string/stats_custom_layout_coordinates_key</item>
|
||||
<item>0,0,1</item>
|
||||
</string-array>
|
||||
<string-array name="stats_custom_layout_fields_default_value_16">
|
||||
<string-array name="stats_custom_layout_fields_default_value_17">
|
||||
<item>@string/stats_custom_layout_heart_rate_key</item>
|
||||
<item>0,0,0</item>
|
||||
</string-array>
|
||||
<string-array name="stats_custom_layout_fields_default_value_17">
|
||||
<string-array name="stats_custom_layout_fields_default_value_18">
|
||||
<item>@string/stats_custom_layout_cadence_key</item>
|
||||
<item>0,0,0</item>
|
||||
</string-array>
|
||||
<string-array name="stats_custom_layout_fields_default_value_18">
|
||||
<string-array name="stats_custom_layout_fields_default_value_19">
|
||||
<item>@string/stats_custom_layout_power_key</item>
|
||||
<item>0,0,0</item>
|
||||
</string-array>
|
||||
<string-array name="stats_custom_layout_fields_default_value_19">
|
||||
<string-array name="stats_custom_layout_fields_default_value_20">
|
||||
<item>@string/stats_custom_layout_clock_key</item>
|
||||
<item>0,0,0</item>
|
||||
</string-array>
|
||||
|
||||
@@ -455,6 +455,7 @@ limitations under the License.
|
||||
<string name="stats_average_moving_speed">Avg moving speed</string>
|
||||
<string name="stats_average_pace">Avg pace</string>
|
||||
<string name="stats_average_segment_pace">Avg Segment pace</string>
|
||||
<string name="stats_elapsed_segment_time">Elapsed Segment time</string>
|
||||
<string name="stats_average_speed">Avg speed</string>
|
||||
<string name="stats_altitude">Elevation</string>
|
||||
<string name="stats_fastest_pace">Fastest pace</string>
|
||||
|
||||
Reference in New Issue
Block a user