Idle detection#1187
This commit is contained in:
Dennis Guse
2023-09-25 18:00:43 +02:00
committed by GitHub
59 changed files with 567 additions and 781 deletions
@@ -1,33 +1,16 @@
package de.dennisguse.opentracks.data.model;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;
import org.junit.Test;
import java.time.Instant;
import java.time.temporal.ChronoUnit;
import de.dennisguse.opentracks.data.models.Distance;
import de.dennisguse.opentracks.data.models.TrackPoint;
public class TrackPointTest {
@Test
public void isRecent_true() {
TrackPoint tp = new TrackPoint(TrackPoint.Type.SEGMENT_END_MANUAL, Instant.now());
assertTrue(tp.isRecent());
}
@Test
public void isRecent_false() {
TrackPoint tp = new TrackPoint(TrackPoint.Type.SEGMENT_END_MANUAL, Instant.now().minus(2, ChronoUnit.MINUTES));
assertFalse(tp.isRecent());
}
@Test
public void distanceToPrevious() {
TrackPoint tp1 = new TrackPoint(TrackPoint.Type.TRACKPOINT, Instant.ofEpochMilli(0))
@@ -1,59 +0,0 @@
package de.dennisguse.opentracks.io.file.exporter;
import static org.junit.Assert.assertEquals;
import android.content.Context;
import androidx.test.core.app.ApplicationProvider;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.runners.JUnit4;
import java.io.ByteArrayOutputStream;
import java.time.Instant;
import java.time.ZoneOffset;
import de.dennisguse.opentracks.data.models.TrackPoint;
import de.dennisguse.opentracks.io.file.TrackFileFormat;
@RunWith(JUnit4.class)
public class KmlTrackExporterTest {
private final Context context = ApplicationProvider.getApplicationContext();
/**
* Sensor data by type should only be created if present in at least on TrackPoint.
*/
@Test
public void writeCloseSegment_only_write_sensordata_if_present() {
String expected = """
<when>1970-01-01T00:00:00Z</when>
<coord/>
<when>1970-01-01T01:00:00+01:00</when>
<coord/>
<ExtendedData>
<SchemaData schemaUrl="#schema">
</SchemaData>
</ExtendedData>
</Track>
""";
// given
TrackPoint trackPoint = new TrackPoint(TrackPoint.Type.SEGMENT_START_MANUAL, Instant.ofEpochSecond(0));
ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
KMLTrackExporter kmlTrackWriter = (KMLTrackExporter) TrackFileFormat.KML_WITH_TRACKDETAIL_AND_SENSORDATA.createTrackExporter(context, null);
kmlTrackWriter.prepare(outputStream);
kmlTrackWriter.writeTrackPoint(ZoneOffset.UTC, trackPoint);
kmlTrackWriter.writeTrackPoint(ZoneOffset.ofTotalSeconds(3600), trackPoint);
// when
kmlTrackWriter.writeCloseSegment();
kmlTrackWriter.close();
// then
assertEquals(expected, outputStream.toString());
}
}
@@ -35,6 +35,7 @@ import java.io.InputStreamReader;
import java.nio.charset.StandardCharsets;
import java.time.Duration;
import java.time.Instant;
import java.time.ZoneOffset;
import java.util.ArrayList;
import java.util.List;
import java.util.TimeZone;
@@ -168,9 +169,12 @@ public class ExportImportTest {
sendLocation(trackPointCreator, "2020-02-02T02:03:22Z", 3, 16, 10, 13, 15, 10, 0);
sendLocation(trackPointCreator, "2020-02-02T02:03:23Z", 3, 16.001, 10, 27, 15, 10, 0);
trackPointCreator.setClock("2020-02-02T02:03:30Z");
service.getTrackRecordingManager().onIdle();
trackPointCreator.setClock("2020-02-02T02:03:24Z");
sendLocation(trackPointCreator, "2020-02-02T02:03:50Z", 3, 16.001, 10, 27, 15, 10, 0);
trackPointCreator.setClock("2020-02-02T02:04:00Z");
service.endCurrentTrack();
Track track = contentProviderUtils.getTrack(trackId);
@@ -182,8 +186,103 @@ public class ExportImportTest {
track = contentProviderUtils.getTrack(trackId);
trackPoints = TestDataUtil.getTrackPoints(contentProviderUtils, trackId);
markers = contentProviderUtils.getMarkers(trackId);
assertEquals(12, trackPoints.size());
assertEquals(2, markers.size());
}
@LargeTest
@Test
public void track() throws TimeoutException {
setUp();
Track track = contentProviderUtils.getTrack(trackId);
TrackStatistics trackStatistics = track.getTrackStatistics();
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(Duration.ofSeconds(56), trackStatistics.getTotalTime());
assertEquals(Duration.ofSeconds(26), trackStatistics.getMovingTime()); //TODO Likely too low
// Distance
assertEquals(222125.53125, trackStatistics.getTotalDistance().toM(), 0.01); //TODO Too low
// Speed
assertEquals(8543.29, trackStatistics.getMaxSpeed().toMPS(), 0.01);
assertEquals(3966.52, trackStatistics.getAverageSpeed().toMPS(), 0.01);
assertEquals(8543.28, trackStatistics.getAverageMovingSpeed().toMPS(), 0.01);
// Altitude
assertEquals(10, trackStatistics.getMinAltitude(), 0.01);
assertEquals(10, trackStatistics.getMaxAltitude(), 0.01);
assertEquals(2, trackStatistics.getTotalAltitudeGain(), 0.01);
assertEquals(2, trackStatistics.getTotalAltitudeLoss(), 0.01);
List<TrackPoint> actual = TestDataUtil.getTrackPoints(contentProviderUtils, trackId);
new TrackPointAssert().assertEquals(List.of(
new TrackPoint(TrackPoint.Type.SEGMENT_START_MANUAL, Instant.parse("2020-02-02T02:02:02Z")),
new TrackPoint(TrackPoint.Type.TRACKPOINT, Instant.parse("2020-02-02T02:02:03Z"))
.setLatitude(3)
.setLongitude(14)
.setAltitude(10)
.setSpeed(Speed.of(15))
.setAltitudeLoss(1f)
.setAltitudeGain(1f)
.setHorizontalAccuracy(Distance.of(10)),
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),
new TrackPoint(TrackPoint.Type.TRACKPOINT, Instant.parse("2020-02-02T02:02:15Z"))
.setHeartRate(HeartRate.of(68))
.setCadence(3)
.setPower(50),
new TrackPoint(TrackPoint.Type.TRACKPOINT, Instant.parse("2020-02-02T02:02:17Z"))
.setLatitude(3)
.setLongitude(14.001)
.setAltitude(10)
.setSensorDistance(Distance.of(2))
.setSpeed(Speed.of(5))
.setAltitudeLoss(0f)
.setAltitudeGain(0f)
.setHorizontalAccuracy(Distance.of(10))
.setHeartRate(HeartRate.of(69))
.setCadence(3)
.setPower(50),
new TrackPoint(TrackPoint.Type.SEGMENT_END_MANUAL, Instant.parse("2020-02-02T02:02:18Z")),
new TrackPoint(TrackPoint.Type.SEGMENT_START_MANUAL, Instant.parse("2020-02-02T02:03:20Z")),
new TrackPoint(TrackPoint.Type.TRACKPOINT, Instant.parse("2020-02-02T02:03:21Z"))
.setLatitude(3)
.setLongitude(14.002)
.setAltitude(10)
.setSpeed(Speed.of(15))
.setAltitudeLoss(0f)
.setAltitudeGain(0f)
.setHorizontalAccuracy(Distance.of(10)),
new TrackPoint(TrackPoint.Type.SEGMENT_START_AUTOMATIC, Instant.parse("2020-02-02T02:03:22Z"))
.setLatitude(3)
.setLongitude(16)
.setAltitude(10)
.setSpeed(Speed.of(15))
.setAltitudeLoss(0f)
.setAltitudeGain(0f)
.setHorizontalAccuracy(Distance.of(10)),
new TrackPoint(TrackPoint.Type.IDLE, Instant.parse("2020-02-02T02:03:30Z")),
new TrackPoint(TrackPoint.Type.TRACKPOINT, Instant.parse("2020-02-02T02:03:50Z"))
.setLatitude(3)
.setLongitude(16.001)
.setAltitude(10)
.setSpeed(Speed.of(15))
.setAltitudeLoss(0f)
.setAltitudeGain(0f)
.setHorizontalAccuracy(Distance.of(10)),
new TrackPoint(TrackPoint.Type.SEGMENT_END_MANUAL, Instant.parse("2020-02-02T02:04:00Z"))
), actual);
}
//TODO Does not test marker images
@@ -225,43 +324,7 @@ public class ExportImportTest {
// Time
assertEquals(track.getZoneOffset(), importedTrack.getZoneOffset());
assertEquals(Instant.parse("2020-02-02T02:02:02Z"), importedTrackStatistics.getStartTime());
assertEquals(Instant.parse("2020-02-02T02:03:24Z"), importedTrackStatistics.getStopTime());
TrackStatistics originalTrackStatistics = track.getTrackStatistics();
assertEquals(originalTrackStatistics.getTotalTime(), importedTrackStatistics.getTotalTime());
assertEquals(Duration.ofSeconds(20), importedTrackStatistics.getTotalTime());
assertEquals(originalTrackStatistics.getMovingTime(), importedTrackStatistics.getMovingTime());
assertEquals(Duration.ofSeconds(4), importedTrackStatistics.getMovingTime());
// Distance
assertEquals(originalTrackStatistics.getTotalDistance(), importedTrackStatistics.getTotalDistance());
assertEquals(222238.70, importedTrackStatistics.getTotalDistance().toM(), 0.01);
// Speed
assertEquals(originalTrackStatistics.getMaxSpeed(), importedTrackStatistics.getMaxSpeed());
assertEquals(55559.67, importedTrackStatistics.getMaxSpeed().toMPS(), 0.01);
assertEquals(originalTrackStatistics.getAverageSpeed(), importedTrackStatistics.getAverageSpeed());
assertEquals(11111.93, importedTrackStatistics.getAverageSpeed().toMPS(), 0.01);
assertEquals(originalTrackStatistics.getAverageMovingSpeed(), importedTrackStatistics.getAverageMovingSpeed());
assertEquals(55559.67, importedTrackStatistics.getMaxSpeed().toMPS(), 0.01);
// Altitude
assertEquals(originalTrackStatistics.getMinAltitude(), importedTrackStatistics.getMinAltitude(), 0.01);
assertEquals(10, importedTrackStatistics.getMinAltitude(), 0.01);
assertEquals(originalTrackStatistics.getMaxAltitude(), importedTrackStatistics.getMaxAltitude(), 0.01);
assertEquals(10, importedTrackStatistics.getMaxAltitude(), 0.01);
assertEquals(originalTrackStatistics.getTotalAltitudeGain(), importedTrackStatistics.getTotalAltitudeGain(), 0.01);
assertEquals(2, importedTrackStatistics.getTotalAltitudeGain(), 0.01);
assertEquals(originalTrackStatistics.getTotalAltitudeLoss(), importedTrackStatistics.getTotalAltitudeLoss(), 0.01);
assertEquals(2, importedTrackStatistics.getTotalAltitudeLoss(), 0.01);
assertEquals(track.getTrackStatistics(), importedTrackStatistics);
// 4. markers
assertMarkers();
@@ -347,7 +410,7 @@ public class ExportImportTest {
.setSpeed(Speed.of(5))
.setAltitudeLoss(1f)
.setAltitudeGain(1f)
.setSensorDistance(Distance.of(14))
.setSensorDistance(Distance.of(12))
.setHeartRate(69)
.setPower(50f)
.setCadence(3f)
@@ -368,7 +431,7 @@ public class ExportImportTest {
.setAltitudeGain(0f)
.setSpeed(Speed.of(15))
.setHorizontalAccuracy(Distance.of(10)),
new TrackPoint(TrackPoint.Type.TRACKPOINT, Instant.parse("2020-02-02T02:03:23Z"))
new TrackPoint(TrackPoint.Type.TRACKPOINT, Instant.parse("2020-02-02T02:03:50Z"))
.setLatitude(3)
.setLongitude(16.001)
.setAltitude(10)
@@ -379,24 +442,23 @@ public class ExportImportTest {
), actual);
// 3. trackstatistics
TrackStatistics trackStatistics = track.getTrackStatistics();
TrackStatistics importedTrackStatistics = importedTrack.getTrackStatistics();
// Time
assertEquals(track.getZoneOffset(), importedTrack.getZoneOffset());
assertEquals(Instant.parse("2020-02-02T02:02:03Z"), importedTrackStatistics.getStartTime());
assertEquals(Instant.parse("2020-02-02T02:03:23Z"), importedTrackStatistics.getStopTime());
assertEquals(Instant.parse("2020-02-02T02:03:50Z"), importedTrackStatistics.getStopTime());
assertEquals(Duration.ofSeconds(80), importedTrackStatistics.getTotalTime());
assertEquals(Duration.ofSeconds(80), importedTrackStatistics.getMovingTime());
assertEquals(Duration.ofSeconds(107), importedTrackStatistics.getTotalTime());
assertEquals(Duration.ofSeconds(107), importedTrackStatistics.getMovingTime());
// Distance
assertEquals(222349.85, importedTrackStatistics.getTotalDistance().toM(), 0.01);
assertEquals(222347.85, importedTrackStatistics.getTotalDistance().toM(), 0.01);
// Speed
assertEquals(2779.37, importedTrackStatistics.getMaxSpeed().toMPS(), 0.01);
assertEquals(2779.37, importedTrackStatistics.getAverageSpeed().toMPS(), 0.01);
assertEquals(2779.37, importedTrackStatistics.getAverageMovingSpeed().toMPS(), 0.01);
assertEquals(2078.01, importedTrackStatistics.getMaxSpeed().toMPS(), 0.01);
assertEquals(2078.01, importedTrackStatistics.getAverageSpeed().toMPS(), 0.01);
assertEquals(2078.01, importedTrackStatistics.getAverageMovingSpeed().toMPS(), 0.01);
// Altitude
assertEquals(10, importedTrackStatistics.getMinAltitude(), 0.01);
@@ -487,14 +549,14 @@ public class ExportImportTest {
private void mockBLESensorData(TrackPointCreator trackPointCreator, Float speed, Distance distance, float heartRate, float cadence, Float power) {
SensorDataSet sensorDataSet = new SensorDataSet();
sensorDataSet.set(new SensorDataCyclingPower("power", "power", Power.of(power)));
sensorDataSet.set(new SensorDataHeartRate("heartRate", "heartRate", HeartRate.of(heartRate)));
SensorDataSet sensorDataSet = new SensorDataSet();
sensorDataSet.set(new SensorDataCyclingPower("power", "power", Power.of(power)));
sensorDataSet.set(new SensorDataHeartRate("heartRate", "heartRate", HeartRate.of(heartRate)));
SensorDataCyclingCadence cyclingCadence = Mockito.mock(SensorDataCyclingCadence.class);
Mockito.when(cyclingCadence.hasValue()).thenReturn(true);
Mockito.when(cyclingCadence.getValue()).thenReturn(Cadence.of(cadence));
sensorDataSet.set(cyclingCadence);
SensorDataCyclingCadence cyclingCadence = Mockito.mock(SensorDataCyclingCadence.class);
Mockito.when(cyclingCadence.hasValue()).thenReturn(true);
Mockito.when(cyclingCadence.getValue()).thenReturn(Cadence.of(cadence));
sensorDataSet.set(cyclingCadence);
if (distance != null && speed != null) {
SensorDataCyclingDistanceSpeed.Data distanceSpeedData = Mockito.mock(SensorDataCyclingDistanceSpeed.Data.class);
@@ -141,8 +141,8 @@ public class GPXTrackImporterTest {
// 3. trackstatistics
TrackStatistics trackStatistics = importedTrack.getTrackStatistics();
assertEquals(1.44, trackStatistics.getMaxSpeed().toMPS(), 0.01);
assertEquals(Duration.ofSeconds(53), trackStatistics.getMovingTime());
assertEquals(0.75, trackStatistics.getMaxSpeed().toMPS(), 0.01);
assertEquals(Duration.ofSeconds(101), trackStatistics.getMovingTime());
// 4. trackpoints
List<TrackPoint> importedTrackPoints = TestDataUtil.getTrackPoints(contentProviderUtils, importTrackId);
@@ -189,7 +189,7 @@ public class GPXTrackImporterTest {
// 3. trackstatistics
TrackStatistics trackStatistics = importedTrack.getTrackStatistics();
assertEquals(4.0, trackStatistics.getMaxSpeed().toMPS(), 0.01);
assertEquals(5.0, trackStatistics.getMaxSpeed().toMPS(), 0.01);
assertEquals(Duration.ofSeconds(101), trackStatistics.getMovingTime());
// 4. trackpoints
@@ -146,6 +146,7 @@ public class TrackRecordingServiceMarkerTest {
assertEquals(0.0, wpt.getLength().toM(), 0.01);
assertNotNull(wpt.getLocation());
trackPointCreator.setClock("2020-02-02T02:02:04Z");
service.endCurrentTrack();
}
}
@@ -88,7 +88,7 @@ public class TrackRecordingServiceRecordingTest {
contentProviderUtils = new ContentProviderUtils(context);
PreferencesUtils.setString(R.string.recording_distance_interval_key, R.string.recording_distance_interval_default);
PreferencesUtils.setString(R.string.idle_speed_key, R.string.idle_speed_default);
PreferencesUtils.setString(R.string.idle_duration_key, R.string.idle_duration_default);
service = startService();
}
@@ -122,7 +122,7 @@ public class TrackRecordingServiceRecordingTest {
service.endCurrentTrack();
// then
assertEquals(new TrackStatistics(startTime, stopTime, 0, 1, 0, 0, 0f, 0f)
assertEquals(new TrackStatistics(startTime, stopTime, 0, 1, 1, 0, 0f, 0f)
, contentProviderUtils.getTrack(trackId).getTrackStatistics());
new TrackPointAssert().assertEquals(List.of(
@@ -150,7 +150,7 @@ public class TrackRecordingServiceRecordingTest {
service.endCurrentTrack();
// then
assertEquals(new TrackStatistics(startTime, pauseTime, 0, 1, 0, 0, 0f, 0f)
assertEquals(new TrackStatistics(startTime, pauseTime, 0, 1, 1, 0, 0f, 0f)
, contentProviderUtils.getTrack(trackId).getTrackStatistics());
new TrackPointAssert().assertEquals(List.of(
@@ -166,7 +166,7 @@ public class TrackRecordingServiceRecordingTest {
service.resumeTrack(trackId);
// then
assertEquals(new TrackStatistics(startTime, resumeTime, 0, 1, 0, 0, 0f, 0f)
assertEquals(new TrackStatistics(startTime, resumeTime, 0, 1, 1, 0, 0f, 0f)
, contentProviderUtils.getTrack(trackId).getTrackStatistics());
new TrackPointAssert().assertEquals(List.of(
@@ -299,7 +299,7 @@ public class TrackRecordingServiceRecordingTest {
TrackRecordingServiceTestUtils.sendGPSLocation(trackPointCreator, gps1, 45.0, 35.0, 1, 15);
// then
assertEquals(new TrackStatistics(startTime, gps1, 0, 1, 0, 0, 0f, 0f)
assertEquals(new TrackStatistics(startTime, gps1, 0, 1, 1, 15, 0f, 0f)
, contentProviderUtils.getTrack(trackId).getTrackStatistics());
// when
@@ -307,7 +307,7 @@ public class TrackRecordingServiceRecordingTest {
TrackRecordingServiceTestUtils.sendGPSLocation(trackPointCreator, gps2, 45.0001, 35.0, 1, 15);
// then
assertEquals(new TrackStatistics(startTime, gps2, 11.113178253173828f, 4, 3, 15, 0f, 0f)
assertEquals(new TrackStatistics(startTime, gps2, 11.113178253173828f, 4, 4, 15, 0f, 0f)
, contentProviderUtils.getTrack(trackId).getTrackStatistics());
// when
@@ -315,7 +315,7 @@ public class TrackRecordingServiceRecordingTest {
TrackRecordingServiceTestUtils.sendGPSLocation(trackPointCreator, gps3, 45.0002, 35.0, 1, 15);
// then
assertEquals(new TrackStatistics(startTime, gps3, 22.226356506347656, 6, 5, 15, 0f, 0f)
assertEquals(new TrackStatistics(startTime, gps3, 22.226356506347656, 6, 6, 15, 0f, 0f)
, contentProviderUtils.getTrack(trackId).getTrackStatistics());
@@ -325,7 +325,7 @@ public class TrackRecordingServiceRecordingTest {
service.endCurrentTrack();
// then
assertEquals(new TrackStatistics(startTime, stopTime, 22.226356506347656, 10, 5, 15, 0f, 0f)
assertEquals(new TrackStatistics(startTime, stopTime, 22.226356506347656, 10, 10, 15, 0f, 0f)
, contentProviderUtils.getTrack(trackId).getTrackStatistics());
new TrackPointAssert().assertEquals(List.of(
@@ -357,71 +357,6 @@ public class TrackRecordingServiceRecordingTest {
), TestDataUtil.getTrackPoints(contentProviderUtils, trackId));
}
@MediumTest
@Test
public void testRecording_gpsOnly_recordingDistance_above_speed_0() {
// given
String startTime = "2020-02-02T02:02:02Z";
TrackPointCreator trackPointCreator = service.getTrackPointCreator();
trackPointCreator.setClock(startTime);
Track.Id trackId = service.startNewTrack();
trackPointCreator.getSensorManager().setAltitudeSumManager(altitudeSumManager);
// when
String gps1 = "2020-02-02T02:02:03Z";
TrackRecordingServiceTestUtils.sendGPSLocation(trackPointCreator, gps1, 45.0, 35.0, 1, 0);
// then
TrackStatistics gps1statistics = new TrackStatistics(startTime, gps1, 0, 1, 0, 0, 0f, 0f);
assertEquals(gps1statistics, contentProviderUtils.getTrack(trackId).getTrackStatistics());
// when
String gps2 = "2020-02-02T02:02:06Z";
TrackRecordingServiceTestUtils.sendGPSLocation(trackPointCreator, gps2, 45.0001, 35.0, 1, 0);
// then
assertEquals(gps1statistics, contentProviderUtils.getTrack(trackId).getTrackStatistics());
// when
String gps3 = "2020-02-02T02:02:08Z";
TrackRecordingServiceTestUtils.sendGPSLocation(trackPointCreator, gps3, 45.0002, 35.0, 1, 0);
// then
assertEquals(gps1statistics, contentProviderUtils.getTrack(trackId).getTrackStatistics());
// when
String stopTime = "2020-02-02T02:02:12Z";
trackPointCreator.setClock(stopTime);
service.endCurrentTrack();
// then
assertEquals(new TrackStatistics(startTime, stopTime, 0, 10, 0, 0, 0f, 0f)
, contentProviderUtils.getTrack(trackId).getTrackStatistics());
new TrackPointAssert().assertEquals(List.of(
new TrackPoint(TrackPoint.Type.SEGMENT_START_MANUAL, Instant.parse(startTime)),
new TrackPoint(TrackPoint.Type.TRACKPOINT, Instant.parse(gps1))
.setLatitude(45)
.setLongitude(35)
.setHorizontalAccuracy(Distance.of(1))
.setSpeed(Speed.of(0))
.setAltitudeGain(0f)
.setAltitudeLoss(0f),
new TrackPoint(TrackPoint.Type.TRACKPOINT, Instant.parse(gps3))
.setLatitude(45.0001)
.setLongitude(35)
.setHorizontalAccuracy(Distance.of(1))
.setSpeed(Speed.of(0))
.setAltitudeGain(0f)
.setAltitudeLoss(0f),
new TrackPoint(TrackPoint.Type.SEGMENT_END_MANUAL, Instant.parse(stopTime))
.setAltitudeGain(0f)
.setAltitudeLoss(0f)
), TestDataUtil.getTrackPoints(contentProviderUtils, trackId));
}
@MediumTest
@Test
public void testRecording_gpsOnly_recordingDistance_below() {
@@ -437,7 +372,7 @@ public class TrackRecordingServiceRecordingTest {
TrackRecordingServiceTestUtils.sendGPSLocation(trackPointCreator, gps1, 45.0, 35.0, 1, 15);
// then
TrackStatistics gps1Statistics = new TrackStatistics(startTime, gps1, 0, 1, 0, 0, 0f, 0f);
TrackStatistics gps1Statistics = new TrackStatistics(startTime, gps1, 0, 1, 1, 15, 0f, 0f);
assertEquals(gps1Statistics, contentProviderUtils.getTrack(trackId).getTrackStatistics());
// when
@@ -461,7 +396,7 @@ public class TrackRecordingServiceRecordingTest {
service.endCurrentTrack();
// then
assertEquals(new TrackStatistics(startTime, stopTime, 2.222635507583618, 10, 5, 15, 0f, 0f)
assertEquals(new TrackStatistics(startTime, stopTime, 2.222635507583618, 10, 10, 15, 0f, 0f)
, contentProviderUtils.getTrack(trackId).getTrackStatistics());
new TrackPointAssert().assertEquals(List.of(
@@ -486,158 +421,6 @@ public class TrackRecordingServiceRecordingTest {
), TestDataUtil.getTrackPoints(contentProviderUtils, trackId));
}
@MediumTest
@Test
public void testRecording_gpsOnly_recordingDistance_idle() {
// given
String startTime = "2020-02-02T02:02:02Z";
TrackPointCreator trackPointCreator = service.getTrackPointCreator();
trackPointCreator.setClock(startTime);
Track.Id trackId = service.startNewTrack();
trackPointCreator.getSensorManager().setAltitudeSumManager(altitudeSumManager);
// when
String gps1 = "2020-02-02T02:02:03Z";
TrackRecordingServiceTestUtils.sendGPSLocation(trackPointCreator, gps1, 45.0, 35.0, 1, 0);
// then
TrackStatistics gps1Statistics = new TrackStatistics(startTime, gps1, 0, 1, 0, 0, 0f, 0f);
assertEquals(gps1Statistics, contentProviderUtils.getTrack(trackId).getTrackStatistics());
// when
String gps2 = "2020-02-02T02:02:06Z";
TrackRecordingServiceTestUtils.sendGPSLocation(trackPointCreator, gps2, 45.0, 35.0, 1, 0);
// then
assertEquals(gps1Statistics, contentProviderUtils.getTrack(trackId).getTrackStatistics());
// when
String gps3 = "2020-02-02T02:02:08Z";
TrackRecordingServiceTestUtils.sendGPSLocation(trackPointCreator, gps3, 45.0, 35.0, 1, 0);
// then
assertEquals(gps1Statistics, contentProviderUtils.getTrack(trackId).getTrackStatistics());
// when
String stopTime = "2020-02-02T02:02:12Z";
trackPointCreator.setClock(stopTime);
service.endCurrentTrack();
// then
assertEquals(new TrackStatistics(startTime, stopTime, 0, 10, 0, 0, 0f, 0f)
, contentProviderUtils.getTrack(trackId).getTrackStatistics());
new TrackPointAssert().assertEquals(List.of(
new TrackPoint(TrackPoint.Type.SEGMENT_START_MANUAL, Instant.parse(startTime)),
new TrackPoint(TrackPoint.Type.TRACKPOINT, Instant.parse(gps1))
.setLatitude(45)
.setLongitude(35)
.setHorizontalAccuracy(Distance.of(1))
.setSpeed(Speed.of(0))
.setAltitudeGain(0f)
.setAltitudeLoss(0f),
new TrackPoint(TrackPoint.Type.TRACKPOINT, Instant.parse(gps3))
.setLatitude(45.00002)
.setLongitude(35)
.setHorizontalAccuracy(Distance.of(1))
.setSpeed(Speed.of(0))
.setAltitudeGain(0f)
.setAltitudeLoss(0f),
new TrackPoint(TrackPoint.Type.SEGMENT_END_MANUAL, Instant.parse(stopTime))
.setAltitudeGain(0f)
.setAltitudeLoss(0f)
), TestDataUtil.getTrackPoints(contentProviderUtils, trackId));
}
@MediumTest
@Test
public void testRecording_gpsOnly_recordingDistance_idle_movement() {
// given
String startTime = "2020-02-02T02:02:02Z";
TrackPointCreator trackPointCreator = service.getTrackPointCreator();
trackPointCreator.setClock(startTime);
Track.Id trackId = service.startNewTrack();
trackPointCreator.getSensorManager().setAltitudeSumManager(altitudeSumManager);
// when
String gps1 = "2020-02-02T02:02:03Z";
TrackRecordingServiceTestUtils.sendGPSLocation(trackPointCreator, gps1, 45.0, 35.0, 1, 15);
// then
assertEquals(new TrackStatistics(startTime, gps1, 0, 1, 0, 0, 0f, 0f)
, contentProviderUtils.getTrack(trackId).getTrackStatistics());
// when
String gps2 = "2020-02-02T02:02:06Z";
TrackRecordingServiceTestUtils.sendGPSLocation(trackPointCreator, gps2, 45.0, 35.0, 1, 0);
// then
final TrackStatistics gps2statistics = new TrackStatistics(startTime, gps2, 0, 4, 0, 0, 0f, 0f);
assertEquals(gps2statistics
, contentProviderUtils.getTrack(trackId).getTrackStatistics());
// when
String gps3 = "2020-02-02T02:02:08Z";
TrackRecordingServiceTestUtils.sendGPSLocation(trackPointCreator, gps3, 45.0, 35.0, 1, 0);
// then
assertEquals(gps2statistics, contentProviderUtils.getTrack(trackId).getTrackStatistics());
// when
String gps4 = "2020-02-02T02:02:10Z";
TrackRecordingServiceTestUtils.sendGPSLocation(trackPointCreator, gps4, 45.0, 35.0, 1, 15);
// then
assertEquals(new TrackStatistics(startTime, gps4, 0, 8, 0, 0, 0f, 0f)
, contentProviderUtils.getTrack(trackId).getTrackStatistics());
// when
String stopTime = "2020-02-02T02:02:12Z";
trackPointCreator.setClock(stopTime);
service.endCurrentTrack();
// then
assertEquals(new TrackStatistics(startTime, stopTime, 0, 10, 0, 0, 0f, 0f)
, contentProviderUtils.getTrack(trackId).getTrackStatistics());
new TrackPointAssert().assertEquals(List.of(
new TrackPoint(TrackPoint.Type.SEGMENT_START_MANUAL, Instant.parse(startTime)),
new TrackPoint(TrackPoint.Type.TRACKPOINT, Instant.parse(gps1))
.setLatitude(45)
.setLongitude(35)
.setHorizontalAccuracy(Distance.of(1))
.setSpeed(Speed.of(15))
.setAltitudeGain(0f)
.setAltitudeLoss(0f),
new TrackPoint(TrackPoint.Type.TRACKPOINT, Instant.parse(gps2))
.setLatitude(45)
.setLongitude(35)
.setHorizontalAccuracy(Distance.of(1))
.setSpeed(Speed.of(0))
.setAltitudeGain(0f)
.setAltitudeLoss(0f),
new TrackPoint(TrackPoint.Type.TRACKPOINT, Instant.parse(gps3))
.setLatitude(45)
.setLongitude(35)
.setHorizontalAccuracy(Distance.of(1))
.setSpeed(Speed.of(0))
.setAltitudeGain(0f)
.setAltitudeLoss(0f),
new TrackPoint(TrackPoint.Type.TRACKPOINT, Instant.parse(gps4))
.setLatitude(45)
.setLongitude(35)
.setHorizontalAccuracy(Distance.of(1))
.setSpeed(Speed.of(15))
.setAltitudeGain(0f)
.setAltitudeLoss(0f),
new TrackPoint(TrackPoint.Type.SEGMENT_END_MANUAL, Instant.parse(stopTime))
.setAltitudeGain(0f)
.setAltitudeLoss(0f)
), TestDataUtil.getTrackPoints(contentProviderUtils, trackId));
}
@MediumTest
@Test
public void testRecording_gpsOnly_recordingDistance_movement_non_idle() {
@@ -721,7 +504,7 @@ public class TrackRecordingServiceRecordingTest {
service.endCurrentTrack();
// then
assertEquals(new TrackStatistics(startTime, stopTime, 0, 10, 0, 0, 0f, 0f)
assertEquals(new TrackStatistics(startTime, stopTime, 0, 10, 10, 0, 0f, 0f)
, contentProviderUtils.getTrack(trackId).getTrackStatistics());
@@ -749,14 +532,14 @@ public class TrackRecordingServiceRecordingTest {
TrackRecordingServiceTestUtils.sendGPSLocation(trackPointCreator, gps1, 45.0, 35.0, 1, 15);
// then
assertEquals(new TrackStatistics(startTime, gps1, 0, 1, 0, 0, 0f, 0f), contentProviderUtils.getTrack(trackId).getTrackStatistics());
assertEquals(new TrackStatistics(startTime, gps1, 0, 1, 1, 15, 0f, 0f), contentProviderUtils.getTrack(trackId).getTrackStatistics());
// when
String gps2 = "2020-02-02T02:02:06Z";
TrackRecordingServiceTestUtils.sendGPSLocation(trackPointCreator, gps2, 45.1, 35.0, 1, 15);
// then
assertEquals(new TrackStatistics(startTime, gps2, 11113.275390625, 4, 3, 3704.4251302083335f, 0f, 0f).toString(), contentProviderUtils.getTrack(trackId).getTrackStatistics().toString());
assertEquals(new TrackStatistics(startTime, gps2, 11113.275390625, 4, 4, 2778.31884765625f, 0f, 0f).toString(), contentProviderUtils.getTrack(trackId).getTrackStatistics().toString());
// when
@@ -765,7 +548,7 @@ public class TrackRecordingServiceRecordingTest {
service.endCurrentTrack();
// then
assertEquals(new TrackStatistics(startTime, stopTime, 11113.275390625, 10, 3, 3704.4251302083335f, 0f, 0f).toString(), contentProviderUtils.getTrack(trackId).getTrackStatistics().toString());
assertEquals(new TrackStatistics(startTime, stopTime, 11113.275390625, 10, 10, 1111.3275390625f, 0f, 0f).toString(), contentProviderUtils.getTrack(trackId).getTrackStatistics().toString());
// then
@@ -63,7 +63,7 @@ public class VoiceAnnouncementUtilsTest {
stats.setTotalAltitudeGain(6000f);
// when
String announcement = VoiceAnnouncementUtils.getAnnouncement(context, stats, UnitSystem.METRIC, true, null, null).toString();
String announcement = VoiceAnnouncementUtils.createStatistics(context, stats, UnitSystem.METRIC, true, null, null).toString();
// then
assertEquals("Total distance 20.0 kilometers. 1 hour 5 minutes 10 seconds. Average moving speed 18.4 kilometers per hour.", announcement);
@@ -79,7 +79,7 @@ public class VoiceAnnouncementUtilsTest {
stats.setTotalAltitudeGain(6000f);
// when
String announcement = VoiceAnnouncementUtils.getAnnouncement(context, stats, UnitSystem.METRIC, true, null, null).toString();
String announcement = VoiceAnnouncementUtils.createStatistics(context, stats, UnitSystem.METRIC, true, null, null).toString();
// then
assertEquals("Total distance 20.0 kilometers. 1 hour 1 second. Average moving speed 20.0 kilometers per hour.", announcement);
@@ -95,7 +95,7 @@ public class VoiceAnnouncementUtilsTest {
stats.setTotalAltitudeGain(6000f);
// when
String announcement = VoiceAnnouncementUtils.getAnnouncement(context, stats, UnitSystem.METRIC, true, null, null).toString();
String announcement = VoiceAnnouncementUtils.createStatistics(context, stats, UnitSystem.METRIC, true, null, null).toString();
// then
assertEquals("Total distance 20.0 kilometers. 1 hour. Average moving speed 20.0 kilometers per hour.", announcement);
@@ -111,7 +111,7 @@ public class VoiceAnnouncementUtilsTest {
stats.setTotalAltitudeGain(6000f);
// when
String announcement = VoiceAnnouncementUtils.getAnnouncement(context, stats, UnitSystem.METRIC, true, null, null).toString();
String announcement = VoiceAnnouncementUtils.createStatistics(context, stats, UnitSystem.METRIC, true, null, null).toString();
// then
assertEquals("Total distance 19.9 kilometers. 1 hour. Average moving speed 19.9 kilometers per hour.", announcement);
@@ -133,7 +133,7 @@ public class VoiceAnnouncementUtilsTest {
}
// when
String announcement = VoiceAnnouncementUtils.getAnnouncement(context, stats, UnitSystem.METRIC, true, lastInterval, null).toString();
String announcement = VoiceAnnouncementUtils.createStatistics(context, stats, UnitSystem.METRIC, true, lastInterval, null).toString();
// then
assertEquals("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);
@@ -149,7 +149,7 @@ public class VoiceAnnouncementUtilsTest {
stats.setTotalAltitudeGain(6000f);
// when
String announcement = VoiceAnnouncementUtils.getAnnouncement(context, stats, UnitSystem.METRIC, false, null, null).toString();
String announcement = VoiceAnnouncementUtils.createStatistics(context, stats, UnitSystem.METRIC, false, null, null).toString();
// then
assertEquals("Total distance 20.0 kilometers. 1 hour 5 minutes 10 seconds. Pace 3 minutes 15 seconds per kilometer.", announcement);
@@ -171,7 +171,7 @@ public class VoiceAnnouncementUtilsTest {
}
// when
String announcement = VoiceAnnouncementUtils.getAnnouncement(context, stats, UnitSystem.METRIC, false, lastInterval, null).toString();
String announcement = VoiceAnnouncementUtils.createStatistics(context, stats, UnitSystem.METRIC, false, lastInterval, null).toString();
// then
assertEquals("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);
@@ -187,7 +187,7 @@ public class VoiceAnnouncementUtilsTest {
stats.setTotalAltitudeGain(6000f);
// when
String announcement = VoiceAnnouncementUtils.getAnnouncement(context, stats, UnitSystem.IMPERIAL_FEET, true, null, null).toString();
String announcement = VoiceAnnouncementUtils.createStatistics(context, stats, UnitSystem.IMPERIAL_FEET, true, null, null).toString();
// then
assertEquals("Total distance 12.4 miles. 1 hour 5 minutes 10 seconds. Average moving speed 11.4 miles per hour.", announcement);
@@ -201,7 +201,7 @@ public class VoiceAnnouncementUtilsTest {
stats.setMovingTime(Duration.ofHours(1));
// when
String announcement = VoiceAnnouncementUtils.getAnnouncement(context, stats, UnitSystem.IMPERIAL_FEET, true, null, null).toString();
String announcement = VoiceAnnouncementUtils.createStatistics(context, stats, UnitSystem.IMPERIAL_FEET, true, null, null).toString();
// then
assertEquals("Total distance 1.1 miles. 1 hour. Average moving speed 1.1 miles per hour.", announcement);
@@ -215,7 +215,7 @@ public class VoiceAnnouncementUtilsTest {
stats.setMovingTime(Duration.ofHours(1));
// when
String announcement = VoiceAnnouncementUtils.getAnnouncement(context, stats, UnitSystem.IMPERIAL_METER, true, null, null).toString();
String announcement = VoiceAnnouncementUtils.createStatistics(context, stats, UnitSystem.IMPERIAL_METER, true, null, null).toString();
// then
assertEquals("Total distance 1.1 miles. 1 hour. Average moving speed 1.1 miles per hour.", announcement);
@@ -229,7 +229,7 @@ public class VoiceAnnouncementUtilsTest {
stats.setMovingTime(Duration.ofHours(1));
// when
String announcement = VoiceAnnouncementUtils.getAnnouncement(context, stats, UnitSystem.METRIC, true, null, null).toString();
String announcement = VoiceAnnouncementUtils.createStatistics(context, stats, UnitSystem.METRIC, true, null, null).toString();
// then
assertEquals("Total distance 1.1 kilometers. 1 hour. Average moving speed 1.1 kilometers per hour.", announcement);
@@ -251,7 +251,7 @@ public class VoiceAnnouncementUtilsTest {
}
// when
String announcement = VoiceAnnouncementUtils.getAnnouncement(context, stats, UnitSystem.IMPERIAL_FEET, true, lastInterval, null).toString();
String announcement = VoiceAnnouncementUtils.createStatistics(context, stats, UnitSystem.IMPERIAL_FEET, true, lastInterval, null).toString();
// then
assertEquals("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);
@@ -267,7 +267,7 @@ public class VoiceAnnouncementUtilsTest {
stats.setTotalAltitudeGain(6000f);
// when
String announcement = VoiceAnnouncementUtils.getAnnouncement(context, stats, UnitSystem.IMPERIAL_FEET, false, null, null).toString();
String announcement = VoiceAnnouncementUtils.createStatistics(context, stats, UnitSystem.IMPERIAL_FEET, false, null, null).toString();
// then
assertEquals("Total distance 12.4 miles. 1 hour 5 minutes 10 seconds. Pace 5 minutes 15 seconds per mile.", announcement);
@@ -289,7 +289,7 @@ public class VoiceAnnouncementUtilsTest {
}
// when
String announcement = VoiceAnnouncementUtils.getAnnouncement(context, stats, UnitSystem.IMPERIAL_FEET, false, lastInterval, null).toString();
String announcement = VoiceAnnouncementUtils.createStatistics(context, stats, UnitSystem.IMPERIAL_FEET, false, lastInterval, null).toString();
// then
assertEquals("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);
@@ -315,7 +315,7 @@ public class VoiceAnnouncementUtilsTest {
SensorStatistics sensorStatistics = new SensorStatistics(HeartRate.of(180f), HeartRate.of(180f), null, null, null, null);
// when
String announcement = VoiceAnnouncementUtils.getAnnouncement(context, stats, UnitSystem.METRIC, true, lastInterval, sensorStatistics).toString();
String announcement = VoiceAnnouncementUtils.createStatistics(context, stats, UnitSystem.METRIC, true, lastInterval, sensorStatistics).toString();
// then
assertEquals("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. Current heart rate 133 bpm.", announcement);
@@ -345,7 +345,7 @@ public class VoiceAnnouncementUtilsTest {
SensorStatistics sensorStatistics = new SensorStatistics(HeartRate.of(180f), HeartRate.of(180f), null, null, null, null);
// when
String announcement = VoiceAnnouncementUtils.getAnnouncement(context, stats, UnitSystem.METRIC, true, lastInterval, sensorStatistics).toString();
String announcement = VoiceAnnouncementUtils.createStatistics(context, stats, UnitSystem.METRIC, true, lastInterval, sensorStatistics).toString();
// then
assertEquals(" Current heart rate 133 bpm.", announcement);
@@ -83,35 +83,17 @@ public class TrackStatisticsUpdaterTest {
TrackStatistics statistics = subject.getTrackStatistics();
assertEquals(142.26, statistics.getTotalDistance().toM(), 0.01);
assertEquals(Duration.ofSeconds(12), statistics.getTotalTime());
assertEquals(Duration.ofSeconds(10), statistics.getMovingTime());
assertEquals(Duration.ofSeconds(12), statistics.getMovingTime());
assertEquals(2.5, statistics.getMinAltitude(), 0.01);
assertEquals(32.5, statistics.getMaxAltitude(), 0.01);
assertEquals(36, statistics.getTotalAltitudeGain(), 0.01);
assertEquals(36, statistics.getTotalAltitudeLoss(), 0.01);
assertEquals(14.226, statistics.getMaxSpeed().toMPS(), 0.01);
assertEquals(14.226, statistics.getAverageMovingSpeed().toMPS(), 0.01);
assertEquals(11.85, statistics.getMaxSpeed().toMPS(), 0.01);
assertEquals(11.85, statistics.getAverageMovingSpeed().toMPS(), 0.01);
assertEquals(11.85, statistics.getAverageSpeed().toMPS(), 0.01);
assertEquals(106.64f, statistics.getAverageHeartRate().getBPM(), 0.01);
}
@Test
public void addTrackPoint_distance_from_GPS_not_moving() {
// given
TrackStatisticsUpdater subject = new TrackStatisticsUpdater();
TrackPoint tp1 = new TrackPoint(TrackPoint.Type.SEGMENT_START_MANUAL, Instant.ofEpochMilli(1000));
TrackPoint tp2 = new TrackPoint(0, 0, Altitude.WGS84.of(5.0), Instant.ofEpochMilli(2000));
TrackPoint tp3 = new TrackPoint(0.00001, 0, Altitude.WGS84.of(5.0), Instant.ofEpochMilli(3000));
// when
subject.addTrackPoint(tp1);
subject.addTrackPoint(tp2);
subject.addTrackPoint(tp3);
// then
assertEquals(0, subject.getTrackStatistics().getTotalDistance().toM(), 0.01);
assertEquals(106.834f, statistics.getAverageHeartRate().getBPM(), 0.01);
}
@Test
@@ -165,35 +147,6 @@ public class TrackStatisticsUpdaterTest {
assertEquals(125.57, subject.getTrackStatistics().getTotalDistance().toM(), 0.01);
}
@Test
public void addTrackPoint_distance_from_GPS_not_moving_and_sensor_moving() {
// given
TrackStatisticsUpdater subject = new TrackStatisticsUpdater();
TrackPoint tp1 = new TrackPoint(TrackPoint.Type.SEGMENT_START_MANUAL, Instant.ofEpochMilli(1000));
TrackPoint tp2 = new TrackPoint(0, 0, Altitude.WGS84.of(5.0), Instant.ofEpochMilli(2000));
TrackPoint tp3 = new TrackPoint(0.00001, 0, Altitude.WGS84.of(5.0), Instant.ofEpochMilli(3000));
TrackPoint tp4 = new TrackPoint(0.00001, 0, Altitude.WGS84.of(5.0), Instant.ofEpochMilli(4000));
tp4.setSensorDistance(Distance.of(5f));
TrackPoint tp5 = new TrackPoint(TrackPoint.Type.SEGMENT_END_MANUAL, Instant.ofEpochMilli(5000));
tp5.setSensorDistance(Distance.of(10f));
// when
subject.addTrackPoint(tp1);
subject.addTrackPoint(tp2);
subject.addTrackPoint(tp3);
// then
assertEquals(0, subject.getTrackStatistics().getTotalDistance().toM(), 0.01);
// when
subject.addTrackPoint(tp4);
subject.addTrackPoint(tp5);
// then
assertEquals(15, subject.getTrackStatistics().getTotalDistance().toM(), 0.01);
}
@Test
public void addTrackPoint_distance_from_GPS_moving_and_sensor_disconnecting() {
// given
@@ -225,42 +178,6 @@ public class TrackStatisticsUpdaterTest {
assertEquals(59.18, subject.getTrackStatistics().getTotalDistance().toM(), 0.01);
}
@Test
public void addTrackPoint_maxSpeed_ignore_above_acceleration() {
TrackStatisticsUpdater subject = new TrackStatisticsUpdater();
assertEquals(Speed.of(0f), subject.getTrackStatistics().getMaxSpeed());
subject.addTrackPoint(new TrackPoint(TrackPoint.Type.SEGMENT_START_MANUAL, Instant.ofEpochSecond(0)));
assertEquals(Speed.of(0f), subject.getTrackStatistics().getMaxSpeed());
// Ignore as we set max speed if two consecutive trackpoints were considered moving
subject.addTrackPoint(new TrackPoint(0, 0, Altitude.WGS84.of(0), Instant.ofEpochSecond(1))
.setSpeed(Speed.of(1f)));
assertEquals(Speed.of(0f), subject.getTrackStatistics().getMaxSpeed());
// Update max speed
subject.addTrackPoint(new TrackPoint(0, 0, Altitude.WGS84.of(0), Instant.ofEpochSecond(2))
.setSpeed(Speed.of(1f)));
assertEquals(Speed.of(1f), subject.getTrackStatistics().getMaxSpeed());
// Update max speed
subject.addTrackPoint(new TrackPoint(0, 0, Altitude.WGS84.of(0), Instant.ofEpochSecond(12))
.setSpeed(Speed.of(50f)));
assertEquals(Speed.of(50f), subject.getTrackStatistics().getMaxSpeed());
// Ignore; we were getting slower
subject.addTrackPoint(new TrackPoint(0, 0, Altitude.WGS84.of(0), Instant.ofEpochSecond(13))
.setSpeed(Speed.of(5f)));
assertEquals(Speed.of(50f), subject.getTrackStatistics().getMaxSpeed());
// Ignore acceleration above 2g
subject.addTrackPoint(new TrackPoint(0, 0, Altitude.WGS84.of(0), Instant.ofEpochSecond(14))
.setSpeed(Speed.of(500f)));
assertEquals(Speed.of(50f), subject.getTrackStatistics().getMaxSpeed());
}
@Test
public void addTrackPoint_maxSpeed_multiple_segments() {
TrackStatisticsUpdater subject = new TrackStatisticsUpdater();
@@ -290,6 +207,64 @@ public class TrackStatisticsUpdaterTest {
assertEquals(Speed.of(2f), subject.getTrackStatistics().getMaxSpeed());
}
@Test
public void addTrackPoint_idle_withoutDistance() {
TrackStatisticsUpdater subject = new TrackStatisticsUpdater();
// when
subject.addTrackPoints(List.of(
new TrackPoint(TrackPoint.Type.SEGMENT_START_MANUAL, Instant.ofEpochSecond(0)),
new TrackPoint(0, 0, Altitude.WGS84.of(0), Instant.ofEpochSecond(1))
.setSpeed(Speed.of(2f)),
new TrackPoint(0, 0, Altitude.WGS84.of(0), Instant.ofEpochSecond(2))
.setSpeed(Speed.of(2f)),
new TrackPoint(TrackPoint.Type.IDLE, Instant.ofEpochSecond(30)),
new TrackPoint(TrackPoint.Type.TRACKPOINT, Instant.ofEpochSecond(40))
.setHeartRate(50),
new TrackPoint(TrackPoint.Type.TRACKPOINT, Instant.ofEpochSecond(45))
.setHeartRate(50),
new TrackPoint(0, 1, Altitude.WGS84.of(0), Instant.ofEpochSecond(50)),
new TrackPoint(0, 2, Altitude.WGS84.of(0), Instant.ofEpochSecond(55)),
new TrackPoint(TrackPoint.Type.SEGMENT_END_MANUAL, Instant.ofEpochSecond(60))
));
// then
assertEquals(Duration.ofSeconds(40), subject.getTrackStatistics().getMovingTime());
}
@Test
public void addTrackPoint_idle_withDistance() {
TrackStatisticsUpdater subject = new TrackStatisticsUpdater();
// when
subject.addTrackPoints(List.of(
new TrackPoint(TrackPoint.Type.SEGMENT_START_MANUAL, Instant.ofEpochSecond(0)),
new TrackPoint(0, 0, Altitude.WGS84.of(0), Instant.ofEpochSecond(1))
.setSensorDistance(Distance.of(10)),
new TrackPoint(0, 0, Altitude.WGS84.of(0), Instant.ofEpochSecond(2))
.setSensorDistance(Distance.of(10)),
new TrackPoint(TrackPoint.Type.IDLE, Instant.ofEpochSecond(30))
.setSensorDistance(Distance.ofKilometer(1)),
new TrackPoint(TrackPoint.Type.TRACKPOINT, Instant.ofEpochSecond(40))
.setHeartRate(50),
new TrackPoint(TrackPoint.Type.TRACKPOINT, Instant.ofEpochSecond(45))
.setHeartRate(50),
new TrackPoint(0, 0, Altitude.WGS84.of(0), Instant.ofEpochSecond(50))
.setSensorDistance(Distance.of(10)),
new TrackPoint(0, 0, Altitude.WGS84.of(0), Instant.ofEpochSecond(55))
.setSensorDistance(Distance.of(10)),
new TrackPoint(TrackPoint.Type.SEGMENT_END_MANUAL, Instant.ofEpochSecond(60))
));
// then
assertEquals(Duration.ofSeconds(45), subject.getTrackStatistics().getMovingTime());
assertEquals(Distance.of(1040), subject.getTrackStatistics().getTotalDistance());
}
@Test
public void copy_constructor() {
// given
@@ -314,7 +289,6 @@ public class TrackStatisticsUpdaterTest {
subject.addTrackPoint(tp5);
copy.addTrackPoint(tp5);
// then
assertEquals(55.287, subject.getTrackStatistics().getTotalDistance().toM(), 0.01);
assertEquals(55.287, copy.getTrackStatistics().getTotalDistance().toM(), 0.01);
+3 -3
View File
@@ -3,11 +3,11 @@
"2020-02-02T03:02:03+01:00","TRACKPOINT",3,14,10,10,,54,1,1,,,,
"2020-02-02T03:02:04+01:00","TRACKPOINT",,,,,,54,1,1,10,66,3,50
"2020-02-02T03:02:15+01:00","TRACKPOINT",,,,,,,,,,68,3,50
"2020-02-02T03:02:16+01:00","TRACKPOINT",,,,,,18,,,2,69,3,50
"2020-02-02T03:02:17+01:00","TRACKPOINT",3,14.001,10,10,,18,0,0,2,69,3,50
"2020-02-02T03:02:18+01:00","SEGMENT_END_MANUAL",,,,,,,,,,,,
"2020-02-02T03:03:20+01:00","SEGMENT_START_MANUAL",,,,,,,,,,,,
"2020-02-02T03:03:21+01:00","TRACKPOINT",3,14.002,10,10,,54,0,0,,,,
"2020-02-02T03:03:22+01:00","SEGMENT_START_AUTOMATIC",3,16,10,10,,54,0,0,,,,
"2020-02-02T03:03:23+01:00","TRACKPOINT",3,16.001,10,10,,54,0,0,,,,
"2020-02-02T03:03:24+01:00","SEGMENT_END_MANUAL",,,,,,,,,,,,
"2020-02-02T03:03:30+01:00","IDLE",,,,,,,,,,,,
"2020-02-02T03:03:50+01:00","TRACKPOINT",3,16.001,10,10,,54,0,0,,,,
"2020-02-02T03:04:00+01:00","SEGMENT_END_MANUAL",,,,,,,,,,,,
1 #time trackpoint_type latitude longitude altitude accuracy_horizontal accuracy_vertical speed altitude_gain altitude_loss sensor_distance heartrate cadence power
3 2020-02-02T03:02:03+01:00 TRACKPOINT 3 14 10 10 54 1 1
4 2020-02-02T03:02:04+01:00 TRACKPOINT 54 1 1 10 66 3 50
5 2020-02-02T03:02:15+01:00 TRACKPOINT 68 3 50
2020-02-02T03:02:16+01:00 TRACKPOINT 18 2 69 3 50
6 2020-02-02T03:02:17+01:00 TRACKPOINT 3 14.001 10 10 18 0 0 2 69 3 50
7 2020-02-02T03:02:18+01:00 SEGMENT_END_MANUAL
8 2020-02-02T03:03:20+01:00 SEGMENT_START_MANUAL
9 2020-02-02T03:03:21+01:00 TRACKPOINT 3 14.002 10 10 54 0 0
10 2020-02-02T03:03:22+01:00 SEGMENT_START_AUTOMATIC 3 16 10 10 54 0 0
11 2020-02-02T03:03:23+01:00 2020-02-02T03:03:30+01:00 TRACKPOINT IDLE 3 16.001 10 10 54 0 0
12 2020-02-02T03:03:24+01:00 2020-02-02T03:03:50+01:00 SEGMENT_END_MANUAL TRACKPOINT 3 16.001 10 10 54 0 0
13 2020-02-02T03:04:00+01:00 SEGMENT_END_MANUAL
@@ -21,7 +21,7 @@ import android.view.View;
import androidx.appcompat.app.AppCompatActivity;
import de.dennisguse.opentracks.services.announcement.VoiceAnnouncement;
import de.dennisguse.opentracks.services.announcement.TTSManager;
/**
* @author Jimmy Shih
@@ -33,7 +33,7 @@ public abstract class AbstractActivity extends AppCompatActivity {
super.onCreate(savedInstanceState);
// Set volume control stream for text to speech
setVolumeControlStream(VoiceAnnouncement.AUDIO_STREAM);
setVolumeControlStream(TTSManager.AUDIO_STREAM);
setContentView(getRootView());
}
@@ -2,7 +2,6 @@ package de.dennisguse.opentracks.data.models;
import java.time.Duration;
import de.dennisguse.opentracks.settings.PreferencesUtils;
import de.dennisguse.opentracks.settings.UnitSystem;
public record Speed(double speed_mps) {
@@ -56,10 +55,6 @@ public record Speed(double speed_mps) {
return Double.isNaN(speed_mps) || Double.isInfinite(speed_mps);
}
public boolean isMoving() {
return !isInvalid() && greaterThan(PreferencesUtils.getIdleSpeed());
}
public boolean lessThan(Speed speed) {
return !greaterThan(speed);
}
@@ -66,6 +66,7 @@ public class TrackPoint {
public final int type_db;
Type(int type_db) {
this.type_db = type_db;
}
@@ -287,11 +288,6 @@ public class TrackPoint {
this.speed = speed;
return this;
}
public boolean isMoving() {
return hasSpeed() && getSpeed().isMoving();
}
public boolean hasBearing() {
return bearing != null;
}
@@ -352,9 +348,8 @@ public class TrackPoint {
return hasHorizontalAccuracy() && horizontalAccuracy.lessThan(thresholdHorizontalAccuracy);
}
//TODO Bearing requires a location; what do we do if we don't have any?
public float bearingTo(@NonNull TrackPoint dest) {
return getLocation().bearingTo(dest.getLocation());
return bearingTo(dest.getLocation());
}
//TODO Bearing requires a location; what do we do if we don't have any?
@@ -449,13 +444,14 @@ public class TrackPoint {
", latitude=" + latitude +
", longitude=" + longitude +
", horizontalAccuracy=" + horizontalAccuracy +
", verticalAccuracy=" + verticalAccuracy +
", altitude=" + altitude +
", speed=" + speed +
", bearing=" + bearing +
", sensorDistance=" + sensorDistance +
", type=" + type +
", heartRate_bpm=" + heartRate +
", cadence_rpm=" + cadence +
", heartRate=" + heartRate +
", cadence=" + cadence +
", power=" + power +
", altitudeGain_m=" + altitudeGain_m +
", altitudeLoss_m=" + altitudeLoss_m +
@@ -171,6 +171,9 @@ public class GPXTrackExporter implements TrackExporter {
sensorPoints.add(trackPoint);
}
}
case IDLE -> {
// Not supported as IDLE-TrackPoints have no location.
}
default ->
throw new RuntimeException("Exporting this TrackPoint type is not implemented: " + trackPoint.getType());
}
@@ -61,6 +61,7 @@ public class KMLTrackExporter implements TrackExporter {
public static final String EXTENDED_DATA_TYPE_ACTIVITYTYPE = "type";
public static final String EXTENDED_DATA_TYPE_TRACKPOINT = "trackpoint_type";
public static final String EXTENDED_DATA_TYPE_SPEED = "speed";
public static final String EXTENDED_DATA_TYPE_DISTANCE = "distance";
public static final String EXTENDED_DATA_TYPE_CADENCE = "cadence";
@@ -83,6 +84,9 @@ public class KMLTrackExporter implements TrackExporter {
private final ContentProviderUtils contentProviderUtils;
private PrintWriter printWriter;
private ArrayList<TrackPoint.Type> trackpointTypeList = new ArrayList<>();
private final List<Float> speedList = new ArrayList<>();
private final List<Float> distanceList = new ArrayList<>();
private final List<Float> powerList = new ArrayList<>();
@@ -177,12 +181,13 @@ public class KMLTrackExporter implements TrackExporter {
writeCloseSegment();
wroteSegment = false;
}
case TRACKPOINT -> {
case TRACKPOINT, IDLE -> {
if (!wroteSegment) {
// Might happen for older data (pre v3.15.0)
writeOpenSegment();
wroteSegment = true;
}
writeTrackPoint(track.getZoneOffset(), trackPoint);
}
default ->
@@ -309,6 +314,7 @@ public class KMLTrackExporter implements TrackExporter {
@VisibleForTesting
void writeOpenSegment() {
printWriter.println("<Track>");
trackpointTypeList.clear();
speedList.clear();
distanceList.clear();
powerList.clear();
@@ -324,32 +330,35 @@ public class KMLTrackExporter implements TrackExporter {
void writeCloseSegment() {
printWriter.println("<ExtendedData>");
printWriter.println("<SchemaData schemaUrl=\"#" + SCHEMA_ID + "\">");
writeTrackPointType(trackpointTypeList);
if (speedList.stream().anyMatch(Objects::nonNull)) {
writeSimpleArrayData(speedList, EXTENDED_DATA_TYPE_SPEED);
writeSimpleArraySensorData(speedList, EXTENDED_DATA_TYPE_SPEED);
}
if (distanceList.stream().anyMatch(Objects::nonNull)) {
writeSimpleArrayData(distanceList, EXTENDED_DATA_TYPE_DISTANCE);
writeSimpleArraySensorData(distanceList, EXTENDED_DATA_TYPE_DISTANCE);
}
if (powerList.stream().anyMatch(Objects::nonNull)) {
writeSimpleArrayData(powerList, EXTENDED_DATA_TYPE_POWER);
writeSimpleArraySensorData(powerList, EXTENDED_DATA_TYPE_POWER);
}
if (cadenceList.stream().anyMatch(Objects::nonNull)) {
writeSimpleArrayData(cadenceList, EXTENDED_DATA_TYPE_CADENCE);
writeSimpleArraySensorData(cadenceList, EXTENDED_DATA_TYPE_CADENCE);
}
if (heartRateList.stream().anyMatch(Objects::nonNull)) {
writeSimpleArrayData(heartRateList, EXTENDED_DATA_TYPE_HEART_RATE);
writeSimpleArraySensorData(heartRateList, EXTENDED_DATA_TYPE_HEART_RATE);
}
if (altitudeGainList.stream().anyMatch(Objects::nonNull)) {
writeSimpleArrayData(altitudeGainList, EXTENDED_DATA_TYPE_ALTITUDE_GAIN);
writeSimpleArraySensorData(altitudeGainList, EXTENDED_DATA_TYPE_ALTITUDE_GAIN);
}
if (altitudeLossList.stream().anyMatch(Objects::nonNull)) {
writeSimpleArrayData(altitudeLossList, EXTENDED_DATA_TYPE_ALTITUDE_LOSS);
writeSimpleArraySensorData(altitudeLossList, EXTENDED_DATA_TYPE_ALTITUDE_LOSS);
}
if (accuracyHorizontal.stream().anyMatch(Objects::nonNull)) {
writeSimpleArrayData(accuracyHorizontal, EXTENDED_DATA_TYPE_ACCURACY_HORIZONTAL);
writeSimpleArraySensorData(accuracyHorizontal, EXTENDED_DATA_TYPE_ACCURACY_HORIZONTAL);
}
if (accuracyVertical.stream().anyMatch(Objects::nonNull)) {
writeSimpleArrayData(accuracyVertical, EXTENDED_DATA_TYPE_ACCURACY_VERTICAL);
writeSimpleArraySensorData(accuracyVertical, EXTENDED_DATA_TYPE_ACCURACY_VERTICAL);
}
printWriter.println("</SchemaData>");
printWriter.println("</ExtendedData>");
@@ -360,6 +369,8 @@ public class KMLTrackExporter implements TrackExporter {
void writeTrackPoint(ZoneOffset zoneOffset, TrackPoint trackPoint) {
printWriter.println("<when>" + getTime(zoneOffset, trackPoint.getLocation()) + "</when>");
trackpointTypeList.add(trackPoint.getType());
if (trackPoint.hasLocation()) {
printWriter.println("<coord>" + getCoordinates(trackPoint.getLocation(), " ") + "</coord>");
} else {
@@ -378,13 +389,7 @@ public class KMLTrackExporter implements TrackExporter {
accuracyVertical.add(trackPoint.hasVerticalAccuracy() ? (float) trackPoint.getVerticalAccuracy().toM() : null);
}
/**
* Writes the simple array data.
*
* @param list a list of simple array data
* @param name the name of the simple array data
*/
private void writeSimpleArrayData(List<Float> list, String name) {
private void writeSimpleArraySensorData(List<Float> list, String name) {
printWriter.println("<SimpleArrayData name=\"" + name + "\">");
for (int i = 0; i < list.size(); i++) {
Float value = list.get(i);
@@ -397,14 +402,14 @@ public class KMLTrackExporter implements TrackExporter {
printWriter.println("</SimpleArrayData>");
}
/**
* Writes a placemark.
*
* @param name the name
* @param activityType the activityType
* @param description the description
* @param location the location
*/
private void writeTrackPointType(List<TrackPoint.Type> list) {
printWriter.println("<SimpleArrayData name=\"" + EXTENDED_DATA_TYPE_TRACKPOINT + "\">");
for (TrackPoint.Type value : list) {
printWriter.println("<value>" + value.name() + "</value>");
}
printWriter.println("</SimpleArrayData>");
}
private void writePlacemark(String name, String activityType, String description, Location location, ZoneOffset zoneOffset) {
if (location != null) {
printWriter.println("<Placemark>");
@@ -94,7 +94,9 @@ public class KmlTrackImporter extends DefaultHandler implements XMLImporter.Trac
private final ArrayList<Instant> whenList = new ArrayList<>();
private final ArrayList<Location> locationList = new ArrayList<>();
private String dataType;
private String dataType; //Could be converted to an ENUM
private final ArrayList<String> trackpointTypeList = new ArrayList<>();
private final ArrayList<Float> sensorSpeedList = new ArrayList<>();
private final ArrayList<Float> sensorDistanceList = new ArrayList<>();
private final ArrayList<Float> sensorCadenceList = new ArrayList<>();
@@ -287,6 +289,7 @@ public class KmlTrackImporter extends DefaultHandler implements XMLImporter.Trac
locationList.clear();
whenList.clear();
trackpointTypeList.clear();
sensorSpeedList.clear();
sensorDistanceList.clear();
sensorHeartRateList.clear();
@@ -313,6 +316,12 @@ public class KmlTrackImporter extends DefaultHandler implements XMLImporter.Trac
trackPoint.setLocation(location);
}
if (i < trackpointTypeList.size() && trackpointTypeList.get(i) != null) {
TrackPoint.Type type = TrackPoint.Type.valueOf(trackpointTypeList.get(i));
trackPoint.setType(type);
}
if (i < sensorSpeedList.size() && sensorSpeedList.get(i) != null) {
trackPoint.setSpeed(Speed.of(sensorSpeedList.get(i)));
}
@@ -399,6 +408,10 @@ public class KmlTrackImporter extends DefaultHandler implements XMLImporter.Trac
}
private void onExtendedDataValueEnd() throws SAXException {
if (dataType.equals(KMLTrackExporter.EXTENDED_DATA_TYPE_TRACKPOINT)) {
trackpointTypeList.add(content != null ? content.trim() : null);
return;
}
Float value = null;
if (content != null) {
content = content.trim();
@@ -5,10 +5,12 @@ import android.content.Context;
import android.content.SharedPreferences;
import android.database.sqlite.SQLiteException;
import android.net.Uri;
import android.os.Handler;
import android.util.Log;
import android.util.Pair;
import androidx.annotation.NonNull;
import androidx.annotation.VisibleForTesting;
import java.time.Duration;
import java.time.ZoneOffset;
@@ -28,19 +30,25 @@ import de.dennisguse.opentracks.stats.TrackStatistics;
import de.dennisguse.opentracks.stats.TrackStatisticsUpdater;
import de.dennisguse.opentracks.util.TrackNameUtils;
class TrackRecordingManager implements SharedPreferences.OnSharedPreferenceChangeListener {
public class TrackRecordingManager implements SharedPreferences.OnSharedPreferenceChangeListener {
private static final String TAG = TrackRecordingManager.class.getSimpleName();
private static final AltitudeCorrectionManager ALTITUDE_CORRECTION_MANAGER = new AltitudeCorrectionManager();
private final Runnable ON_IDLE = this::onIdle;
private final ContentProviderUtils contentProviderUtils;
private final Context context;
private final IdleObserver idleObserver;
private final Handler handler;
private final TrackPointCreator trackPointCreator;
private Distance recordingDistanceInterval;
private Distance maxRecordingDistance;
private Duration idleDuration;
private Track.Id trackId;
private TrackStatisticsUpdater trackStatisticsUpdater;
@@ -52,15 +60,17 @@ class TrackRecordingManager implements SharedPreferences.OnSharedPreferenceChang
private TrackPoint lastStoredTrackPoint;
private TrackPoint lastStoredTrackPointWithLocation;
TrackRecordingManager(Context context, TrackPointCreator trackPointCreator) {
TrackRecordingManager(Context context, TrackPointCreator trackPointCreator, IdleObserver idleObserver, Handler handler) {
this.context = context;
this.idleObserver = idleObserver;
this.trackPointCreator = trackPointCreator;
this.handler = handler;
contentProviderUtils = new ContentProviderUtils(context);
}
Track.Id startNewTrack() {
TrackPoint segmentStartTrackPoint = trackPointCreator.createSegmentStartManual();
// Create new track
ZoneOffset zoneOffset = ZoneOffset.systemDefault().getRules().getOffset(segmentStartTrackPoint.getTime());
Track track = new Track(zoneOffset);
trackId = contentProviderUtils.insertTrack(track);
@@ -74,7 +84,6 @@ class TrackRecordingManager implements SharedPreferences.OnSharedPreferenceChang
track.setActivityTypeLocalized(activityTypeLocalized);
track.setActivityType(ActivityType.findByLocalizedString(context, activityTypeLocalized));
track.setTrackStatistics(trackStatisticsUpdater.getTrackStatistics());
//TODO Pass TrackPoint
track.setName(TrackNameUtils.getTrackName(context, trackId, track.getStartTime()));
contentProviderUtils.updateTrack(track);
@@ -100,7 +109,7 @@ class TrackRecordingManager implements SharedPreferences.OnSharedPreferenceChang
return true;
}
void end() {
void endCurrentTrack() {
TrackPoint segmentEnd = trackPointCreator.createSegmentEnd();
insertTrackPoint(segmentEnd, true);
@@ -154,16 +163,30 @@ class TrackRecordingManager implements SharedPreferences.OnSharedPreferenceChang
return new Marker.Id(ContentUris.parseId(uri));
}
@VisibleForTesting(otherwise = VisibleForTesting.PACKAGE_PRIVATE)
public void onIdle() {
Log.d(TAG, "Becoming idle");
onNewTrackPoint(trackPointCreator.createIdle());
idleObserver.onIdle();
}
/**
* @return TrackPoint was stored?
*/
boolean onNewTrackPoint(@NonNull TrackPoint trackPoint) {
synchronized boolean onNewTrackPoint(@NonNull TrackPoint trackPoint) {
if (trackPoint.hasSpeed()) {
lastTrackPointUIWithSpeed = trackPoint;
}
if (trackPoint.hasAltitude()) {
lastTrackPointUIWithAltitude = trackPoint;
}
if (trackPoint.getType() == TrackPoint.Type.IDLE) {
insertTrackPoint(trackPoint, true);
handler.removeCallbacks(ON_IDLE);
return true;
}
//Storing trackPoint
// Always insert the first segment location
@@ -184,10 +207,10 @@ class TrackRecordingManager implements SharedPreferences.OnSharedPreferenceChang
if (!shouldStore) {
Log.d(TAG, "Ignoring TrackPoint as it has no distance (and sensor data is not new enough).");
return false;
} else {
insertTrackPoint(trackPoint, true);
return true;
}
insertTrackPoint(trackPoint, true);
return true;
}
Distance distanceToLastStoredTrackPoint;
@@ -200,18 +223,17 @@ class TrackRecordingManager implements SharedPreferences.OnSharedPreferenceChang
if (distanceToLastStoredTrackPoint.greaterThan(maxRecordingDistance)) {
trackPoint.setType(TrackPoint.Type.SEGMENT_START_AUTOMATIC);
insertTrackPoint(trackPoint, true);
handler.removeCallbacks(ON_IDLE);
handler.postDelayed(ON_IDLE, idleDuration.toMillis());
return true;
}
if (distanceToLastStoredTrackPoint.greaterOrEqualThan(recordingDistanceInterval)
&& trackPoint.isMoving()) {
if (distanceToLastStoredTrackPoint.greaterOrEqualThan(recordingDistanceInterval)) {
insertTrackPoint(trackPoint, false);
return true;
}
if (trackPoint.isMoving() != lastStoredTrackPoint.isMoving()) {
// Moving from non-moving to moving or vice versa; required to compute moving time correctly.
insertTrackPoint(trackPoint, true);
handler.removeCallbacks(ON_IDLE);
handler.postDelayed(ON_IDLE, idleDuration.toMillis());
return true;
}
@@ -252,6 +274,7 @@ class TrackRecordingManager implements SharedPreferences.OnSharedPreferenceChang
lastStoredTrackPointWithLocation = lastStoredTrackPoint;
}
} catch (SQLiteException e) {
// TODO Remove; if this is a problem; use a synchronized method.
/*
* Insert failed, most likely because of SqlLite error code 5 (SQLite_BUSY).
* This is expected to happen extremely rarely (if our listener gets invoked twice at about the same time).
@@ -277,5 +300,12 @@ class TrackRecordingManager implements SharedPreferences.OnSharedPreferenceChang
if (PreferencesUtils.isKey(R.string.max_recording_distance_key, key)) {
maxRecordingDistance = PreferencesUtils.getMaxRecordingDistance();
}
if (PreferencesUtils.isKey(R.string.idle_duration_key, key)) {
idleDuration = PreferencesUtils.getIdleDurationTimeout();
}
}
public interface IdleObserver {
void onIdle();
}
}
@@ -47,7 +47,7 @@ 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 {
public class TrackRecordingService extends Service implements TrackPointCreator.Callback, SharedPreferences.OnSharedPreferenceChangeListener, TrackRecordingManager.IdleObserver {
private static final String TAG = TrackRecordingService.class.getSimpleName();
@@ -107,8 +107,8 @@ public class TrackRecordingService extends Service implements TrackPointCreator.
gpsStatusObservable = new MutableLiveData<>(STATUS_GPS_DEFAULT);
recordingDataObservable = new MutableLiveData<>(NOT_RECORDING);
trackPointCreator = new TrackPointCreator(this, this, handler);
trackRecordingManager = new TrackRecordingManager(this, trackPointCreator);
trackPointCreator = new TrackPointCreator(this);
trackRecordingManager = new TrackRecordingManager(this, trackPointCreator, this , handler);
voiceAnnouncementManager = new VoiceAnnouncementManager(this);
notificationManager = new TrackRecordingServiceNotificationManager(this);
@@ -214,7 +214,7 @@ public class TrackRecordingService extends Service implements TrackPointCreator.
// Set recording status
updateRecordingStatus(STATUS_DEFAULT);
trackRecordingManager.end();
trackRecordingManager.endCurrentTrack();
stopUpdateRecordingData();
@@ -296,11 +296,15 @@ public class TrackRecordingService extends Service implements TrackPointCreator.
// Compute temporary track statistics using sensorData and update time.
Pair<Track, Pair<TrackPoint, SensorDataSet>> data = trackRecordingManager.getDataForUI();
voiceAnnouncementManager.update(this, data.first);
voiceAnnouncementManager.announceStatisticsIfNeeded(data.first);
recordingDataObservable.postValue(new RecordingData(data.first, data.second.first, data.second.second));
}
public void onIdle() {
voiceAnnouncementManager.announceIdle();
}
@VisibleForTesting
public void stopUpdateRecordingData() {
handler.removeCallbacks(updateRecordingData);
@@ -31,37 +31,19 @@ import java.util.List;
import java.util.Locale;
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.TrackPoint;
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;
/**
* This class will announce the user's {@link TrackStatistics}.
*
* @author Sandor Dornbush
*/
public class VoiceAnnouncement {
public class TTSManager {
public final static int AUDIO_STREAM = TextToSpeech.Engine.DEFAULT_STREAM;
private static final String TAG = VoiceAnnouncement.class.getSimpleName();
private static final String TAG = TTSManager.class.getSimpleName();
private final Context context;
private final AudioManager audioManager;
private final ContentProviderUtils contentProviderUtils;
private TrackPoint.Id startTrackPointId = null;
private IntervalStatistics intervalStatistics;
private Distance intervalDistance;
private final AudioManager.OnAudioFocusChangeListener audioFocusChangeListener = new AudioManager.OnAudioFocusChangeListener() {
@Override
public void onAudioFocusChange(int focusChange) {
@@ -108,12 +90,9 @@ public class VoiceAnnouncement {
private MediaPlayer ttsFallback;
VoiceAnnouncement(Context context) {
TTSManager(Context context) {
this.context = context;
audioManager = (AudioManager) context.getSystemService(Context.AUDIO_SERVICE);
contentProviderUtils = new ContentProviderUtils(context);
intervalDistance = PreferencesUtils.getVoiceAnnouncementDistance();
intervalStatistics = new IntervalStatistics(intervalDistance);
}
public void start() {
@@ -139,7 +118,7 @@ public class VoiceAnnouncement {
}
}
public void announce(@NonNull Track track) {
public void announce(@NonNull Spannable announcement) {
synchronized (this) {
if (!ttsReady) {
ttsReady = ttsInitStatus == TextToSpeech.SUCCESS;
@@ -166,23 +145,6 @@ public class VoiceAnnouncement {
return;
}
Distance currentIntervalDistance = PreferencesUtils.getVoiceAnnouncementDistance();
if (currentIntervalDistance != intervalDistance) {
intervalStatistics = new IntervalStatistics(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());
}
Spannable announcement = VoiceAnnouncementUtils.getAnnouncement(context, track.getTrackStatistics(), PreferencesUtils.getUnitSystem(), PreferencesUtils.isReportSpeed(track), lastInterval, sensorStatistics);
if (announcement.length() > 0) {
// We don't care about the utterance id. It is supplied here to force onUtteranceCompleted to be called.
tts.speak(announcement, TextToSpeech.QUEUE_FLUSH, null, "not used");
@@ -17,6 +17,7 @@ package de.dennisguse.opentracks.services.announcement;
import android.content.Context;
import android.content.SharedPreferences;
import android.text.Spannable;
import android.util.Log;
import androidx.annotation.NonNull;
@@ -27,11 +28,15 @@ import androidx.mediarouter.media.MediaRouter;
import java.time.Duration;
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.services.TrackRecordingService;
import de.dennisguse.opentracks.data.models.TrackPoint;
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;
/**
* Execute a periodic task on a time or distance schedule.
@@ -42,9 +47,9 @@ public class VoiceAnnouncementManager implements SharedPreferences.OnSharedPrefe
private static final String TAG = VoiceAnnouncementManager.class.getSimpleName();
private final TrackRecordingService trackRecordingService;
private final Context context;
private VoiceAnnouncement voiceAnnouncement;
private TTSManager voiceAnnouncement;
private TrackStatistics trackStatistics;
@@ -58,12 +63,22 @@ public class VoiceAnnouncementManager implements SharedPreferences.OnSharedPrefe
@NonNull
private Duration nextTotalTime = TOTALTIME_OFF;
public VoiceAnnouncementManager(@NonNull TrackRecordingService trackRecordingService) {
this.trackRecordingService = trackRecordingService;
private final ContentProviderUtils contentProviderUtils;
private TrackPoint.Id startTrackPointId = null;
private IntervalStatistics intervalStatistics;
private Distance intervalDistance;
public VoiceAnnouncementManager(@NonNull Context context) {
this.context = context;
contentProviderUtils = new ContentProviderUtils(context);
intervalDistance = PreferencesUtils.getVoiceAnnouncementDistance();
intervalStatistics = new IntervalStatistics(intervalDistance);
}
public void start(@Nullable TrackStatistics trackStatistics) {
voiceAnnouncement = new VoiceAnnouncement(trackRecordingService);
voiceAnnouncement = new TTSManager(context);
voiceAnnouncement.start();
update(trackStatistics);
}
@@ -74,10 +89,10 @@ public class VoiceAnnouncementManager implements SharedPreferences.OnSharedPrefe
updateNextTaskDistance();
}
public void update(@NonNull Context context, @NonNull Track track) {
private boolean shouldNotAnnounce() {
if (voiceAnnouncement == null) {
Log.e(TAG, "Cannot update when in status shutdown.");
return;
return true;
}
if (!PreferencesUtils.shouldVoiceAnnouncementOnDeviceSpeaker()
@@ -85,6 +100,26 @@ public class VoiceAnnouncementManager implements SharedPreferences.OnSharedPrefe
.getSelectedRoute()
.isDeviceSpeaker()) {
Log.i(TAG, "No voice announcement on device speaker.");
return true;
}
return false;
}
public void announceIdle() {
if (shouldNotAnnounce()) {
return;
}
if (!PreferencesUtils.shouldVoiceAnnouncementIdle()) {
return;
}
voiceAnnouncement.announce(VoiceAnnouncementUtils.createIdle(context));
}
public void announceStatisticsIfNeeded(@NonNull Track track) {
if (shouldNotAnnounce()) {
return;
}
@@ -100,10 +135,29 @@ public class VoiceAnnouncementManager implements SharedPreferences.OnSharedPrefe
}
if (announce) {
voiceAnnouncement.announce(track);
voiceAnnouncement.announce(createAnnouncement(track));
}
}
private Spannable createAnnouncement(Track track) {
Distance currentIntervalDistance = PreferencesUtils.getVoiceAnnouncementDistance();
if (currentIntervalDistance != intervalDistance) {
intervalStatistics = new IntervalStatistics(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());
}
return VoiceAnnouncementUtils.createStatistics(context, track.getTrackStatistics(), PreferencesUtils.getUnitSystem(), PreferencesUtils.isReportSpeed(track), lastInterval, sensorStatistics);
}
public void stop() {
if (voiceAnnouncement != null) {
voiceAnnouncement.stop();
@@ -33,7 +33,12 @@ class VoiceAnnouncementUtils {
private VoiceAnnouncementUtils() {
}
static Spannable getAnnouncement(Context context, TrackStatistics trackStatistics, UnitSystem unitSystem, boolean isReportSpeed, @Nullable IntervalStatistics.Interval currentInterval, @Nullable SensorStatistics sensorStatistics) {
static Spannable createIdle(Context context) {
return new SpannableStringBuilder()
.append(context.getString(R.string.voiceIdle));
}
static Spannable createStatistics(Context context, TrackStatistics trackStatistics, UnitSystem unitSystem, boolean isReportSpeed, @Nullable IntervalStatistics.Interval currentInterval, @Nullable SensorStatistics sensorStatistics) {
SpannableStringBuilder builder = new SpannableStringBuilder();
Distance totalDistance = trackStatistics.getTotalDistance();
Speed averageMovingSpeed = trackStatistics.getAverageMovingSpeed();
@@ -36,16 +36,11 @@ public class TrackPointCreator implements SharedPreferences.OnSharedPreferenceCh
private Clock clock = new MonotonicClock();
private SensorManager sensorManager;
public TrackPointCreator(Callback service, Context context, Handler handler) {
public TrackPointCreator(Callback service) {
this.service = service;
this.sensorManager = new SensorManager(this);
}
@VisibleForTesting
TrackPointCreator(Callback service) {
this.service = service;
}
public synchronized void start(@NonNull Context context, @NonNull Handler handler) {
this.context = context;
@@ -106,6 +101,13 @@ public class TrackPointCreator implements SharedPreferences.OnSharedPreferenceCh
return segmentEnd;
}
public synchronized TrackPoint createIdle() {
TrackPoint idle = new TrackPoint(TrackPoint.Type.IDLE, createNow());
addSensorData(idle);
reset();
return idle;
}
public Pair<TrackPoint, SensorDataSet> createCurrentTrackPoint(@Nullable TrackPoint lastTrackPointUISpeed, @Nullable TrackPoint lastTrackPointUIAltitude, @Nullable TrackPoint lastStoredTrackPointWithLocation) {
TrackPoint currentTrackPoint = new TrackPoint(TrackPoint.Type.TRACKPOINT, createNow());
@@ -17,7 +17,7 @@ public class AnnouncementsSettingsFragment extends PreferenceFragmentCompat {
@Override
public void onStart() {
super.onStart();
((SettingsActivity) getActivity()).getSupportActionBar().setTitle(R.string.settings_announcements_title);
((SettingsActivity) getActivity()).getSupportActionBar().setTitle(R.string.settings_announcements_statistics_title);
}
@Override
@@ -79,7 +79,7 @@ public class GpsSettingsFragment extends PreferenceFragmentCompat {
ListPreference recordingGpsAccuracy = findPreference(getString(R.string.recording_gps_accuracy_key));
recordingGpsAccuracy.setEntries(PreferencesUtils.getThresholdHorizontalAccuracyEntries());
ListPreference idleSpeed = findPreference(getString(R.string.idle_speed_key));
idleSpeed.setEntries(PreferencesUtils.getIdleSpeedEntries());
ListPreference idleDuration = findPreference(getString(R.string.idle_duration_key));
idleDuration.setEntries(PreferencesUtils.getIdleDurationEntries());
}
}
@@ -45,7 +45,6 @@ import de.dennisguse.opentracks.data.models.Distance;
import de.dennisguse.opentracks.data.models.DistanceFormatter;
import de.dennisguse.opentracks.data.models.HeartRate;
import de.dennisguse.opentracks.data.models.HeartRateZones;
import de.dennisguse.opentracks.data.models.Speed;
import de.dennisguse.opentracks.data.models.Track;
import de.dennisguse.opentracks.io.file.TrackFileFormat;
import de.dennisguse.opentracks.io.file.TrackFilenameGenerator;
@@ -313,6 +312,10 @@ public class PreferencesUtils {
return getBoolean(R.string.voice_on_device_speaker_key, DEFAULT);
}
public static boolean shouldVoiceAnnouncementIdle() {
return getBoolean(R.string.voice_announce_idle_key, true);
}
public static Duration getVoiceAnnouncementFrequency() {
final int DEFAULT = Integer.parseInt(resources.getString(R.string.voice_announcement_frequency_default));
int value = getInt(R.string.voice_announcement_frequency_key, DEFAULT);
@@ -530,11 +533,12 @@ public class PreferencesUtils {
static String[] getMinRecordingIntervalEntries() {
String[] entryValues = resources.getStringArray(R.array.min_recording_interval_values);
long recommended = PreferencesUtils.getMinRecordingIntervalDefault().getSeconds();
String[] entries = new String[entryValues.length];
for (int i = 0; i < entryValues.length; i++) {
int value = Integer.parseInt(entryValues[i]);
if (value == PreferencesUtils.getMinRecordingIntervalDefault().getSeconds()) {
if (value == recommended) {
entries[i] = resources.getString(R.string.value_smallest_recommended);
} else {
entries[i] = value < 60 ? resources.getString(R.string.value_integer_second, value) : resources.getString(R.string.value_integer_minute, value / 60);
@@ -598,49 +602,25 @@ public class PreferencesUtils {
return entries;
}
public static Speed getIdleSpeed() {
final float DEFAULT = Float.parseFloat(resources.getString(R.string.idle_speed_default));
float value = getFloat(R.string.idle_speed_key, DEFAULT);
return Speed.ofKMH(value);
public static Duration getIdleDurationTimeout() {
final int DEFAULT = Integer.parseInt(resources.getString(R.string.idle_duration_default));
int value = getInt(R.string.idle_duration_key, DEFAULT);
return Duration.ofSeconds(value);
}
static String[] getIdleSpeedEntries() {
String[] entryValues = resources.getStringArray(R.array.idle_speed_values);
static String[] getIdleDurationEntries() {
String[] entryValues = resources.getStringArray(R.array.idle_duration_values);
String[] entries = new String[entryValues.length];
final float idleSpeedDefault = Float.parseFloat(resources.getString(R.string.idle_speed_default));
UnitSystem unitSystem = getUnitSystem();
final int idleDurationDefault = Integer.parseInt(resources.getString(R.string.idle_duration_default));
for (int i = 0; i < entryValues.length; i++) {
float value = Float.parseFloat(entryValues[i]);
int value = Integer.parseInt(entryValues[i]);
switch (unitSystem) {
case METRIC -> {
if (value == idleSpeedDefault) {
entries[i] = resources.getString(R.string.value_float_kilometer_hour_recommended, value);
} else {
entries[i] = resources.getString(R.string.value_float_kilometer_hour, value);
}
}
case IMPERIAL_FEET, IMPERIAL_METER -> {
double valueMPH = Speed.ofKMH(value).toMPH();
if (value == idleSpeedDefault) {
entries[i] = resources.getString(R.string.value_float_mile_hour_recommended, valueMPH);
} else {
entries[i] = resources.getString(R.string.value_float_mile_hour, valueMPH);
}
}
case NAUTICAL_IMPERIAL -> {
double valueKnots = Speed.ofKMH(value).toKnots();
if (value == idleSpeedDefault) {
entries[i] = resources.getString(R.string.value_float_knots_recommended, valueKnots);
} else {
entries[i] = resources.getString(R.string.value_float_knots, valueKnots);
}
}
default -> throw new RuntimeException("Not implemented");
if (value == idleDurationDefault) {
entries[i] = resources.getString(R.string.value_int_seconds, value);
} else {
entries[i] = value < 60 ? resources.getString(R.string.value_integer_second, value) : resources.getString(R.string.value_integer_minute, value / 60);
}
}
@@ -61,6 +61,8 @@ public class TrackStatistics {
// The average heart rate seen on this track
private HeartRate avgHeartRate = null;
private boolean isIdle;
public TrackStatistics() {
reset();
}
@@ -81,6 +83,7 @@ public class TrackStatistics {
totalAltitudeGain_m = other.totalAltitudeGain_m;
totalAltitudeLoss_m = other.totalAltitudeLoss_m;
avgHeartRate = other.avgHeartRate;
isIdle = other.isIdle;
}
@VisibleForTesting
@@ -168,6 +171,8 @@ public class TrackStatistics {
setMaxSpeed(Speed.zero());
setTotalAltitudeGain(null);
setTotalAltitudeLoss(null);
isIdle = false;
}
public void reset(Instant startTime) {
@@ -248,6 +253,14 @@ public class TrackStatistics {
return totalTime.minus(movingTime);
}
public boolean isIdle() {
return isIdle;
}
public void setIdle(boolean idle) {
isIdle = idle;
}
public boolean hasAverageHeartRate() {
return avgHeartRate != null;
}
@@ -16,8 +16,6 @@
package de.dennisguse.opentracks.stats;
import android.util.Log;
import androidx.annotation.NonNull;
import java.time.Duration;
@@ -40,11 +38,6 @@ import de.dennisguse.opentracks.data.models.TrackPoint;
public class TrackStatisticsUpdater {
private static final String TAG = TrackStatisticsUpdater.class.getSimpleName();
/**
* Ignore any acceleration faster than this.
* Will ignore any speeds that imply acceleration greater than 2g's
*/
private static final double SPEED_MAX_ACCELERATION = 2 * 9.81;
private final TrackStatistics trackStatistics;
@@ -132,26 +125,35 @@ public class TrackStatisticsUpdater {
currentSegment.setAverageHeartRate(HeartRate.of(averageHeartRateBPM));
}
// Update total distance
if (trackPoint.hasSensorDistance()) {
// Sensor-based distance/speed
currentSegment.addTotalDistance(trackPoint.getSensorDistance());
} else if (lastTrackPoint != null
&& lastTrackPoint.hasLocation()
&& trackPoint.hasLocation() && trackPoint.isMoving()) {
// GPS-based distance/speed
// Assumption: we ignore TrackPoints that are not moving as those are likely imprecise GPS measurements
Distance movingDistance = trackPoint.distanceToPrevious(lastTrackPoint);
currentSegment.addTotalDistance(movingDistance);
}
{
// Update total distance
Distance movingDistance = null;
if (trackPoint.hasSensorDistance()) {
movingDistance = trackPoint.getSensorDistance();
} else if (lastTrackPoint != null
&& lastTrackPoint.hasLocation()
&& trackPoint.hasLocation()) {
// GPS-based distance/speed
movingDistance = trackPoint.distanceToPrevious(lastTrackPoint);
}
if (movingDistance != null) {
currentSegment.setIdle(false);
currentSegment.addTotalDistance(movingDistance);
}
if (!currentSegment.isIdle() && !trackPoint.isSegmentManualStart()) {
if (lastTrackPoint != null) {
currentSegment.addMovingTime(trackPoint, lastTrackPoint);
}
}
// Update moving time
if (trackPoint.isMoving() && lastTrackPoint != null && lastTrackPoint.isMoving()) {
currentSegment.addMovingTime(trackPoint, lastTrackPoint);
if (trackPoint.getType() == TrackPoint.Type.IDLE) {
currentSegment.setIdle(true);
}
// Update max speed
updateSpeed(trackPoint, lastTrackPoint);
if (trackPoint.hasSpeed()) {
updateSpeed(trackPoint);
}
}
if (trackPoint.isSegmentManualEnd()) {
@@ -180,27 +182,13 @@ public class TrackStatisticsUpdater {
/**
* Updates a speed reading while assuming the user is moving.
*/
private void updateSpeed(@NonNull TrackPoint trackPoint, @NonNull TrackPoint lastTrackPoint) {
if (isValidSpeed(trackPoint, lastTrackPoint)) {
Speed currentSpeed = trackPoint.getSpeed();
if (currentSpeed.greaterThan(currentSegment.getMaxSpeed())) {
currentSegment.setMaxSpeed(currentSpeed);
}
} else {
Log.d(TAG, "Invalid speed. speed: " + trackPoint.getSpeed() + " lastLocationSpeed: " + lastTrackPoint.getSpeed());
private void updateSpeed(@NonNull TrackPoint trackPoint) {
Speed currentSpeed = trackPoint.getSpeed();
if (currentSpeed.greaterThan(currentSegment.getMaxSpeed())) {
currentSegment.setMaxSpeed(currentSpeed);
}
}
private boolean isValidSpeed(@NonNull TrackPoint trackPoint, @NonNull TrackPoint lastTrackPoint) {
// See if the speed seems physically likely. Ignore any speeds that imply acceleration greater than 2g.
Duration timeDifference = Duration.between(lastTrackPoint.getTime(), trackPoint.getTime());
Speed maxSpeedDifference = Speed.of(Distance.of(SPEED_MAX_ACCELERATION), Duration.ofMillis(1000))
.mul(timeDifference.toSeconds());
Speed speedDifference = Speed.absDiff(lastTrackPoint.getSpeed(), trackPoint.getSpeed());
return speedDifference.lessThan(maxSpeedDifference);
}
@NonNull
@Override
public String toString() {
+1 -2
View File
@@ -425,11 +425,10 @@
<string name="generic_filter">Filtrar</string>
<string name="menu_select_layout">Seleccionar diseño</string>
<string name="settings_import_export_title">Importar/Exportar</string>
<string name="settings_announcements_title">Avisos de voz</string>
<string name="settings_announcements_statistics_title">Avisos de voz</string>
<string name="settings_announcements_summary">Tiempo, Distancia, Velocidad de la voz</string>
<string name="settings_import_export_summary">Importar, Exportar, Autoexportar</string>
<string name="settings_units_category">Unidades</string>
<string name="settings_recording_idle_speed_title">Umbral de velocidad de ralentí</string>
<string name="custom_layout_select_layout">Seleccionar diseño</string>
<string name="stats_custom_layout_default_layout">Diseño predeterminado</string>
<string name="settings_generic_sensor">Genérico</string>
+1 -2
View File
@@ -477,7 +477,6 @@ limitations under the License.
<string name="permission_bluetooth_failed_rejected">V nastavení systému je třeba ručně povolit aplikaci OpenTracks přístup k blízkým zařízením.</string>
<string name="value_float_mile_hour">%1$.1f mi/h</string>
<string name="value_float_mile_hour_recommended">%1$.1f mil/h (doporučeno)</string>
<string name="settings_recording_idle_speed_title">Rychlost pro autopausu</string>
<string name="value_float_kilometer_hour">%1$.1f km/h</string>
<string name="value_float_kilometer_hour_recommended">%1$.1f km/h (doporučeno)</string>
<string name="stats_clock">Hodiny</string>
@@ -489,7 +488,7 @@ limitations under the License.
<string name="settings_import_export_summary">Import, export, automatický export</string>
<string name="settings_import_export_title">Import/Export</string>
<string name="settings_announcements_summary">Čas, vzdálenost, rychlost hlasu</string>
<string name="settings_announcements_title">Hlasová oznámení</string>
<string name="settings_announcements_statistics_title">Hlasová oznámení</string>
<string name="settings_sensors_title">Bluetooth senzory</string>
<string name="settings_gps_summary">Časový interval, Interval vzdálenosti, Maximální vzdálenost, Přesnost</string>
<string name="settings_ui_summary">Vzhled, Rozložení záznamu, Téma</string>
+1 -2
View File
@@ -458,7 +458,7 @@ Hvis GPS-enheden rapporterer unøjagtige data (f.eks. Placering, hastighed, høj
<string name="settings_defaults_summary">Foretrukne enheder, standardaktivitet, spor standardnavn</string>
<string name="settings_gps_summary">Tidsinterval, Distanceinterval, Maksimal afstand, Nøjagtighed</string>
<string name="settings_sensors_summary">Cykelsensorer, Hjulomkreds, Løbesensorer</string>
<string name="settings_announcements_title">Stemmemeddelelser</string>
<string name="settings_announcements_statistics_title">Stemmemeddelelser</string>
<string name="settings_announcements_summary">Tid, afstand, stemmehastighed</string>
<string name="settings_import_export_title">Import/eksport</string>
<string name="settings_import_export_summary">Import, eksport, automatisk eksport</string>
@@ -487,7 +487,6 @@ Hvis GPS-enheden rapporterer unøjagtige data (f.eks. Placering, hastighed, høj
<string name="settings_import_section">Importere</string>
<string name="settings_export_section">Eksport</string>
<string name="stats_clock">Klokken</string>
<string name="settings_recording_idle_speed_title">Tærskel for tomgangshastighed</string>
<string name="value_float_mile_hour_recommended">%1$.1f mi/h (anbefalet)</string>
<string name="value_float_mile_hour">%1$.1f mi/h</string>
<string name="generic_to">Til</string>
+1 -2
View File
@@ -496,12 +496,11 @@ Meldet das GPS-Gerät ungenaue Daten (z.B. Standort, Geschwindigkeit, Höhe), ka
<string name="description_speed_source_gps">GPS</string>
<string name="menu_select_layout">Wähle Layout</string>
<string name="generic_choose_an_option">Wähle eine Option</string>
<string name="settings_recording_idle_speed_title">Inaktivitätsgeschwindigkeit</string>
<string name="settings_gps_title">GPS</string>
<string name="settings_import_export_title">Import/Export</string>
<string name="settings_sensors_title">Bluetooth Sensoren</string>
<string name="settings_sensors_summary">Fahrradsensoren, Radumfang und Lauf-Sensoren</string>
<string name="settings_announcements_title">Sprachansagen</string>
<string name="settings_announcements_statistics_title">Sprachansagen</string>
<string name="generic_undo">Rückgängig</string>
<string name="generic_today">Heute</string>
<string name="generic_yesterday">Gestern</string>
+1 -2
View File
@@ -507,7 +507,6 @@ limitations under the License.
<string name="value_float_kilometer_hour">%1$.1f km/h</string>
<string name="value_float_mile_hour">%1$.1f mi/h</string>
<string name="value_float_mile_hour_recommended">%1$.1f mi/h (recomendado)</string>
<string name="settings_recording_idle_speed_title">Umbral de velocidad de ralentí</string>
<string name="stats_clock">Reloj</string>
<string name="value_float_kilometer_hour_recommended">%1$.1f km/h (recomendado)</string>
<string name="settings_generic_sensor">Genérico</string>
@@ -526,7 +525,7 @@ limitations under the License.
<string name="settings_ui_title">Interfaz de usuario</string>
<string name="settings_ui_summary">Apariencia, Diseño de grabación, Tema</string>
<string name="settings_gps_title">GPS</string>
<string name="settings_announcements_title">Avisos de voz</string>
<string name="settings_announcements_statistics_title">Avisos de voz</string>
<string name="settings_units_category">Unidades</string>
<string name="settings_import_export_title">Importar/Exportar</string>
<string name="stats_custom_layout_default_layout">Diseño predeterminado</string>
+1 -2
View File
@@ -450,7 +450,6 @@ limitations under the License.
<string name="stats_clock">Kell</string>
<string name="value_float_kilometer_hour_recommended">%1$.1f km/h (soovitatav)</string>
<string name="value_float_kilometer_hour">%1$.1f km/h</string>
<string name="settings_recording_idle_speed_title">Tühikäigu kiiruse lävi</string>
<string name="settings_announcements_distance_frequency">Vahemaa intervall</string>
<string name="description_speed_source_gps">GPS</string>
<string name="settings_cycling_sensor">Jalgrattasõit</string>
@@ -469,7 +468,7 @@ limitations under the License.
<string name="settings_ui_summary">Välimus, Salvestamise Paigutus, Teema</string>
<string name="settings_gps_summary">Ajavahemik, vahemaa intervall, maksimaalne kaugus, täpsus</string>
<string name="settings_sensors_title">Bluetooth Andurid</string>
<string name="settings_announcements_title">Häälteated</string>
<string name="settings_announcements_statistics_title">Häälteated</string>
<string name="settings_announcements_summary">Aeg, Kaugus, Hääle Kiirus</string>
<string name="settings_units_category">Ühikud</string>
<string name="settings_import_export_summary">Import, Eksport, Automaatne Eksport</string>
+1 -2
View File
@@ -477,7 +477,7 @@ GPS gailuak datu okerrak salatzen baditu (adibidez, kokapena, abiadura, kota), O
<string name="value_float_mile_hour_recommended">%1$.1f mi/h (gomendatua)</string>
<string name="settings_sensors_title">Bluetooth</string>
<string name="settings_gps_summary">Denbora-tartea, Distantzia-tartea, Gehienezko Distantzia, Zehaztasuna</string>
<string name="settings_announcements_title">Ahots Iragarkiak</string>
<string name="settings_announcements_statistics_title">Ahots Iragarkiak</string>
<string name="settings_announcements_summary">Denbora, Distantzia, Ahotsaren Abiadura</string>
<string name="settings_import_export_title">Inportatu/Esportatu</string>
<string name="settings_import_export_summary">Inportatu, Esportatu, Auto-Export</string>
@@ -485,7 +485,6 @@ GPS gailuak datu okerrak salatzen baditu (adibidez, kokapena, abiadura, kota), O
<string name="settings_sensors_summary">Txirrindularitza Sentsoreak, Gurpila Zirkunferentzia, Sentsore Exekutatzen</string>
<string name="settings_recording_customize_layout_select_columns">Datu-eremuak errenkadako</string>
<string name="settings_recording_customize_layout_title">Pertsonalizatu zure grabazioaren diseinua</string>
<string name="settings_recording_idle_speed_title">Inaktibitatearen abiaduraren muga</string>
<string name="settings_announcements_totaltime_frequency">Denbora-tartea</string>
<string name="settings_announcements_distance_frequency">Distantzia tartea</string>
<string name="settings_running_sensor">Exekutatzen</string>
+1 -2
View File
@@ -521,7 +521,6 @@ limitations under the License.
<string name="settings_public_api_dashboard_enabled_title">Automaattinen tiedonsiirto</string>
<string name="settings_public_api_dashboard_enabled_summary_on">Tallennuksen aloittanut sovellus voi myös käyttää tallennettuja tietoja.</string>
<string name="menu_filter">Suodatin</string>
<string name="settings_recording_idle_speed_title">Joutokäyntinopeuden kynnysarvo</string>
<string name="stats_sensors_cadence">Kadenssi</string>
<string name="stats_clock">Kello</string>
<string name="current_heart_rate">Nykyinen syke</string>
@@ -540,7 +539,7 @@ limitations under the License.
<string name="settings_defaults_summary">Suositeltavat yksiköt, Oletusaktiviteetti, Seurannan oletusnimi</string>
<string name="select_show_on_map_behavior">Valitse\" Näytä kartalla\"käyttäytyminen</string>
<string name="settings_sensors_summary">Pyöräilyanturit, Pyörän ympärysmitta, Juoksuanturit</string>
<string name="settings_announcements_title">Ääni-ilmoitukset</string>
<string name="settings_announcements_statistics_title">Ääni-ilmoitukset</string>
<string name="settings_announcements_summary">Aika, etäisyys, äänennopeus</string>
<string name="settings_gps_summary">Aikaväli, etäisyysväli, maksimietäisyys, tarkkuus</string>
</resources>
+1 -2
View File
@@ -511,7 +511,6 @@ Si le dispositif GPS signale des données inexactes (par exemple, la localisatio
<string name="value_float_kilometer_hour_recommended">%1$.1f km/h (recommandé)</string>
<string name="value_float_mile_hour">%1$.1f mi/h</string>
<string name="value_float_mile_hour_recommended">%1$.1f mi/h (recommandé)</string>
<string name="settings_recording_idle_speed_title">Seuil de vitesse au ralenti</string>
<string name="stats_clock">Horloge</string>
<string name="value_float_kilometer_hour">%1$.1f km/h</string>
<string name="settings_generic_sensor">Générique</string>
@@ -527,7 +526,7 @@ Si le dispositif GPS signale des données inexactes (par exemple, la localisatio
<string name="settings_import_export_summary">Importez, Exportez, Auto Export</string>
<string name="settings_import_export_title">Importez/Exportez</string>
<string name="settings_announcements_summary">Temps, distance, vitesse de la voix</string>
<string name="settings_announcements_title">Annonces vocales</string>
<string name="settings_announcements_statistics_title">Annonces vocales</string>
<string name="settings_sensors_title">Capteurs Bluetooth</string>
<string name="settings_gps_summary">Intervalle de temps, Intervalle de distance, Distance maximale, Précision</string>
<string name="settings_gps_title">GPS</string>
+1 -2
View File
@@ -238,7 +238,6 @@
<string name="settings_recording_track_name_date_local_option">Dáta (áitiúil)</string>
<string name="settings_recording_track_name_number_option">Uimhir</string>
<string name="settings_recording_track_name_title">Ainm rian réamhshocraithe</string>
<string name="settings_recording_idle_speed_title">Tairseach luais díomhaoin</string>
<string name="settings_night_mode_title">Téama Chomhéadain</string>
<string name="settings_night_mode_option_system">Córas</string>
<string name="settings_night_mode_option_day">Lá</string>
@@ -353,7 +352,7 @@
<string name="settings_reset_summary">Athshocraigh Socruithe go Luachanna Réamhshocraithe</string>
<string name="settings_announcements_totaltime_frequency">Eatramh ama</string>
<string name="settings_layout_reset_confirm_message">Cuirfear gach socrú leagan amach ar ais chuig na luachanna réamhshocraithe. Ní scriosfaidh sé seo aon rianta ar an bhfeiste.</string>
<string name="settings_announcements_title">Fógraí Gutha</string>
<string name="settings_announcements_statistics_title">Fógraí Gutha</string>
<string name="settings_recording_show_on_lockscreen_while_recording_title">Taispeáin staitisticí ar an scáileán glas</string>
<string name="settings_announcements_average_heart_rate">Meánráta croí</string>
<string name="settings_layout_reset_confirm_title">Athshocraigh do leagan amach\?</string>
+1 -2
View File
@@ -477,7 +477,6 @@ Se o GPS informa sobre datos pouco precisos (ex.: localización, velocidade, ele
<string name="settings_announcements_distance_frequency">Intervalo de distancia</string>
<string name="permission_bluetooth_failed">OpenTracks require permiso para usar o Bluetooth.</string>
<string name="permission_bluetooth_failed_rejected">Tes que permitirlle a OpenTracks nos axustes do sistema que acceda a Dispositivos Próximos.</string>
<string name="settings_recording_idle_speed_title">Limiar detección movemento</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>
@@ -493,7 +492,7 @@ Se o GPS informa sobre datos pouco precisos (ex.: localización, velocidade, ele
<string name="settings_gps_summary">Intervalo de tempo, Distancia intervalo, Max Distancia, Precisión</string>
<string name="settings_sensors_title">Sensores Bluetooth</string>
<string name="settings_sensors_summary">Sensores de ciclismo, Circunferencia da roda, Sensores de carreira</string>
<string name="settings_announcements_title">Indicacións por Voz</string>
<string name="settings_announcements_statistics_title">Indicacións por Voz</string>
<string name="settings_import_export_title">Importación/Exportación</string>
<string name="settings_import_export_summary">Importación, Exportación, Auto Exportación</string>
<string name="settings_units_category">Unidades</string>
+1 -2
View File
@@ -466,7 +466,7 @@ limitations under the License.
<string name="settings_sensors_title">Bluetooth Érzékelők</string>
<string name="generic_dismiss">Elbocsátás</string>
<string name="settings_sensors_summary">Kerékpáros Érzékelők, Kerék Kerülete, Futásérzékelők</string>
<string name="settings_announcements_title">Voice Közlemények</string>
<string name="settings_announcements_statistics_title">Voice Közlemények</string>
<string name="settings_export_filename_title">A fájlnév formátuma</string>
<string name="settings_public_api_dashboard_enabled_summary_off">A rögzített adatokat nem osztják meg automatikusan más alkalmazásokkal.</string>
<string name="field_remove_from_layout">Eltávolítás</string>
@@ -474,7 +474,6 @@ limitations under the License.
<string name="stats_sensors_cadence">Ütem</string>
<string name="settings_all_reset">Visszaállítás</string>
<string name="settings_layout_reset_confirm_message">Minden elrendezési beállítás visszaáll az alapértelmezett értékekre. Ez nem törli az eszközön lévő zeneszámokat.</string>
<string name="settings_recording_idle_speed_title">Alapjárati fordulatszám-küszöbérték</string>
<string name="custom_layout_list_title">Elrendezések</string>
<string name="custom_layout_list_edit_name">Írja be az elrendezés nevét a hozzáadáshoz</string>
<string name="aggregated_stats_filter_no_results">A megadott szűrőhöz nincsenek tevékenységek</string>
+1 -2
View File
@@ -456,7 +456,6 @@ Infatti, un\'applicazione deve supportare <i>ACTION_VIEW</i> con MIME <i>applica
<string name="settings_sensor_running_speed_and_cadence">Distanza, Velocità e Cadenza</string>
<string name="stats_clock">Orologio</string>
<string name="settings_announcements_totaltime_frequency">Intervallo di tempo</string>
<string name="settings_recording_idle_speed_title">Soglia velocità di movimento</string>
<string name="settings_announcements_distance_frequency">Intervallo di distanza</string>
<string name="location_latitude_longitude">%1$s, %2$s</string>
<string name="location_coordinate">%1$s°</string>
@@ -490,7 +489,7 @@ Infatti, un\'applicazione deve supportare <i>ACTION_VIEW</i> con MIME <i>applica
<string name="settings_gps_title">GPS</string>
<string name="settings_gps_summary">Intervallo di tempo, Intervallo di distanza, Massima Distanza, Precisione</string>
<string name="settings_sensors_title">Sensori Bluetooth</string>
<string name="settings_announcements_title">Annunci Vocali</string>
<string name="settings_announcements_statistics_title">Annunci Vocali</string>
<string name="settings_announcements_summary">Tempo, Distanza, Velocità della Voce</string>
<string name="settings_import_export_title">Importa/Esporta</string>
<string name="settings_import_export_summary">Importa, Esporta, Auto Esporta</string>
+1 -2
View File
@@ -472,7 +472,6 @@ limitations under the License.
<string name="settings_export_section">Eksport</string>
<string name="value_float_kilometer_hour">%1$.1f km/t</string>
<string name="value_float_kilometer_hour_recommended">%1$.1f km/t (anbefales)</string>
<string name="settings_recording_idle_speed_title">Terskel for tomgangshastighet</string>
<string name="value_float_mile_hour">%1$.1f mi/h</string>
<string name="value_float_mile_hour_recommended">%1$.1f mi/h (anbefales)</string>
<string name="stats_clock">Klokke</string>
@@ -485,7 +484,7 @@ limitations under the License.
<string name="settings_gps_title">GPS</string>
<string name="settings_gps_summary">Tidsintervall, distanseintervall, maks, distanse, nøyaktighet</string>
<string name="settings_sensors_title">Blåtannssensorer</string>
<string name="settings_announcements_title">Talemeldinger</string>
<string name="settings_announcements_statistics_title">Talemeldinger</string>
<string name="settings_announcements_summary">Tid, distanse, talehastighet</string>
<string name="settings_import_export_title">Import/eksport</string>
<string name="settings_import_export_summary">Import, eksport, auto-eksport</string>
+1 -2
View File
@@ -458,7 +458,6 @@ Als het GPS-apparaat onnauwkeurige gegevens rapporteert (bijv. locatie, snelheid
<string name="settings_ui_title">User Interface</string>
<string name="stats_coordinates">Coördinaten</string>
<string name="settings_announcements_distance_frequency">Afstandsinterval</string>
<string name="settings_recording_idle_speed_title">Drempelwaarde stationair</string>
<string name="value_float_kilometer_hour">%1$.1f km/u</string>
<string name="settings_api_title">Publieke API</string>
<string name="settings_public_api_enabled_title">Public API</string>
@@ -533,7 +532,7 @@ Als het GPS-apparaat onnauwkeurige gegevens rapporteert (bijv. locatie, snelheid
<string name="settings_gps_summary">Tijdsinterval, afstandsinterval, maximale afstand, nauwkeurigheid</string>
<string name="settings_sensors_title">Bluetooth sensoren</string>
<string name="settings_sensors_summary">Fiets sensoren, wielomtrek, loop sensoren</string>
<string name="settings_announcements_title">Gesproken berichten</string>
<string name="settings_announcements_statistics_title">Gesproken berichten</string>
<string name="custom_layout_list_title">Lay-outs</string>
<string name="custom_layout_list_edit_name">Voer de naam van de lay-out in</string>
<string name="custom_layout_list_edit_already_exists">Lay-out naam bestaat al</string>
+1 -2
View File
@@ -456,7 +456,6 @@ limitations under the License.
<string name="stats_sensors_heart_rate">Częstość akcji serca</string>
<string name="stats_clock">Zegar</string>
<string name="settings_announcements_distance_frequency">Przedział dystansu</string>
<string name="settings_recording_idle_speed_title">Próg prędkości bezobciążeń</string>
<string name="settings_layout_reset_confirm_title">Zresetować twoje układy\?</string>
<string name="settings_cycling_sensor">Kolarstwo</string>
<string name="settings_running_sensor">Bieganie</string>
@@ -475,7 +474,7 @@ limitations under the License.
<string name="settings_gps_title">GPS</string>
<string name="settings_sensors_title">Czujnik Bluetooth</string>
<string name="settings_sensors_summary">Czujniki rowerowe, Obwód koła, Czujniki biegowe</string>
<string name="settings_announcements_title">Wiadomości głosowe</string>
<string name="settings_announcements_statistics_title">Wiadomości głosowe</string>
<string name="settings_announcements_summary">Czas, Dystans, Głos prędkości</string>
<string name="settings_import_export_title">Import/Eksport</string>
<string name="settings_units_category">Jednostki</string>
+1 -2
View File
@@ -427,9 +427,8 @@
<string name="settings_export_section">Exportar</string>
<string name="value_float_kilometer_hour">%1$.1f km/h</string>
<string name="value_float_kilometer_hour_recommended">%1$.1f km/h (Recomendado)</string>
<string name="settings_recording_idle_speed_title">Limite de velocidade de marcha lenta</string>
<string name="stats_clock">Relógio</string>
<string name="settings_announcements_title">Anúncios de voz</string>
<string name="settings_announcements_statistics_title">Anúncios de voz</string>
<string name="settings_sensors_summary">Voice announcements</string>
<string name="settings_sensors_title">Sensores Bluetooth</string>
<string name="settings_gps_summary">Intervalo de tempo, Intervalo de distância, Distância máxima, Precisão</string>
+1 -2
View File
@@ -428,7 +428,7 @@
<string name="settings_gps_summary">Intervalo de tempo, Intervalo de distância, Distância máxima, Precisão</string>
<string name="settings_sensors_title">Sensores Bluetooth</string>
<string name="settings_sensors_summary">Voice announcements</string>
<string name="settings_announcements_title">Anúncios de voz</string>
<string name="settings_announcements_statistics_title">Anúncios de voz</string>
<string name="settings_announcements_summary">Tempo, distância, velocidade da voz</string>
<string name="settings_import_export_title">Importar/Exportar</string>
<string name="settings_import_export_summary">Importar, Exportar, Auto Export</string>
@@ -438,7 +438,6 @@
<string name="settings_recording_keep_screen_on_while_recording_title">Mantenha o ecrã ligada</string>
<string name="settings_recording_fullscreen_on_while_recording_summary">Durante a gravação, apresente em ecrã inteiro.</string>
<string name="settings_recording_keep_screen_on_while_recording_summary">Durante a gravação, mantenha o ecrã ligado.</string>
<string name="settings_recording_idle_speed_title">Limite de velocidade de marcha lenta</string>
<string name="settings_night_mode_title">Tema UI</string>
<string name="settings_night_mode_option_system">Sistema</string>
<string name="settings_night_mode_option_night">Noite</string>
+1 -2
View File
@@ -460,12 +460,11 @@ limitations under the License.
<string name="settings_sensors_summary">Voice announcements</string>
<string name="settings_ui_summary">Aparência, Layout de gravação, Tema</string>
<string name="settings_gps_title">GPS</string>
<string name="settings_announcements_title">Anúncios de voz</string>
<string name="settings_announcements_statistics_title">Anúncios de voz</string>
<string name="settings_announcements_summary">Tempo, distância, velocidade da voz</string>
<string name="settings_import_export_title">Importar/Exportar</string>
<string name="settings_import_export_summary">Importar, Exportar, Auto Export</string>
<string name="settings_units_category">Unidades</string>
<string name="settings_recording_idle_speed_title">Limite de velocidade de marcha lenta</string>
<string name="settings_night_mode_title">Tema UI</string>
<string name="settings_layout_reset_confirm_message">Todas as configurações de layout serão revertidas para os valores padrão. Isso não excluirá nenhuma trilha do aparelho.</string>
<string name="settings_all_reset">Redefinir</string>
-1
View File
@@ -457,7 +457,6 @@ limitations under the License.
<string name="custom_layout_list_edit_already_exists">Numele Layout-ului există deja</string>
<string name="custom_layout_list_title">Machete</string>
<string name="custom_layout_list_edit_name">Tastați numele layout pentru a adăuga</string>
<string name="settings_recording_idle_speed_title">Pragul de viteză la ralanti</string>
<string name="generic_filter">Filtru</string>
<string name="menu_select_layout">Selectați Layout</string>
<string name="settings_import_export_title">Importație/Export</string>
+1 -2
View File
@@ -460,7 +460,7 @@ limitations under the License.
<string name="settings_gps_summary">Интервал времени и дистанции, Максимальная дистанция, Точность</string>
<string name="settings_sensors_title">Bluetooth-датчики</string>
<string name="settings_sensors_summary">Датчики велосипеда, Оборот колеса, Датчики бега</string>
<string name="settings_announcements_title">Голосовые подсказки</string>
<string name="settings_announcements_statistics_title">Голосовые подсказки</string>
<string name="settings_import_export_title">Импорт/Экспорт</string>
<string name="settings_import_export_summary">Импорт, Экспорт, Автоэкспорт</string>
<string name="settings_units_category">Единицы</string>
@@ -490,7 +490,6 @@ limitations under the License.
<string name="stats_clock">Часы</string>
<string name="settings_activity_defaults_category">Настройки активности</string>
<string name="settings_reset_summary">Сбросить настройки</string>
<string name="settings_recording_idle_speed_title">Порог скорости холостого хода</string>
<string name="settings_generic_sensor">Обычные</string>
<string name="custom_layout_select_layout">Выбрать раскладку</string>
<string name="stats_custom_layout_default_layout">Раскладка по умолчанию</string>
+1 -2
View File
@@ -427,7 +427,6 @@ limitations under the License.
<string name="settings_recording_show_on_lockscreen_while_recording_summary">Počas nahrávania zobrazte štatistiky bez odomknutia zariadenia.</string>
<string name="settings_recording_show_on_lockscreen_while_recording_title">Zobrazenie štatistík na uzamknutej obrazovke</string>
<string name="settings_generic_sensor">Všeobecné</string>
<string name="settings_recording_idle_speed_title">Prahová hodnota voľnobehu</string>
<string name="value_float_mile_hour">%1$.1f mi/h</string>
<string name="settings_default_export_uri_title">Adresár pre export stôp</string>
<string name="instant_export_enabled_title">Okamžitý export po tréningu</string>
@@ -484,7 +483,7 @@ limitations under the License.
<string name="settings_defaults_summary">Predvolené jednotky, predvolená aktivita, predvolený názov skladby</string>
<string name="settings_ui_title">Používateľské rozhranie</string>
<string name="settings_ui_summary">Vzhľad, Rozloženie nahrávania, Téma</string>
<string name="settings_announcements_title">Hlasové oznámenia</string>
<string name="settings_announcements_statistics_title">Hlasové oznámenia</string>
<string name="settings_gps_title">GPS</string>
<string name="settings_sensors_title">Senzory Bluetooth</string>
<string name="settings_sensors_summary">Cyklistické snímače, obvod kolies, snímače behu</string>
+1 -2
View File
@@ -401,7 +401,7 @@ limitations under the License.
<string name="export_with_photos">s fotografijami</string>
<string name="export_track_errors">Skladbe niso izvožene</string>
<string name="settings_layout_reset">Ponastavitev postavitve po meri</string>
<string name="settings_announcements_title">Glasovna obvestila</string>
<string name="settings_announcements_statistics_title">Glasovna obvestila</string>
<string name="settings_announcements_summary">Čas, razdalja, hitrost glasu</string>
<string name="settings_import_export_title">Uvoz/izvoz</string>
<string name="settings_activity_defaults_category">Privzete vrednosti dejavnosti</string>
@@ -448,7 +448,6 @@ limitations under the License.
<string name="settings_all_reset">Ponastavitev</string>
<string name="settings_sensor_running_speed_and_cadence">Razdalja, hitrost in kadenca</string>
<string name="settings_sensor_wheel_circumference">Obod kolesa (mm)</string>
<string name="settings_recording_idle_speed_title">Prag prostega teka</string>
<string name="settings_night_mode_option_system">Sistem</string>
<string name="settings_public_api_disabled_toast">OpenTracks Public API je onemogočen: omogočite ga lahko v nastavitvah.</string>
<string name="current_heart_rate">Trenutni srčni utrip</string>
+1 -2
View File
@@ -365,7 +365,7 @@ limitations under the License.
<string name="settings_gps_title">GPS</string>
<string name="settings_defaults_summary">Tercih Edilen Birimler, Varsayılan Etkinlik, Varsayılan Parça Adı</string>
<string name="settings_gps_summary">Zaman aralığı, Mesafe aralığı, Maks. Mesafe, Hassasiyet</string>
<string name="settings_announcements_title">Sesli Anonslar</string>
<string name="settings_announcements_statistics_title">Sesli Anonslar</string>
<string name="settings_announcements_summary">Zaman, Mesafe, Ses Hızı</string>
<string name="help_data_storage_content"> Sensör verileri, kaydedilen konum başına saklanır. Kalp atış hızı sensörü açısından bu, konum başına yalnızca <i>bir</i> kalp atış hızı ölçümünün depolandığı anlamına gelir. Hareketsiz duruyorsanız veya içerideyseniz, hiçbir sensör verisi kaydedilmeyecektir (ancak gösterilecektir). </string>
<string name="help_support_content"> Yapabileceklerin:
@@ -453,7 +453,6 @@ limitations under the License.
<string name="settings_recording_show_on_lockscreen_while_recording_title">Kilit ekranında istatistikleri göster</string>
<string name="settings_recording_keep_screen_on_while_recording_summary">Kayıt esnasında ekranı açık tutar.</string>
<string name="settings_recording_fullscreen_on_while_recording_summary">Kayıt esnasında tam ekranda sunar.</string>
<string name="settings_recording_idle_speed_title">Boşta hız eşiği</string>
<string name="settings_night_mode_title">Kullanıcı arayüzü teması</string>
<string name="settings_night_mode_option_system">Sistem</string>
<string name="settings_night_mode_option_night">Gece</string>
+1 -2
View File
@@ -475,7 +475,6 @@ limitations under the License.
<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_recording_idle_speed_title">Поріг холостого ходу</string>
<string name="settings_running_sensor">Рухомий</string>
<string name="settings_generic_sensor">Загальний</string>
<string name="settings_layout_reset_confirm_title">Скинути ваші макети\?</string>
@@ -489,7 +488,7 @@ limitations under the License.
<string name="settings_ui_title">Інтерфейс користувача</string>
<string name="settings_ui_summary">Зовнішній вигляд, Макет запису, Тема</string>
<string name="settings_sensors_title">Датчики Bluetooth</string>
<string name="settings_announcements_title">Голосові оголошення</string>
<string name="settings_announcements_statistics_title">Голосові оголошення</string>
<string name="settings_gps_summary">Інтервал часу, інтервал відстані, Максимальна відстань, точність</string>
<string name="settings_sensors_summary">Датчики Їзди На Велосипеді, Окружність Колеса, Датчики Ходу</string>
<string name="settings_cycling_sensor">Їзда на велосипеді</string>
+1 -2
View File
@@ -469,7 +469,6 @@ limitations under the License.
<string name="settings_import_section">Nhập</string>
<string name="settings_export_section">Xuất</string>
<string name="settings_generic_sensor">Chung chung</string>
<string name="settings_recording_idle_speed_title">Tốc độ ngưỡng</string>
<string name="value_float_mile_hour">%1$.1f mi/h</string>
<string name="value_float_mile_hour_recommended">%1$.1f mi/h (khuyến khích)</string>
<string name="value_float_kilometer_hour">%1$.1f km/h</string>
@@ -488,7 +487,7 @@ limitations under the License.
<string name="settings_gps_title">GPS</string>
<string name="settings_sensors_title">Cảm Biến Bluetooth</string>
<string name="settings_sensors_summary">Đi Xe Đạp Cảm Biến, Bánh Vòng, Chạy Bộ Cảm Biến</string>
<string name="settings_announcements_title">Giọng Nói Thông Báo</string>
<string name="settings_announcements_statistics_title">Giọng Nói Thông Báo</string>
<string name="settings_announcements_summary">Thời Gian, Khoảng Cách, Tốc Độ Giọng Nói</string>
<string name="settings_import_export_title">Xuất Nhập Khẩu</string>
<string name="generic_undo">Hoàn tác</string>
+1 -2
View File
@@ -332,7 +332,7 @@ limitations under the License.
<string name="settings_gps_summary">時間間隔、距離間隔、最大距離、精度</string>
<string name="settings_sensors_title">藍芽感測器</string>
<string name="settings_sensors_summary">自行車感測器、輪周、跑步感測器</string>
<string name="settings_announcements_title">語音播報</string>
<string name="settings_announcements_statistics_title">語音播報</string>
<string name="settings_announcements_summary">時間、距離、語速</string>
<string name="settings_import_export_title">匯入/匯出</string>
<string name="settings_import_export_summary">匯入、匯出、自動匯出</string>
@@ -366,7 +366,6 @@ limitations under the License.
<string name="settings_recording_track_name_date_local_option">日期 (當地)</string>
<string name="settings_recording_track_name_number_option">數字</string>
<string name="settings_recording_track_name_title">預設蹤跡名稱</string>
<string name="settings_recording_idle_speed_title">怠速閾值</string>
<string name="settings_night_mode_title">界面主題</string>
<string name="settings_night_mode_option_system">系統</string>
<string name="settings_night_mode_option_day">日</string>
+1 -2
View File
@@ -430,7 +430,6 @@ limitations under the License.
\n 方法二:通过一个正常的地图应用(例如 <a href="http://osmand.net/">OsmAnd</a> 和 <a href="https://maps.me/">MAPS.ME</a>)。这些应用需要支持 KMZ 文件格式(只包含位置和时间戳)。事实上,应用需要支持 <i>application/vnd.google-earth.kmz</i> MIME 类型的 <i>ACTION_VIEW</i> 操作。 </string>
<string name="export_progress_summary_errors_msg">错误</string>
<string name="permission_gps_failed">OpenTracks 需要使用 GPS 的权限。</string>
<string name="settings_recording_idle_speed_title">静止速度阈值</string>
<string name="custom_layout_list_title">布局</string>
<string name="image_discard">丢弃</string>
<string name="import_thread_interrupted">线程被中断</string>
@@ -559,7 +558,7 @@ limitations under the License.
<string name="settings_defaults_title">默认的单位和活动类型</string>
<string name="settings_sensors_title">蓝牙传感器</string>
<string name="settings_sensors_summary">自行车传感器,车轮周长,跑步传感器</string>
<string name="settings_announcements_title">语音播报</string>
<string name="settings_announcements_statistics_title">语音播报</string>
<string name="settings_reset_summary">将设置重置为默认值</string>
<string name="settings_layout_reset">重置自定义布局</string>
<string name="description_pace_nautical">配速(分钟/海里)</string>
+19 -12
View File
@@ -123,19 +123,16 @@
<item>@string/recording_gps_accuracy_poor</item>
</string-array>
<string name="idle_speed_key" translatable="false">idleSpeed</string>
<string name="idle_speed_default" translatable="false">0.5</string>
<string-array name="idle_speed_values">
<item>0.3</item>
<item>@string/idle_speed_default</item>
<item>1</item>
<item>1.5</item>
<item>2</item>
<item>2.5</item>
<item>3</item>
<item>3.5</item>
<item>4</item>
<string name="idle_duration_key" translatable="false">idleSpeedDuration</string>
<string name="idle_duration_default" translatable="false">10</string>
<string-array name="idle_duration_values">
<item>5</item>
<item>@string/idle_duration_default</item>
<item>15</item>
<item>30</item>
<item>45</item>
<item>60</item>
<item>120</item>
</string-array>
<string name="stats_rate_key" translatable="false">statsRate</string>
@@ -240,12 +237,22 @@
</string-array>
<string name="voice_on_device_speaker_key" translatable="false">voiceOnDeviceSpeaker</string>
<bool name="voice_on_device_speaker_default" translatable="false">true</bool>
<string name="voice_announce_idle_key" translatable="false">voice_announce_idle_key</string>
<bool name="voice_announce_idle_default" translatable="false">false</bool>
<string name="voice_announce_total_distance_key" translatable="false">voiceAnnounceTotalDistance</string>
<bool name="voice_announce_total_distance_default" translatable="false">true</bool>
<string name="voice_announce_moving_time_key" translatable="false">voiceAnnounceMovingTime</string>
<bool name="voice_announce_moving_time_default" translatable="false">true</bool>
<string name="voice_announce_average_speed_pace_key" translatable="false">voiceAnnounceAverageSpeedPace</string>
<bool name="voice_announce_average_speed_pace_default" translatable="false">true</bool>
<string name="voice_announce_lap_speed_pace_key" translatable="false">voiceAnnounceLapSpeedPace</string>
<bool name="voice_announce_lap_speed_pace_default" translatable="false">true</bool>
<string name="voice_announce_average_heart_rate_key" translatable="false">voiceAnnounceAverageHeartRate</string>
<bool name="voice_announce_average_heart_rate_default" translatable="false">false</bool>
<string name="voice_announce_lap_heart_rate_key" translatable="false">voiceAnnounceLapHeartRate</string>
<bool name="voice_announce_lap_heart_rate_default" translatable="false">false</bool>
<string name="export_trackfileformat_key" translatable="false">exportTrackFileFormat</string>
<!-- See TrackFileFormat -->
+8 -2
View File
@@ -356,7 +356,11 @@ limitations under the License.
<string name="settings_sensors_title">Bluetooth Sensors</string>
<string name="settings_sensors_summary">Cycling Sensors, Wheel Circumference, Running Sensors</string>
<string name="settings_announcements_title">Voice Announcements</string>
<string name="settings_announcements_idle_category_title">Idle Announcements</string>
<string name="settings_announcements_idle_title">Idle</string>
<string name="settings_announcements_statistics_title">Statistics Announcements</string>
<string name="settings_announcements_summary">Time, Distance, Voice Speed</string>
<string name="settings_import_export_title">Import/Export</string>
@@ -395,7 +399,7 @@ limitations under the License.
<string name="settings_recording_track_name_date_local_option">Date (local)</string>
<string name="settings_recording_track_name_number_option">Number</string>
<string name="settings_recording_track_name_title">Default track name</string>
<string name="settings_recording_idle_speed_title">Idle speed threshold</string>
<string name="settings_recording_idle_timeout_title">Idle threshold</string>
<string name="settings_night_mode_title">UI Theme</string>
<string name="settings_night_mode_option_system">System</string>
<string name="settings_night_mode_option_day">Day</string>
@@ -557,6 +561,8 @@ limitations under the License.
<string name="value_none">None</string>
<string name="value_off">Off</string>
<string name="value_smallest_recommended">Smallest (recommended)</string>
<string name="value_int_seconds">%1$d s (recommended)</string>
<string name="voiceIdle">Becoming idle.</string>
<string name="voiceHoursPlural">
{n, plural,
=1 {1 hour}
+1 -1
View File
@@ -31,7 +31,7 @@
android:icon="@drawable/ic_baseline_volume_up_24"
android:key="@string/settings_announcements_key"
android:summary="@string/settings_announcements_summary"
android:title="@string/settings_announcements_title" />
android:title="@string/settings_announcements_statistics_title" />
<Preference
android:icon="@drawable/ic_baseline_import_export_24"
+34 -27
View File
@@ -2,21 +2,12 @@
<PreferenceScreen xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:title="@string/settings_announcements_title">
android:title="@string/settings_announcements_statistics_title">
<ListPreference
android:defaultValue="@string/voice_announcement_frequency_default"
android:entryValues="@array/voice_announcement_frequency_values"
android:key="@string/voice_announcement_frequency_key"
android:title="@string/settings_announcements_totaltime_frequency"
app:useSimpleSummaryProvider="true" />
<ListPreference
android:defaultValue="@string/voice_announcement_distance_default"
android:entryValues="@array/voice_announcement_distance_values"
android:key="@string/voice_announcement_distance_key"
android:title="@string/settings_announcements_distance_frequency"
app:useSimpleSummaryProvider="true" />
<CheckBoxPreference
android:defaultValue="@bool/voice_on_device_speaker_default"
android:key="@string/voice_on_device_speaker_key"
android:title="@string/voice_on_device_speaker_title" />
<ListPreference
android:defaultValue="@string/voice_speed_rate_default"
@@ -26,39 +17,55 @@
android:title="@string/menu_voice_rate"
app:useSimpleSummaryProvider="true" />
<CheckBoxPreference
android:defaultValue="@bool/voice_on_device_speaker_default"
android:key="@string/voice_on_device_speaker_key"
android:title="@string/voice_on_device_speaker_title" />
<PreferenceCategory android:title="@string/settings_announcements_title">
<PreferenceCategory android:title="@string/settings_announcements_idle_category_title">
<SwitchPreference
android:defaultValue="true"
android:defaultValue="@bool/voice_announce_total_distance_default"
android:key="@string/voice_announce_total_distance_key"
android:title="@string/total_distance" />
android:title="@string/settings_announcements_idle_title" />
</PreferenceCategory>
<PreferenceCategory android:title="@string/settings_announcements_statistics_title">
<ListPreference
android:defaultValue="@string/voice_announcement_frequency_default"
android:entryValues="@array/voice_announcement_frequency_values"
android:key="@string/voice_announcement_frequency_key"
android:title="@string/settings_announcements_totaltime_frequency"
app:useSimpleSummaryProvider="true" />
<ListPreference
android:defaultValue="@string/voice_announcement_distance_default"
android:entryValues="@array/voice_announcement_distance_values"
android:key="@string/voice_announcement_distance_key"
android:title="@string/settings_announcements_distance_frequency"
app:useSimpleSummaryProvider="true" />
<SwitchPreference
android:defaultValue="true"
android:defaultValue="@bool/voice_announce_idle_default"
android:key="@string/voice_announce_idle_key"
android:title="@string/settings_announcements_idle_title" />
<SwitchPreference
android:defaultValue="@bool/voice_announce_moving_time_default"
android:key="@string/voice_announce_moving_time_key"
android:title="@string/stats_moving_time" />
<SwitchPreference
android:defaultValue="true"
android:defaultValue="@bool/voice_announce_average_speed_pace_default"
android:key="@string/voice_announce_average_speed_pace_key"
android:title="@string/settings_announcements_average_speed_pace" />
<SwitchPreference
android:defaultValue="true"
android:defaultValue="@bool/voice_announce_lap_speed_pace_default"
android:key="@string/voice_announce_lap_speed_pace_key"
android:title="@string/settings_announcements_lap_speed_pace" />
<SwitchPreference
android:defaultValue="false"
android:defaultValue="@bool/voice_announce_average_heart_rate_default"
android:key="@string/voice_announce_average_heart_rate_key"
android:title="@string/settings_announcements_average_heart_rate" />
<SwitchPreference
android:defaultValue="false"
android:defaultValue="@bool/voice_announce_lap_heart_rate_default"
android:key="@string/voice_announce_lap_heart_rate_key"
android:title="@string/settings_announcements_lap_heart_rate" />
</PreferenceCategory>
+4 -4
View File
@@ -29,10 +29,10 @@
android:title="@string/settings_recording_min_required_accuracy_title" />
<ListPreference
android:defaultValue="@string/idle_speed_default"
android:entryValues="@array/idle_speed_values"
android:key="@string/idle_speed_key"
android:title="@string/settings_recording_idle_speed_title"
android:defaultValue="@string/idle_duration_default"
android:entryValues="@array/idle_duration_values"
android:key="@string/idle_duration_key"
android:title="@string/settings_recording_idle_timeout_title"
app:useSimpleSummaryProvider="true" />
</PreferenceScreen>