forked from upstream-mirrors/OpenTracks
Compare commits
51 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 0b30ecabb1 | |||
| 107cc14a2d | |||
| 6d3d8bb83c | |||
| 9c8e0335ef | |||
| 8c648c69e8 | |||
| 4422464f8d | |||
| d094531779 | |||
| a01341d235 | |||
| 83065dad39 | |||
| 68ba3e0c2a | |||
| 3f21edc44c | |||
| aefd1a6a35 | |||
| 74fa73e4a4 | |||
| 0a006d64f9 | |||
| 997ab6060e | |||
| 6d926ca7a8 | |||
| 3c1f1a9ba5 | |||
| ed5da55fba | |||
| d1e9649505 | |||
| 2431d60171 | |||
| e08f5e7273 | |||
| 7d2a6fff59 | |||
| a5158a55d9 | |||
| 15fbd90ca4 | |||
| eebc33f60c | |||
| ba2a755fb7 | |||
| 344c50a20c | |||
| 9a9a610321 | |||
| 8af427ea48 | |||
| 7b7d7c8061 | |||
| 7d6919d3f9 | |||
| 7eceb979c3 | |||
| bd48944ad5 | |||
| 503cba1315 | |||
| a313cb6244 | |||
| 7f0c6309c0 | |||
| 02c578327d | |||
| 7fec0dcff4 | |||
| 34eb7a833a | |||
| 5db3aa7ac1 | |||
| 47fd7c51a9 | |||
| 2f6f0b4e17 | |||
| 41b794f66b | |||
| e2c592367a | |||
| 7a24c56474 | |||
| 3f6bd6926a | |||
| 508af45449 | |||
| 0f89cd7875 | |||
| 29b522b8a2 | |||
| 14e63a1a54 | |||
| e559583d23 |
@@ -19,6 +19,12 @@ However, often only one value is provided.
|
||||
### Speed
|
||||
|
||||
* Garmin Speed Sensor 2
|
||||
* Has updatable firmware that requires an account for garmin.com
|
||||
* This sensor reports speed data as cadence.
|
||||
A workaround is in place.
|
||||
* This sensor sometimes counts backwards (unknown software version).
|
||||
A workaround is in place.
|
||||
|
||||
* Wahoo Speed (Model: WFRPMSPD)
|
||||
|
||||
### Cadence
|
||||
|
||||
+4
-4
@@ -6,7 +6,7 @@ buildscript {
|
||||
jcenter()
|
||||
}
|
||||
dependencies {
|
||||
classpath 'com.android.tools.build:gradle:4.1.2'
|
||||
classpath 'com.android.tools.build:gradle:4.1.3'
|
||||
}
|
||||
}
|
||||
|
||||
@@ -51,8 +51,8 @@ android {
|
||||
|
||||
defaultConfig {
|
||||
applicationId "de.dennisguse.opentracks"
|
||||
versionCode 3890
|
||||
versionName "v3.15.3"
|
||||
versionCode 3941
|
||||
versionName "v3.16.0"
|
||||
|
||||
buildConfigField "String", "VERSION_NAME_FULL", "\"${getVersionName()}\""
|
||||
|
||||
@@ -86,7 +86,7 @@ android {
|
||||
}
|
||||
|
||||
dependencies {
|
||||
androidTestImplementation 'androidx.test.espresso:espresso-core:3.4.0-alpha04'
|
||||
androidTestImplementation 'androidx.test.espresso:espresso-core:3.4.0-alpha05'
|
||||
coreLibraryDesugaring 'com.android.tools:desugar_jdk_libs:1.1.5'
|
||||
|
||||
implementation 'androidx.appcompat:appcompat:1.2.0'
|
||||
|
||||
@@ -2,17 +2,7 @@
|
||||
interface TrackExporter {
|
||||
}
|
||||
class KmzTrackExporter implements TrackExporter
|
||||
KmzTrackExporter o-- FileTrackExporter
|
||||
class FileTrackExporter implements TrackExporter {
|
||||
/' One track only '/
|
||||
}
|
||||
FileTrackExporter o-- TrackWriter
|
||||
|
||||
interface TrackWriter {
|
||||
}
|
||||
class GpxTrackWriter implements TrackWriter
|
||||
class KmlTrackWriter implements TrackWriter
|
||||
|
||||
class KmlTrackExporter implements TrackExporter
|
||||
|
||||
interface TrackImporter {
|
||||
}
|
||||
|
||||
@@ -1,8 +1,32 @@
|
||||
<?xml version="1.0"?>
|
||||
<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema"
|
||||
<xsd:schema xmlns:xsd="http://www.w3.org/2001/XMLSchema"
|
||||
targetNamespace="http://opentracksapp.com/xmlschemas/v1">
|
||||
|
||||
<xs:element name="trackuuid"
|
||||
type="xs:string" />
|
||||
<xsd:element name="trackuuid"
|
||||
type="xsd:string">
|
||||
<xsd:annotation>
|
||||
<xsd:documentation xml:lang="en">
|
||||
A unique track identifier stored as UUID.
|
||||
</xsd:documentation>
|
||||
</xsd:annotation>
|
||||
</xsd:element>
|
||||
|
||||
</xs:schema>
|
||||
<xsd:element name="loss"
|
||||
type="xsd:float">
|
||||
<xsd:annotation>
|
||||
<xsd:documentation xml:lang="en">
|
||||
The lost elevation in meters from the previous to the current TrackPoint.
|
||||
Only used in GPX.
|
||||
</xsd:documentation>
|
||||
</xsd:annotation>
|
||||
</xsd:element>
|
||||
<xsd:element name="gain"
|
||||
type="xsd:string">
|
||||
<xsd:annotation>
|
||||
<xsd:documentation xml:lang="en">
|
||||
The gained elevation in meters from the previous to the current TrackPoint.
|
||||
Only used in GPX.
|
||||
</xsd:documentation>
|
||||
</xsd:annotation>
|
||||
</xsd:element>
|
||||
</xsd:schema>
|
||||
@@ -0,0 +1,4 @@
|
||||
v3.15.4: OpenTracks
|
||||
|
||||
Bugfix:
|
||||
- Voice annoucements interval should be in minutes (introduced in v3.15.0)
|
||||
@@ -0,0 +1,13 @@
|
||||
v3.16.0: OpenTracks
|
||||
|
||||
Changes:
|
||||
- Cycling: get distance from BLE speed sensor
|
||||
- Reworked UI presented while recording
|
||||
- UI performance optimizations
|
||||
- Exported filenames: <First 32bits UUID>_<track's name>.<extension>
|
||||
- Import multiple files via Intent (e.g., from Gadgetbridge)
|
||||
- GPX: export power data
|
||||
- Updated translations
|
||||
|
||||
Bugfix:
|
||||
- Voice announcement use minutes correctly
|
||||
@@ -11,6 +11,7 @@ import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import de.dennisguse.opentracks.content.provider.ContentProviderUtils;
|
||||
import de.dennisguse.opentracks.content.provider.TrackPointIterator;
|
||||
import de.dennisguse.opentracks.util.FileUtils;
|
||||
|
||||
public class TestDataUtil {
|
||||
@@ -150,4 +151,13 @@ public class TestDataUtil {
|
||||
|
||||
return new Marker("Marker name", "Marker description", "Marker category", "", trackId, 0.0, 0, trackPoint, photoUrl);
|
||||
}
|
||||
|
||||
public static List<TrackPoint> getTrackPoints(ContentProviderUtils contentProviderUtils, Track.Id trackId) {
|
||||
TrackPointIterator trackPointIterator = contentProviderUtils.getTrackPointLocationIterator(trackId, null);
|
||||
ArrayList<TrackPoint> trackPoints = new ArrayList<>();
|
||||
while (trackPointIterator.hasNext()) {
|
||||
trackPoints.add(trackPointIterator.next());
|
||||
}
|
||||
return trackPoints;
|
||||
}
|
||||
}
|
||||
|
||||
+10
-10
@@ -7,8 +7,8 @@ import java.util.List;
|
||||
|
||||
public class TestSensorDataUtil {
|
||||
|
||||
private List<TrackPoint> trackPointList = new ArrayList<>();
|
||||
private List<SensorData> sensorDataList = new ArrayList<>();
|
||||
private final List<TrackPoint> trackPointList = new ArrayList<>();
|
||||
private final List<SensorData> sensorDataList = new ArrayList<>();
|
||||
|
||||
public void add(Instant time, Float hr, Float cadence, Float power, TrackPoint.Type type) {
|
||||
sensorDataList.add(new TestSensorDataUtil.SensorData(time, hr, cadence, power, type));
|
||||
@@ -33,7 +33,7 @@ public class TestSensorDataUtil {
|
||||
}
|
||||
|
||||
public SensorDataStats computeStats() {
|
||||
if (sensorDataList == null || sensorDataList.size() <= 1) {
|
||||
if (sensorDataList.size() <= 1) {
|
||||
return null;
|
||||
}
|
||||
|
||||
@@ -49,9 +49,9 @@ public class TestSensorDataUtil {
|
||||
if (dataPrev.type != TrackPoint.Type.SEGMENT_START_MANUAL) {
|
||||
timeElapsed = dataCurrent.type != TrackPoint.Type.SEGMENT_START_MANUAL ? dataCurrent.time.getEpochSecond() - dataPrev.time.getEpochSecond() : 0;
|
||||
stats.avgHr += (dataPrev.hr * timeElapsed);
|
||||
stats.maxHr = dataPrev.hr > stats.maxHr ? dataPrev.hr : stats.maxHr;
|
||||
stats.maxHr = Math.max(dataPrev.hr, stats.maxHr);
|
||||
stats.avgCadence += (dataPrev.cadence * timeElapsed);
|
||||
stats.maxCadence = dataPrev.cadence > stats.maxCadence ? dataPrev.cadence : stats.maxCadence;
|
||||
stats.maxCadence = Math.max(dataPrev.cadence, stats.maxCadence);
|
||||
stats.avgPower += (dataPrev.power * timeElapsed);
|
||||
|
||||
movingTime += timeElapsed;
|
||||
@@ -67,11 +67,11 @@ public class TestSensorDataUtil {
|
||||
}
|
||||
|
||||
private static class SensorData {
|
||||
Instant time;
|
||||
float hr;
|
||||
float cadence;
|
||||
float power;
|
||||
TrackPoint.Type type;
|
||||
final Instant time;
|
||||
final float hr;
|
||||
final float cadence;
|
||||
final float power;
|
||||
final TrackPoint.Type type;
|
||||
|
||||
public SensorData(Instant time, Float hr, Float cadence, Float power, TrackPoint.Type type) {
|
||||
this.time = time;
|
||||
|
||||
+17
-24
@@ -94,29 +94,22 @@ public class CustomContentProviderUtilsTest {
|
||||
|
||||
@Test
|
||||
public void testLocationIterator_noPoints() {
|
||||
testIterator(new Track.Id(1), 0, 1);
|
||||
testIterator(new Track.Id(1), 0);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testLocationIterator_noBatchAscending() {
|
||||
testIterator(new Track.Id(1), 50, 100);
|
||||
testIterator(new Track.Id(2), 50, 50);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testLocationIterator_batchAscending() {
|
||||
testIterator(new Track.Id(1), 50, 11);
|
||||
testIterator(new Track.Id(2), 50, 25);
|
||||
public void testLocationIterator_noAscending() {
|
||||
testIterator(new Track.Id(1), 50);
|
||||
testIterator(new Track.Id(2), 50);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testLocationIterator_largeTrack() {
|
||||
testIterator(new Track.Id(1), 20000, 2000);
|
||||
testIterator(new Track.Id(1), 20000);
|
||||
}
|
||||
|
||||
private void testIterator(Track.Id trackId, int numPoints, int batchSize) {
|
||||
long lastPointId = initializeTrack(trackId, numPoints);
|
||||
contentProviderUtils.setDefaultCursorBatchSize(batchSize);
|
||||
private void testIterator(Track.Id trackId, int numPoints) {
|
||||
TrackPoint.Id lastPointId = initializeTrack(trackId, numPoints);
|
||||
List<TrackPoint> locations = new ArrayList<>(numPoints);
|
||||
try (TrackPointIterator it = contentProviderUtils.getTrackPointLocationIterator(trackId, null)) {
|
||||
while (it.hasNext()) {
|
||||
@@ -124,13 +117,13 @@ public class CustomContentProviderUtilsTest {
|
||||
assertNotNull(trackPoint);
|
||||
locations.add(trackPoint);
|
||||
// Make sure the IDs are returned in the right order.
|
||||
assertEquals(lastPointId - numPoints + locations.size(), trackPoint.getId().getId());
|
||||
assertEquals(lastPointId.getId() - numPoints + locations.size(), trackPoint.getId().getId());
|
||||
}
|
||||
assertEquals(numPoints, locations.size());
|
||||
}
|
||||
}
|
||||
|
||||
private long initializeTrack(Track.Id id, int numPoints) {
|
||||
private TrackPoint.Id initializeTrack(Track.Id id, int numPoints) {
|
||||
Track track = new Track();
|
||||
track.setId(id);
|
||||
track.setName("Test: " + id.getId());
|
||||
@@ -150,17 +143,17 @@ public class CustomContentProviderUtilsTest {
|
||||
contentProviderUtils.bulkInsertTrackPoint(trackPoints, id);
|
||||
|
||||
// Load all inserted trackPoints.
|
||||
long lastPointId = -1;
|
||||
TrackPoint.Id lastPointId = null;
|
||||
int counter = 0;
|
||||
try (TrackPointIterator it = contentProviderUtils.getTrackPointLocationIterator(id, null)) {
|
||||
while (it.hasNext()) {
|
||||
TrackPoint trackPoint = it.next();
|
||||
lastPointId = trackPoint.getId().getId();
|
||||
lastPointId = trackPoint.getId();
|
||||
counter++;
|
||||
}
|
||||
}
|
||||
|
||||
assertTrue(numPoints == 0 || lastPointId > 0);
|
||||
assertTrue(numPoints == 0 || lastPointId.getId() > 0);
|
||||
assertEquals(numPoints, counter);
|
||||
|
||||
return lastPointId;
|
||||
@@ -770,9 +763,9 @@ public class CustomContentProviderUtilsTest {
|
||||
|
||||
// when / then
|
||||
contentProviderUtils.bulkInsertTrackPoint(track.second, trackId);
|
||||
assertEquals(20, contentProviderUtils.getTrackPointCursor(trackId, null, 1000).getCount());
|
||||
assertEquals(20, contentProviderUtils.getTrackPointCursor(trackId, null).getCount());
|
||||
contentProviderUtils.bulkInsertTrackPoint(track.second.subList(0, 8), trackId);
|
||||
assertEquals(28, contentProviderUtils.getTrackPointCursor(trackId, null, 1000).getCount());
|
||||
assertEquals(28, contentProviderUtils.getTrackPointCursor(trackId, null).getCount());
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -829,7 +822,7 @@ public class CustomContentProviderUtilsTest {
|
||||
Track track = TestDataUtil.createTrackAndInsert(contentProviderUtils, trackId, 10);
|
||||
|
||||
contentProviderUtils.insertTrackPoint(TestDataUtil.createTrackPoint(22), trackId);
|
||||
assertEquals(11, contentProviderUtils.getTrackPoints(trackId).size());
|
||||
assertEquals(11, contentProviderUtils.getTrackPointCursor(trackId, null).getCount());
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -847,7 +840,7 @@ public class CustomContentProviderUtilsTest {
|
||||
contentProviderUtils.insertTrackPoint(trackPoint, trackId);
|
||||
|
||||
// then
|
||||
List<TrackPoint> trackPoints = contentProviderUtils.getTrackPoints(trackId);
|
||||
List<TrackPoint> trackPoints = TestDataUtil.getTrackPoints(contentProviderUtils, trackId);
|
||||
assertTrue(trackPoints.get(10).hasHeartRate());
|
||||
assertEquals(trackPoint.getHeartRate_bpm(), trackPoints.get(10).getHeartRate_bpm(), 0.01);
|
||||
assertEquals(trackPoint.getCyclingCadence_rpm(), trackPoints.get(10).getCyclingCadence_rpm(), 0.01);
|
||||
@@ -879,7 +872,7 @@ public class CustomContentProviderUtilsTest {
|
||||
.map(TrackPoint.Id::new).collect(Collectors.toList());
|
||||
|
||||
// when
|
||||
Cursor cursor = contentProviderUtils.getTrackPointCursor(trackId, trackpointIds.get(8), 5);
|
||||
Cursor cursor = contentProviderUtils.getTrackPointCursor(trackId, trackpointIds.get(8));
|
||||
|
||||
// then
|
||||
assertEquals(2, cursor.getCount());
|
||||
|
||||
+10
-8
@@ -96,34 +96,36 @@ public class SensorDataCyclingTest {
|
||||
@Test
|
||||
public void compute_speed() {
|
||||
// given
|
||||
SensorDataCycling.Speed previous = new SensorDataCycling.Speed("sensorAddress", "sensorName", 1, 6184);
|
||||
SensorDataCycling.Speed current = new SensorDataCycling.Speed("sensorAddress", "sensorName", 2, 8016);
|
||||
SensorDataCycling.DistanceSpeed previous = new SensorDataCycling.DistanceSpeed("sensorAddress", "sensorName", 1, 6184);
|
||||
SensorDataCycling.DistanceSpeed current = new SensorDataCycling.DistanceSpeed("sensorAddress", "sensorName", 2, 8016);
|
||||
|
||||
// when
|
||||
current.compute(previous, 2150);
|
||||
|
||||
// then
|
||||
assertEquals(1.20, current.getValue(), 0.01);
|
||||
assertEquals(1.20, current.getValue().distance_m, 2150);
|
||||
assertEquals(1.20, current.getValue().speed_mps, 0.01);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void compute_speed_rollOverCount() {
|
||||
// given
|
||||
SensorDataCycling.Speed previous = new SensorDataCycling.Speed("sensorAddress", "sensorName", UintUtils.UINT16_MAX - 1, 1024);
|
||||
SensorDataCycling.Speed current = new SensorDataCycling.Speed("sensorAddress", "sensorName", 0, 2048);
|
||||
SensorDataCycling.DistanceSpeed previous = new SensorDataCycling.DistanceSpeed("sensorAddress", "sensorName", UintUtils.UINT16_MAX - 1, 1024);
|
||||
SensorDataCycling.DistanceSpeed current = new SensorDataCycling.DistanceSpeed("sensorAddress", "sensorName", 0, 2048);
|
||||
|
||||
// when
|
||||
current.compute(previous, 2000);
|
||||
|
||||
// then
|
||||
assertEquals(2, current.getValue(), 0.01);
|
||||
assertEquals(1.20, current.getValue().distance_m, 2000);
|
||||
assertEquals(2, current.getValue().speed_mps, 0.01);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void equals_speed_with_no_data() {
|
||||
// given
|
||||
SensorDataCycling.Speed previous = new SensorDataCycling.Speed("sensorAddress");
|
||||
SensorDataCycling.Speed current = new SensorDataCycling.Speed("sensorAddress", "sensorName", 0, 2048);
|
||||
SensorDataCycling.DistanceSpeed previous = new SensorDataCycling.DistanceSpeed("sensorAddress");
|
||||
SensorDataCycling.DistanceSpeed current = new SensorDataCycling.DistanceSpeed("sensorAddress", "sensorName", 0, 2048);
|
||||
|
||||
// when
|
||||
previous.toString();
|
||||
|
||||
+41
-71
@@ -2,7 +2,7 @@ package de.dennisguse.opentracks.io.file.importer;
|
||||
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.location.Location;
|
||||
import android.content.SharedPreferences;
|
||||
import android.os.Looper;
|
||||
import android.util.Log;
|
||||
|
||||
@@ -23,6 +23,7 @@ import org.junit.runners.JUnit4;
|
||||
import java.io.ByteArrayInputStream;
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.io.InputStream;
|
||||
import java.time.Instant;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
@@ -31,6 +32,7 @@ import java.util.stream.Collectors;
|
||||
|
||||
import de.dennisguse.opentracks.R;
|
||||
import de.dennisguse.opentracks.content.data.Marker;
|
||||
import de.dennisguse.opentracks.content.data.TestDataUtil;
|
||||
import de.dennisguse.opentracks.content.data.Track;
|
||||
import de.dennisguse.opentracks.content.data.TrackPoint;
|
||||
import de.dennisguse.opentracks.content.provider.ContentProviderUtils;
|
||||
@@ -69,6 +71,7 @@ public class ExportImportTest {
|
||||
}
|
||||
|
||||
private final Context context = ApplicationProvider.getApplicationContext();
|
||||
private final SharedPreferences sharedPreferences = PreferencesUtils.getSharedPreferences(context);
|
||||
|
||||
private final ContentProviderUtils contentProviderUtils = new ContentProviderUtils(context);
|
||||
|
||||
@@ -89,17 +92,17 @@ public class ExportImportTest {
|
||||
|
||||
trackId = service.startNewTrack();
|
||||
|
||||
service.newTrackPoint(createTrackPoint(System.currentTimeMillis(), 3, 14, 10, 15, 10, 0, 66, 3, 50), 0);
|
||||
service.newTrackPoint(createTrackPoint(System.currentTimeMillis(), 3, 14, 10, 15, 10, 0, 66, 3, 50, 5), 0);
|
||||
service.insertMarker("Marker 1", "Marker 1 category", "Marker 1 desc", null);
|
||||
service.newTrackPoint(createTrackPoint(System.currentTimeMillis(), 3, 14.001, 10, 15, 10, 0, 66, 3, 50), 0);
|
||||
service.newTrackPoint(createTrackPoint(System.currentTimeMillis(), 3, 14.002, 10, 15, 10, 0, 66, 3, 50), 0);
|
||||
service.newTrackPoint(createTrackPoint(System.currentTimeMillis(), 3, 14.001, 10, 15, 10, 0, 66, 3, 50, 5), 0);
|
||||
service.newTrackPoint(createTrackPoint(System.currentTimeMillis(), 3, 14.002, 10, 15, 10, 0, 66, 3, 50, 5), 0);
|
||||
service.insertMarker("Marker 2", "Marker 2 category", "Marker 2 desc", null);
|
||||
service.pauseCurrentTrack();
|
||||
|
||||
service.resumeCurrentTrack();
|
||||
service.newTrackPoint(createTrackPoint(System.currentTimeMillis(), 3, 14.003, 10, 15, 10, 0, 66, 3, 50), 0);
|
||||
service.newTrackPoint(createTrackPoint(System.currentTimeMillis(), 3, 16, 10, 15, 10, 0, 66, 3, 50), 0);
|
||||
service.newTrackPoint(createTrackPoint(System.currentTimeMillis(), 3, 16.001, 10, 15, 10, 0, 66, 3, 50), 0);
|
||||
service.newTrackPoint(createTrackPoint(System.currentTimeMillis(), 3, 14.003, 10, 15, 10, 0, 66, 3, 50, 5), 0);
|
||||
service.newTrackPoint(createTrackPoint(System.currentTimeMillis(), 3, 16, 10, 15, 10, 0, 66, 3, 50, 5), 0);
|
||||
service.newTrackPoint(createTrackPoint(System.currentTimeMillis(), 3, 16.001, 10, 15, 10, 0, 66, 3, 50, 5), 0);
|
||||
service.endCurrentTrack();
|
||||
|
||||
track = contentProviderUtils.getTrack(trackId);
|
||||
@@ -109,7 +112,7 @@ public class ExportImportTest {
|
||||
contentProviderUtils.updateTrack(track);
|
||||
|
||||
track = contentProviderUtils.getTrack(trackId);
|
||||
trackPoints = contentProviderUtils.getTrackPoints(trackId);
|
||||
trackPoints = TestDataUtil.getTrackPoints(contentProviderUtils, trackId);
|
||||
markers = contentProviderUtils.getMarkers(trackId);
|
||||
assertEquals(10, trackPoints.size());
|
||||
assertEquals(2, markers.size());
|
||||
@@ -148,7 +151,7 @@ public class ExportImportTest {
|
||||
// 2. import
|
||||
InputStream inputStream = new ByteArrayInputStream(outputStream.toByteArray());
|
||||
AbstractFileTrackImporter trackImporter = new KmlFileTrackImporter(context);
|
||||
importTrackId = trackImporter.importFile(inputStream);
|
||||
importTrackId = trackImporter.importFile(inputStream).get(0);
|
||||
|
||||
// then
|
||||
// 1. track
|
||||
@@ -161,10 +164,10 @@ public class ExportImportTest {
|
||||
assertEquals(track.getUuid(), importedTrack.getUuid());
|
||||
|
||||
// 2. trackpoints
|
||||
assertTrackpoints(trackPoints, false, false, false, false, false);
|
||||
assertTrackpoints(trackPoints, false, false, false, false, false, false);
|
||||
|
||||
// 3. trackstatistics
|
||||
assertTrackStatistics(false, false);
|
||||
assertTrackStatistics(false, false, false);
|
||||
|
||||
// 4. markers
|
||||
assertMarkers();
|
||||
@@ -187,7 +190,7 @@ public class ExportImportTest {
|
||||
// 2. import
|
||||
InputStream inputStream = new ByteArrayInputStream(outputStream.toByteArray());
|
||||
AbstractFileTrackImporter trackImporter = new KmlFileTrackImporter(context);
|
||||
importTrackId = trackImporter.importFile(inputStream);
|
||||
importTrackId = trackImporter.importFile(inputStream).get(0);
|
||||
|
||||
// then
|
||||
// 1. track
|
||||
@@ -199,10 +202,10 @@ public class ExportImportTest {
|
||||
assertEquals(track.getIcon(), importedTrack.getIcon());
|
||||
|
||||
// 2. trackpoints
|
||||
assertTrackpoints(trackPoints, true, true, true, true, true);
|
||||
assertTrackpoints(trackPoints, true, true, true, true, true, true);
|
||||
|
||||
// 2. trackstatistics
|
||||
assertTrackStatistics(false, true);
|
||||
assertTrackStatistics(false, true, true);
|
||||
|
||||
// 4. markers
|
||||
assertMarkers();
|
||||
@@ -212,7 +215,7 @@ public class ExportImportTest {
|
||||
@Test(expected = ImportAlreadyExistsException.class)
|
||||
public void kml_with_trackdetail_and_sensordata_duplicate_trackUUID() {
|
||||
// given
|
||||
PreferencesUtils.setBoolean(context, R.string.import_prevent_reimport_key, true);
|
||||
PreferencesUtils.setBoolean(sharedPreferences, context, R.string.import_prevent_reimport_key, true);
|
||||
Track track = contentProviderUtils.getTrack(trackId);
|
||||
|
||||
TrackExporter trackExporter = TrackFileFormat.KML_WITH_TRACKDETAIL_AND_SENSORDATA.createTrackExporter(context);
|
||||
@@ -225,45 +228,13 @@ public class ExportImportTest {
|
||||
// 2. import
|
||||
InputStream inputStream = new ByteArrayInputStream(outputStream.toByteArray());
|
||||
AbstractFileTrackImporter trackImporter = new KmlFileTrackImporter(context);
|
||||
importTrackId = trackImporter.importFile(inputStream);
|
||||
importTrackId = trackImporter.importFile(inputStream).get(0);
|
||||
|
||||
// then
|
||||
Track importedTrack = contentProviderUtils.getTrack(importTrackId);
|
||||
assertNull(importedTrack);
|
||||
}
|
||||
|
||||
@Ignore
|
||||
@LargeTest
|
||||
@Test
|
||||
public void kmz_only_track() {
|
||||
// TODO
|
||||
Log.e(TAG, "Test not implemented.");
|
||||
}
|
||||
|
||||
@Ignore
|
||||
@LargeTest
|
||||
@Test
|
||||
public void kmz_with_trackdetail() {
|
||||
// TODO
|
||||
Log.e(TAG, "Test not implemented.");
|
||||
}
|
||||
|
||||
@Ignore
|
||||
@LargeTest
|
||||
@Test
|
||||
public void kmz_with_trackdetail_and_sensordata() {
|
||||
// TODO
|
||||
Log.e(TAG, "Test not implemented.");
|
||||
}
|
||||
|
||||
@Ignore
|
||||
@LargeTest
|
||||
@Test
|
||||
public void kmz_with_trackdetail_and_sensordata_and_pictures() {
|
||||
// TODO
|
||||
Log.e(TAG, "Test not implemented.");
|
||||
}
|
||||
|
||||
@LargeTest
|
||||
@Test
|
||||
public void gpx() {
|
||||
@@ -281,7 +252,7 @@ public class ExportImportTest {
|
||||
// 2. import
|
||||
InputStream inputStream = new ByteArrayInputStream(outputStream.toByteArray());
|
||||
AbstractFileTrackImporter trackImporter = new GpxFileTrackImporter(context, contentProviderUtils);
|
||||
importTrackId = trackImporter.importFile(inputStream);
|
||||
importTrackId = trackImporter.importFile(inputStream).get(0);
|
||||
|
||||
// then
|
||||
// 1. track
|
||||
@@ -301,10 +272,10 @@ public class ExportImportTest {
|
||||
trackPointsWithCoordinates.get(0).setType(TrackPoint.Type.SEGMENT_START_AUTOMATIC);
|
||||
trackPointsWithCoordinates.get(3).setType(TrackPoint.Type.SEGMENT_START_AUTOMATIC);
|
||||
|
||||
assertTrackpoints(trackPointsWithCoordinates, false, true, true, true, true);
|
||||
assertTrackpoints(trackPointsWithCoordinates, true, true, true, true, true, false);
|
||||
|
||||
// 3. trackstatistics
|
||||
assertTrackStatistics(true, true);
|
||||
assertTrackStatistics(true, true, false);
|
||||
|
||||
// 4. markers
|
||||
assertMarkers();
|
||||
@@ -314,7 +285,7 @@ public class ExportImportTest {
|
||||
@Test(expected = ImportAlreadyExistsException.class)
|
||||
public void gpx_duplicate_trackUUID() {
|
||||
// given
|
||||
PreferencesUtils.setBoolean(context, R.string.import_prevent_reimport_key, true);
|
||||
PreferencesUtils.setBoolean(sharedPreferences, context, R.string.import_prevent_reimport_key, true);
|
||||
Track track = contentProviderUtils.getTrack(trackId);
|
||||
|
||||
TrackExporter trackExporter = TrackFileFormat.GPX.createTrackExporter(context);
|
||||
@@ -327,7 +298,7 @@ public class ExportImportTest {
|
||||
// 2. import
|
||||
InputStream inputStream = new ByteArrayInputStream(outputStream.toByteArray());
|
||||
AbstractFileTrackImporter trackImporter = new GpxFileTrackImporter(context, contentProviderUtils);
|
||||
importTrackId = trackImporter.importFile(inputStream);
|
||||
importTrackId = trackImporter.importFile(inputStream).get(0);
|
||||
|
||||
// then
|
||||
// 1. track
|
||||
@@ -354,9 +325,8 @@ public class ExportImportTest {
|
||||
}
|
||||
}
|
||||
|
||||
private void assertTrackpoints(List<TrackPoint> trackPoints, boolean verifyPower, boolean verifyHeartrate, boolean verifyCadence, boolean verifyElevationGain, boolean verifyElevationLoss) {
|
||||
List<TrackPoint> importedTrackPoints = contentProviderUtils.getTrackPoints(importTrackId);
|
||||
|
||||
private void assertTrackpoints(List<TrackPoint> trackPoints, boolean verifyPower, boolean verifyHeartrate, boolean verifyCadence, boolean verifyElevationGain, boolean verifyElevationLoss, boolean verifyDistance) {
|
||||
List<TrackPoint> importedTrackPoints = TestDataUtil.getTrackPoints(contentProviderUtils, importTrackId);
|
||||
assertEquals(trackPoints.size(), importedTrackPoints.size());
|
||||
|
||||
for (int i = 0; i < trackPoints.size(); i++) {
|
||||
@@ -391,13 +361,13 @@ public class ExportImportTest {
|
||||
}
|
||||
|
||||
if (verifyHeartrate) {
|
||||
assertEquals(trackPoint.getHeartRate_bpm(), importedTrackPoint.getHeartRate_bpm(), 0.01);
|
||||
assertEquals("" + trackPoint, trackPoint.getHeartRate_bpm(), importedTrackPoint.getHeartRate_bpm(), 0.01);
|
||||
}
|
||||
if (verifyCadence) {
|
||||
assertEquals(trackPoint.getCyclingCadence_rpm(), importedTrackPoint.getCyclingCadence_rpm(), 0.01);
|
||||
assertEquals("" + trackPoint, trackPoint.getCyclingCadence_rpm(), importedTrackPoint.getCyclingCadence_rpm(), 0.01);
|
||||
}
|
||||
if (verifyPower) {
|
||||
assertEquals(trackPoint.getPower(), importedTrackPoint.getPower(), 0.01);
|
||||
assertEquals("" + trackPoint, trackPoint.getPower(), importedTrackPoint.getPower(), 0.01);
|
||||
}
|
||||
if (verifyElevationGain) {
|
||||
assertEquals(trackPoint.getElevationGain(), importedTrackPoint.getElevationGain(), 0.01);
|
||||
@@ -405,10 +375,13 @@ public class ExportImportTest {
|
||||
if (verifyElevationLoss) {
|
||||
assertEquals(trackPoint.getElevationLoss(), importedTrackPoint.getElevationLoss(), 0.01);
|
||||
}
|
||||
if (verifyDistance) {
|
||||
assertEquals(trackPoint.getSensorDistance(), importedTrackPoint.getSensorDistance(), 0.01);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void assertTrackStatistics(boolean isGpx, boolean verifyElevationGainAndLoss) {
|
||||
private void assertTrackStatistics(boolean isGpx, boolean verifyElevationGainAndLoss, boolean verifyDistance) {
|
||||
Track importedTrack = contentProviderUtils.getTrack(importTrackId);
|
||||
|
||||
assertNotNull(importedTrack.getTrackStatistics());
|
||||
@@ -426,7 +399,9 @@ public class ExportImportTest {
|
||||
assertEquals(trackStatistics.getMovingTime(), importedTrackStatistics.getMovingTime());
|
||||
|
||||
// Distance
|
||||
assertEquals(trackStatistics.getTotalDistance(), importedTrackStatistics.getTotalDistance(), 0.01);
|
||||
if (verifyDistance) {
|
||||
assertEquals(trackStatistics.getTotalDistance(), importedTrackStatistics.getTotalDistance(), 0.01);
|
||||
}
|
||||
|
||||
// Speed
|
||||
assertEquals(trackStatistics.getMaxSpeed(), importedTrackStatistics.getMaxSpeed(), 0.01);
|
||||
@@ -446,20 +421,15 @@ public class ExportImportTest {
|
||||
}
|
||||
}
|
||||
|
||||
private static TrackPoint createTrackPoint(long time, double latitude, double longitude, float accuracy, long speed, long altitude, float elevationGain, float heartRate, float cyclingCadence, float power) {
|
||||
Location location = new Location("");
|
||||
location.setTime(time);
|
||||
location.setLongitude(longitude);
|
||||
location.setLatitude(latitude);
|
||||
location.setAccuracy(accuracy);
|
||||
location.setAltitude(altitude);
|
||||
location.setSpeed(speed);
|
||||
|
||||
TrackPoint tp = new TrackPoint(location);
|
||||
private static TrackPoint createTrackPoint(long time, double latitude, double longitude, float accuracy, float speed, float altitude, float elevationGain, float heartRate, float cyclingCadence, float power, float distance) {
|
||||
TrackPoint tp = new TrackPoint(latitude, longitude, (double) altitude, Instant.ofEpochMilli(time));
|
||||
tp.setAccuracy(accuracy);
|
||||
tp.setSpeed(speed);
|
||||
tp.setHeartRate_bpm(heartRate);
|
||||
tp.setCyclingCadence_rpm(cyclingCadence);
|
||||
tp.setPower(power);
|
||||
tp.setElevationGain(elevationGain);
|
||||
tp.setSensorDistance(distance);
|
||||
return tp;
|
||||
}
|
||||
}
|
||||
+6
-5
@@ -14,6 +14,7 @@ import org.junit.runners.JUnit4;
|
||||
import java.io.InputStream;
|
||||
import java.util.List;
|
||||
|
||||
import de.dennisguse.opentracks.content.data.TestDataUtil;
|
||||
import de.dennisguse.opentracks.content.data.Track;
|
||||
import de.dennisguse.opentracks.content.data.TrackPoint;
|
||||
import de.dennisguse.opentracks.content.provider.ContentProviderUtils;
|
||||
@@ -54,7 +55,7 @@ public class LegacyImportTest {
|
||||
|
||||
// when
|
||||
// 1. import
|
||||
importTrackId = trackImporter.importFile(inputStream);
|
||||
importTrackId = trackImporter.importFile(inputStream).get(0);
|
||||
|
||||
// then
|
||||
// 1. track
|
||||
@@ -69,7 +70,7 @@ public class LegacyImportTest {
|
||||
assertEquals(0, contentProviderUtils.getMarkerCount(importTrackId));
|
||||
|
||||
// 3. trackpoints
|
||||
List<TrackPoint> importedTrackPoints = contentProviderUtils.getTrackPoints(importTrackId);
|
||||
List<TrackPoint> importedTrackPoints = TestDataUtil.getTrackPoints(contentProviderUtils, importTrackId);
|
||||
assertEquals(6, importedTrackPoints.size());
|
||||
|
||||
// first 3 trackpoints
|
||||
@@ -95,7 +96,7 @@ public class LegacyImportTest {
|
||||
InputStream inputStream = InstrumentationRegistry.getInstrumentation().getContext().getResources().openRawResource(de.dennisguse.opentracks.debug.test.R.raw.legacy_kml_empty);
|
||||
|
||||
// when
|
||||
importTrackId = trackImporter.importFile(inputStream);
|
||||
importTrackId = trackImporter.importFile(inputStream).get(0);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -110,7 +111,7 @@ public class LegacyImportTest {
|
||||
|
||||
// when
|
||||
// 1. import
|
||||
importTrackId = trackImporter.importFile(inputStream);
|
||||
importTrackId = trackImporter.importFile(inputStream).get(0);
|
||||
|
||||
// then
|
||||
// 1. track
|
||||
@@ -122,7 +123,7 @@ public class LegacyImportTest {
|
||||
assertEquals("UNKNOWN", importedTrack.getIcon());
|
||||
|
||||
// 3. trackpoints
|
||||
List<TrackPoint> importedTrackPoints = contentProviderUtils.getTrackPoints(importTrackId);
|
||||
List<TrackPoint> importedTrackPoints = TestDataUtil.getTrackPoints(contentProviderUtils, importTrackId);
|
||||
assertEquals(6, importedTrackPoints.size());
|
||||
|
||||
// first segment
|
||||
|
||||
+13
-7
@@ -12,6 +12,7 @@ import org.mockito.Mock;
|
||||
import org.mockito.junit.MockitoJUnitRunner;
|
||||
|
||||
import de.dennisguse.opentracks.content.data.TrackPoint;
|
||||
import de.dennisguse.opentracks.stats.TrackStatistics;
|
||||
|
||||
import static org.mockito.ArgumentMatchers.anyBoolean;
|
||||
import static org.mockito.ArgumentMatchers.anyString;
|
||||
@@ -25,7 +26,10 @@ public class TrackRecordingServiceNotificationManagerTest {
|
||||
private final Context context = ApplicationProvider.getApplicationContext();
|
||||
|
||||
@Mock
|
||||
private TrackPoint locationMock;
|
||||
private TrackPoint trackPointMock;
|
||||
|
||||
@Mock
|
||||
private TrackStatistics trackStatisticsMock;
|
||||
|
||||
@Mock
|
||||
private NotificationCompat.Builder notificationCompatBuilder;
|
||||
@@ -35,18 +39,20 @@ public class TrackRecordingServiceNotificationManagerTest {
|
||||
|
||||
@Test
|
||||
public void updateLocation_triggersAlertOnlyOnFirstInaccurateLocation() {
|
||||
when(locationMock.hasAccuracy()).thenReturn(true);
|
||||
when(locationMock.getAccuracy()).thenReturn(999f);
|
||||
when(trackPointMock.hasAccuracy()).thenReturn(true);
|
||||
when(trackPointMock.getAccuracy()).thenReturn(999f);
|
||||
when(trackStatisticsMock.getTotalDistance()).thenReturn(0d);
|
||||
when(notificationCompatBuilder.setContentText(anyString())).thenReturn(notificationCompatBuilder);
|
||||
when(notificationCompatBuilder.setOnlyAlertOnce(anyBoolean())).thenReturn(notificationCompatBuilder);
|
||||
|
||||
TrackRecordingServiceNotificationManager subject = new TrackRecordingServiceNotificationManager(notificationManager, notificationCompatBuilder);
|
||||
subject.setMetricUnits(true);
|
||||
|
||||
// when
|
||||
subject.updateTrackPoint(context, locationMock, 100);
|
||||
subject.updateTrackPoint(context, locationMock, 100);
|
||||
subject.updateTrackPoint(context, locationMock, 1000);
|
||||
subject.updateTrackPoint(context, locationMock, 100);
|
||||
subject.updateTrackPoint(context, trackStatisticsMock, trackPointMock, 100);
|
||||
subject.updateTrackPoint(context, trackStatisticsMock, trackPointMock, 100);
|
||||
subject.updateTrackPoint(context, trackStatisticsMock, trackPointMock, 1000);
|
||||
subject.updateTrackPoint(context, trackStatisticsMock, trackPointMock, 100);
|
||||
|
||||
// then
|
||||
verify(notificationCompatBuilder, times(6)).setOnlyAlertOnce(true);
|
||||
|
||||
+16
-16
@@ -45,6 +45,7 @@ import java.util.concurrent.TimeoutException;
|
||||
|
||||
import de.dennisguse.opentracks.R;
|
||||
import de.dennisguse.opentracks.content.data.Marker;
|
||||
import de.dennisguse.opentracks.content.data.TestDataUtil;
|
||||
import de.dennisguse.opentracks.content.data.Track;
|
||||
import de.dennisguse.opentracks.content.data.TrackPoint;
|
||||
import de.dennisguse.opentracks.content.provider.ContentProviderUtils;
|
||||
@@ -84,6 +85,7 @@ public class TrackRecordingServiceTest {
|
||||
|
||||
|
||||
private final Context context = ApplicationProvider.getApplicationContext();
|
||||
private final SharedPreferences sharedPreferences = PreferencesUtils.getSharedPreferences(context);
|
||||
private ContentProviderUtils contentProviderUtils;
|
||||
|
||||
private final Track.Id trackId = new Track.Id(Math.abs(new Random().nextLong()));
|
||||
@@ -102,7 +104,6 @@ public class TrackRecordingServiceTest {
|
||||
contentProviderUtils = new ContentProviderUtils(context);
|
||||
|
||||
// Let's use default values.
|
||||
SharedPreferences sharedPreferences = PreferencesUtils.getSharedPreferences(context);
|
||||
sharedPreferences.edit().clear().commit();
|
||||
|
||||
// Ensure that the database is empty before every test
|
||||
@@ -190,7 +191,7 @@ public class TrackRecordingServiceTest {
|
||||
Track.Id trackId = service.startNewTrack();
|
||||
|
||||
// then
|
||||
List<TrackPoint> trackPoints = contentProviderUtils.getTrackPoints(trackId);
|
||||
List<TrackPoint> trackPoints = TestDataUtil.getTrackPoints(contentProviderUtils, trackId);
|
||||
|
||||
assertEquals(1, trackPoints.size());
|
||||
assertEquals(TrackPoint.Type.SEGMENT_START_MANUAL, trackPoints.get(0).getType());
|
||||
@@ -207,7 +208,7 @@ public class TrackRecordingServiceTest {
|
||||
service.endCurrentTrack();
|
||||
|
||||
// then
|
||||
List<TrackPoint> trackPoints = contentProviderUtils.getTrackPoints(trackId);
|
||||
List<TrackPoint> trackPoints = TestDataUtil.getTrackPoints(contentProviderUtils, trackId);
|
||||
|
||||
assertEquals(2, trackPoints.size());
|
||||
assertEquals(TrackPoint.Type.SEGMENT_START_MANUAL, trackPoints.get(0).getType());
|
||||
@@ -225,7 +226,7 @@ public class TrackRecordingServiceTest {
|
||||
service.pauseCurrentTrack();
|
||||
|
||||
// then
|
||||
assertEquals(2, contentProviderUtils.getTrackPoints(trackId).size());
|
||||
assertEquals(2, contentProviderUtils.getTrackPointCursor(trackId, null).getCount());
|
||||
|
||||
//when
|
||||
service.resumeTrack(trackId);
|
||||
@@ -234,7 +235,7 @@ public class TrackRecordingServiceTest {
|
||||
assertTrue(service.isRecording());
|
||||
assertEquals(trackId, service.getRecordingTrackId());
|
||||
|
||||
List<TrackPoint> trackPoints = contentProviderUtils.getTrackPoints(trackId);
|
||||
List<TrackPoint> trackPoints = TestDataUtil.getTrackPoints(contentProviderUtils, trackId);
|
||||
assertEquals(3, trackPoints.size());
|
||||
assertEquals(TrackPoint.Type.SEGMENT_START_MANUAL, trackPoints.get(0).getType());
|
||||
assertEquals(TrackPoint.Type.SEGMENT_END_MANUAL, trackPoints.get(1).getType());
|
||||
@@ -250,7 +251,7 @@ public class TrackRecordingServiceTest {
|
||||
assertTrue(service.isRecording());
|
||||
service.endCurrentTrack();
|
||||
|
||||
assertEquals(2, contentProviderUtils.getTrackPoints(trackId).size());
|
||||
assertEquals(2, contentProviderUtils.getTrackPointCursor(trackId, null).getCount());
|
||||
|
||||
// when
|
||||
service.resumeTrack(trackId);
|
||||
@@ -260,7 +261,7 @@ public class TrackRecordingServiceTest {
|
||||
assertTrue(service.isRecording());
|
||||
assertEquals(trackId, service.getRecordingTrackId());
|
||||
|
||||
List<TrackPoint> trackPoints = contentProviderUtils.getTrackPoints(trackId);
|
||||
List<TrackPoint> trackPoints = TestDataUtil.getTrackPoints(contentProviderUtils, trackId);
|
||||
assertEquals(4, trackPoints.size());
|
||||
assertEquals(TrackPoint.Type.SEGMENT_START_MANUAL, trackPoints.get(0).getType());
|
||||
assertEquals(TrackPoint.Type.SEGMENT_END_MANUAL, trackPoints.get(1).getType());
|
||||
@@ -277,7 +278,7 @@ public class TrackRecordingServiceTest {
|
||||
assertTrue(service.isRecording());
|
||||
service.pauseCurrentTrack();
|
||||
|
||||
assertEquals(2, contentProviderUtils.getTrackPoints(trackId).size());
|
||||
assertEquals(2, contentProviderUtils.getTrackPointCursor(trackId, null).getCount());
|
||||
|
||||
// when
|
||||
service.endCurrentTrack();
|
||||
@@ -286,7 +287,7 @@ public class TrackRecordingServiceTest {
|
||||
assertFalse(service.isRecording());
|
||||
assertNull(service.getRecordingTrackId());
|
||||
|
||||
List<TrackPoint> trackPoints = contentProviderUtils.getTrackPoints(trackId);
|
||||
List<TrackPoint> trackPoints = TestDataUtil.getTrackPoints(contentProviderUtils, trackId);
|
||||
assertEquals(2, trackPoints.size());
|
||||
assertEquals(TrackPoint.Type.SEGMENT_START_MANUAL, trackPoints.get(0).getType());
|
||||
assertEquals(TrackPoint.Type.SEGMENT_END_MANUAL, trackPoints.get(1).getType());
|
||||
@@ -302,7 +303,7 @@ public class TrackRecordingServiceTest {
|
||||
// when
|
||||
// Just set recording track to a bogus value.
|
||||
// Make sure that the service will not start recording and will clear the bogus track.
|
||||
PreferencesUtils.setLong(context, R.string.recording_track_id_key, 123L);
|
||||
PreferencesUtils.setLong(sharedPreferences, context, R.string.recording_track_id_key, 123L);
|
||||
|
||||
// then
|
||||
assertFalse(service.isRecording());
|
||||
@@ -325,7 +326,7 @@ public class TrackRecordingServiceTest {
|
||||
// then
|
||||
assertNull(newTrackId);
|
||||
|
||||
assertEquals(trackId, PreferencesUtils.getRecordingTrackId(context));
|
||||
assertEquals(trackId, PreferencesUtils.getRecordingTrackId(sharedPreferences, context));
|
||||
assertEquals(trackId, service.getRecordingTrackId());
|
||||
}
|
||||
|
||||
@@ -341,7 +342,7 @@ public class TrackRecordingServiceTest {
|
||||
service.endCurrentTrack();
|
||||
|
||||
// then
|
||||
assertFalse(PreferencesUtils.isRecording(context));
|
||||
assertFalse(PreferencesUtils.isRecording(sharedPreferences, context));
|
||||
assertNull(service.getRecordingTrackId());
|
||||
}
|
||||
|
||||
@@ -388,8 +389,8 @@ public class TrackRecordingServiceTest {
|
||||
assertNotNull(track.getId());
|
||||
contentProviderUtils.insertTrack(track);
|
||||
assertEquals(track.getId(), contentProviderUtils.getTrack(track.getId()).getId());
|
||||
PreferencesUtils.setLong(context, R.string.recording_track_id_key, isRecording ? track.getId().getId() : PreferencesUtils.RECORDING_TRACK_ID_DEFAULT);
|
||||
PreferencesUtils.setBoolean(context, R.string.recording_track_paused_key, !isRecording);
|
||||
PreferencesUtils.setLong(sharedPreferences, context, R.string.recording_track_id_key, isRecording ? track.getId().getId() : PreferencesUtils.RECORDING_TRACK_ID_DEFAULT);
|
||||
PreferencesUtils.setBoolean(sharedPreferences, context, R.string.recording_track_paused_key, !isRecording);
|
||||
}
|
||||
|
||||
// NOTE: Do not use to create a track that is currently recording.
|
||||
@@ -422,7 +423,6 @@ public class TrackRecordingServiceTest {
|
||||
location.setTime(time);
|
||||
location.setBearing(3.0f);
|
||||
TrackPoint trackPoint = new TrackPoint(location);
|
||||
int prefAccuracy = PreferencesUtils.getRecordingGPSAccuracy(ApplicationProvider.getApplicationContext());
|
||||
trackRecordingService.newTrackPoint(trackPoint, prefAccuracy);
|
||||
trackRecordingService.newTrackPoint(trackPoint, 50);
|
||||
}
|
||||
}
|
||||
|
||||
+7
-6
@@ -23,6 +23,7 @@ import java.util.List;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import java.util.concurrent.TimeoutException;
|
||||
|
||||
import de.dennisguse.opentracks.content.data.TestDataUtil;
|
||||
import de.dennisguse.opentracks.content.data.Track;
|
||||
import de.dennisguse.opentracks.content.data.TrackPoint;
|
||||
import de.dennisguse.opentracks.content.provider.ContentProviderUtils;
|
||||
@@ -104,7 +105,7 @@ public class TrackRecordingServiceTestLocation {
|
||||
// then
|
||||
assertFalse(service.isRecording());
|
||||
|
||||
List<TrackPoint> trackPoints = contentProviderUtils.getTrackPoints(trackId);
|
||||
List<TrackPoint> trackPoints = TestDataUtil.getTrackPoints(contentProviderUtils, trackId);
|
||||
assertEquals(8, trackPoints.size());
|
||||
assertTrackPoints(List.of(
|
||||
new Pair<>(TrackPoint.Type.SEGMENT_START_MANUAL, null),
|
||||
@@ -138,7 +139,7 @@ public class TrackRecordingServiceTestLocation {
|
||||
// then
|
||||
assertFalse(service.isRecording());
|
||||
|
||||
List<TrackPoint> trackPoints = contentProviderUtils.getTrackPoints(trackId);
|
||||
List<TrackPoint> trackPoints = TestDataUtil.getTrackPoints(contentProviderUtils, trackId);
|
||||
assertEquals(4, trackPoints.size());
|
||||
assertTrackPoints(List.of(
|
||||
new Pair<>(TrackPoint.Type.SEGMENT_START_MANUAL, null),
|
||||
@@ -185,7 +186,7 @@ public class TrackRecordingServiceTestLocation {
|
||||
// then
|
||||
assertFalse(service.isRecording());
|
||||
|
||||
List<TrackPoint> trackPoints = contentProviderUtils.getTrackPoints(trackId);
|
||||
List<TrackPoint> trackPoints = TestDataUtil.getTrackPoints(contentProviderUtils, trackId);
|
||||
assertTrackPoints(List.of(
|
||||
new Pair<>(TrackPoint.Type.SEGMENT_START_MANUAL, null),
|
||||
new Pair<>(TrackPoint.Type.TRACKPOINT, 1),
|
||||
@@ -214,7 +215,7 @@ public class TrackRecordingServiceTestLocation {
|
||||
// then
|
||||
assertFalse(service.isRecording());
|
||||
|
||||
List<TrackPoint> trackPoints = contentProviderUtils.getTrackPoints(trackId);
|
||||
List<TrackPoint> trackPoints = TestDataUtil.getTrackPoints(contentProviderUtils, trackId);
|
||||
assertEquals(6, trackPoints.size());
|
||||
assertTrackPoints(List.of(
|
||||
new Pair<>(TrackPoint.Type.SEGMENT_START_MANUAL, null),
|
||||
@@ -260,7 +261,7 @@ public class TrackRecordingServiceTestLocation {
|
||||
// then
|
||||
assertFalse(service.isRecording());
|
||||
|
||||
List<TrackPoint> trackPoints = contentProviderUtils.getTrackPoints(trackId);
|
||||
List<TrackPoint> trackPoints = TestDataUtil.getTrackPoints(contentProviderUtils, trackId);
|
||||
assertEquals(8, trackPoints.size());
|
||||
assertTrackPoints(List.of(
|
||||
new Pair<>(TrackPoint.Type.SEGMENT_START_MANUAL, null),
|
||||
@@ -292,7 +293,7 @@ public class TrackRecordingServiceTestLocation {
|
||||
// then
|
||||
assertFalse(service.isRecording());
|
||||
|
||||
List<TrackPoint> trackPoints = contentProviderUtils.getTrackPoints(trackId);
|
||||
List<TrackPoint> trackPoints = TestDataUtil.getTrackPoints(contentProviderUtils, trackId);
|
||||
assertEquals(7, trackPoints.size());
|
||||
assertTrackPoints(List.of(
|
||||
new Pair<>(TrackPoint.Type.SEGMENT_START_MANUAL, null),
|
||||
|
||||
+16
-16
@@ -53,6 +53,7 @@ public class TrackRecordingServiceTestLooper {
|
||||
public final ServiceTestRule mServiceRule = ServiceTestRule.withTimeout(5, TimeUnit.SECONDS);
|
||||
|
||||
private final Context context = ApplicationProvider.getApplicationContext();
|
||||
private final SharedPreferences sharedPreferences = PreferencesUtils.getSharedPreferences(context);
|
||||
|
||||
private ContentProviderUtils contentProviderUtils;
|
||||
|
||||
@@ -77,7 +78,6 @@ public class TrackRecordingServiceTestLooper {
|
||||
contentProviderUtils = new ContentProviderUtils(context);
|
||||
|
||||
// Let's use default values.
|
||||
SharedPreferences sharedPreferences = PreferencesUtils.getSharedPreferences(context);
|
||||
sharedPreferences.edit().clear().apply();
|
||||
|
||||
// Ensure that the database is empty before every test
|
||||
@@ -99,28 +99,28 @@ public class TrackRecordingServiceTestLooper {
|
||||
@MediumTest
|
||||
@Test
|
||||
public void testWithProperties_minRequiredAccuracy() throws TimeoutException {
|
||||
PreferencesUtils.setInt(context, R.string.recording_gps_accuracy_key, 500);
|
||||
PreferencesUtils.setInt(sharedPreferences, context, R.string.recording_gps_accuracy_key, 500);
|
||||
fullRecordingSession();
|
||||
}
|
||||
|
||||
@MediumTest
|
||||
@Test
|
||||
public void testWithProperties_voiceFrequencyDefault() throws TimeoutException {
|
||||
PreferencesUtils.setInt(context, R.string.voice_frequency_key, Integer.parseInt(context.getResources().getString(R.string.voice_frequency_default)));
|
||||
PreferencesUtils.setInt(sharedPreferences, context, R.string.voice_frequency_key, Integer.parseInt(context.getResources().getString(R.string.voice_frequency_default)));
|
||||
fullRecordingSession();
|
||||
}
|
||||
|
||||
@MediumTest
|
||||
@Test
|
||||
public void testWithProperties_voiceFrequencyByDistance() throws TimeoutException {
|
||||
PreferencesUtils.setInt(context, R.string.voice_frequency_key, -1);
|
||||
PreferencesUtils.setInt(sharedPreferences, context, R.string.voice_frequency_key, -1);
|
||||
fullRecordingSession();
|
||||
}
|
||||
|
||||
@MediumTest
|
||||
@Test
|
||||
public void testWithProperties_voiceFrequencyByTime() throws TimeoutException {
|
||||
PreferencesUtils.setInt(context, R.string.voice_frequency_key, 1);
|
||||
PreferencesUtils.setInt(sharedPreferences, context, R.string.voice_frequency_key, 1);
|
||||
fullRecordingSession();
|
||||
}
|
||||
|
||||
@@ -128,14 +128,14 @@ public class TrackRecordingServiceTestLooper {
|
||||
@MediumTest
|
||||
@Test
|
||||
public void testWithProperties_maxRecordingDistanceDefault() throws TimeoutException {
|
||||
PreferencesUtils.setInt(context, R.string.max_recording_distance_key, Integer.parseInt(context.getResources().getString(R.string.max_recording_distance_default)));
|
||||
PreferencesUtils.setInt(sharedPreferences, context, R.string.max_recording_distance_key, Integer.parseInt(context.getResources().getString(R.string.max_recording_distance_default)));
|
||||
fullRecordingSession();
|
||||
}
|
||||
|
||||
@MediumTest
|
||||
@Test
|
||||
public void testWithProperties_maxRecordingDistance() throws TimeoutException {
|
||||
PreferencesUtils.setInt(context, R.string.max_recording_distance_key, 50);
|
||||
PreferencesUtils.setInt(sharedPreferences, context, R.string.max_recording_distance_key, 50);
|
||||
fullRecordingSession();
|
||||
}
|
||||
|
||||
@@ -143,42 +143,42 @@ public class TrackRecordingServiceTestLooper {
|
||||
@Test
|
||||
public void testWithProperties_minRecordingDistanceDefault() throws TimeoutException {
|
||||
int minRecordingIntervalDefault = Integer.parseInt(context.getResources().getString(R.string.min_recording_interval_default));
|
||||
PreferencesUtils.setInt(context, R.string.recording_distance_interval_key, minRecordingIntervalDefault);
|
||||
PreferencesUtils.setInt(sharedPreferences, context, R.string.recording_distance_interval_key, minRecordingIntervalDefault);
|
||||
fullRecordingSession();
|
||||
}
|
||||
|
||||
@MediumTest
|
||||
@Test
|
||||
public void testWithProperties_minRecordingDistance() throws TimeoutException {
|
||||
PreferencesUtils.setInt(context, R.string.recording_distance_interval_key, 2);
|
||||
PreferencesUtils.setInt(sharedPreferences, context, R.string.recording_distance_interval_key, 2);
|
||||
fullRecordingSession();
|
||||
}
|
||||
|
||||
@MediumTest
|
||||
@Test
|
||||
public void testWithProperties_metricUnitsDefault() throws TimeoutException {
|
||||
PreferencesUtils.setString(context, R.string.stats_units_key, context.getString(R.string.stats_units_default));
|
||||
PreferencesUtils.setString(sharedPreferences, context, R.string.stats_units_key, context.getString(R.string.stats_units_default));
|
||||
fullRecordingSession();
|
||||
}
|
||||
|
||||
@MediumTest
|
||||
@Test
|
||||
public void testWithProperties_metricUnitsDisabled() throws TimeoutException {
|
||||
PreferencesUtils.setString(context, R.string.stats_units_key, context.getString(R.string.stats_units_imperial));
|
||||
PreferencesUtils.setString(sharedPreferences, context, R.string.stats_units_key, context.getString(R.string.stats_units_imperial));
|
||||
fullRecordingSession();
|
||||
}
|
||||
|
||||
@MediumTest
|
||||
@Test
|
||||
public void testWithProperties_minRecordingIntervalDefault() throws TimeoutException {
|
||||
PreferencesUtils.setInt(context, R.string.min_recording_interval_key, Integer.parseInt(context.getResources().getString(R.string.min_recording_interval_default)));
|
||||
PreferencesUtils.setInt(sharedPreferences, context, R.string.min_recording_interval_key, Integer.parseInt(context.getResources().getString(R.string.min_recording_interval_default)));
|
||||
fullRecordingSession();
|
||||
}
|
||||
|
||||
@MediumTest
|
||||
@Test
|
||||
public void testWithProperties_minRecordingInterval() throws TimeoutException {
|
||||
PreferencesUtils.setInt(context, R.string.min_recording_interval_key, 2);
|
||||
PreferencesUtils.setInt(sharedPreferences, context, R.string.min_recording_interval_key, 2);
|
||||
fullRecordingSession();
|
||||
}
|
||||
|
||||
@@ -186,7 +186,7 @@ public class TrackRecordingServiceTestLooper {
|
||||
@Test
|
||||
public void testWithProperties_minRequiredAccuracyDefault() throws TimeoutException {
|
||||
int recordingGPSAccuracyDefault = Integer.parseInt(context.getResources().getString(R.string.recording_gps_accuracy_default));
|
||||
PreferencesUtils.setInt(context, R.string.recording_gps_accuracy_key, recordingGPSAccuracyDefault);
|
||||
PreferencesUtils.setInt(sharedPreferences, context, R.string.recording_gps_accuracy_key, recordingGPSAccuracyDefault);
|
||||
fullRecordingSession();
|
||||
}
|
||||
|
||||
@@ -209,7 +209,7 @@ public class TrackRecordingServiceTestLooper {
|
||||
Track track = contentProviderUtils.getTrack(trackId);
|
||||
assertNotNull(track);
|
||||
assertEquals(trackId, track.getId());
|
||||
assertEquals(trackId, PreferencesUtils.getRecordingTrackId(context));
|
||||
assertEquals(trackId, PreferencesUtils.getRecordingTrackId(sharedPreferences, context));
|
||||
assertEquals(trackId, service.getRecordingTrackId());
|
||||
|
||||
// Insert a few points, markers and statistics.
|
||||
@@ -223,7 +223,7 @@ public class TrackRecordingServiceTestLooper {
|
||||
location.setTime(startTime + i * 10000);
|
||||
location.setBearing(3.0f);
|
||||
TrackPoint trackPoint = new TrackPoint(location);
|
||||
int prefAccuracy = PreferencesUtils.getRecordingGPSAccuracy(context);
|
||||
int prefAccuracy = PreferencesUtils.getRecordingGPSAccuracy(sharedPreferences, context);
|
||||
service.newTrackPoint(trackPoint, prefAccuracy);
|
||||
|
||||
if (i % 7 == 0) {
|
||||
|
||||
+6
-2
@@ -1,6 +1,7 @@
|
||||
package de.dennisguse.opentracks.services.handlers;
|
||||
|
||||
import android.content.Context;
|
||||
import android.content.SharedPreferences;
|
||||
|
||||
import org.junit.After;
|
||||
import org.junit.Before;
|
||||
@@ -25,6 +26,9 @@ public class HandlerServerTest {
|
||||
@Mock
|
||||
private LocationHandler locationHandler;
|
||||
|
||||
@Mock
|
||||
private SharedPreferences sharedPreferences;
|
||||
|
||||
private HandlerServer subject;
|
||||
|
||||
@Before
|
||||
@@ -41,10 +45,10 @@ public class HandlerServerTest {
|
||||
@Test
|
||||
public void onSharedPreferenceChanged() {
|
||||
// when
|
||||
subject.onSharedPreferenceChanged(null, null, null);
|
||||
subject.onSharedPreferenceChanged(context, sharedPreferences, null);
|
||||
|
||||
// then
|
||||
verify(locationHandler).onSharedPreferenceChanged(null, null, null);
|
||||
verify(locationHandler).onSharedPreferenceChanged(context, sharedPreferences, null);
|
||||
}
|
||||
|
||||
@Test
|
||||
|
||||
+4
-4
@@ -27,6 +27,7 @@ import static org.mockito.Mockito.verify;
|
||||
public class LocationHandlerTest {
|
||||
|
||||
private final Context context = ApplicationProvider.getApplicationContext();
|
||||
private final SharedPreferences sharedPreferences = PreferencesUtils.getSharedPreferences(context);
|
||||
|
||||
@Mock
|
||||
private HandlerServer handlerServer;
|
||||
@@ -43,11 +44,10 @@ public class LocationHandlerTest {
|
||||
@Before
|
||||
public void setUp() {
|
||||
// Let's use default values.
|
||||
SharedPreferences sharedPreferences = PreferencesUtils.getSharedPreferences(context);
|
||||
sharedPreferences.edit().clear().commit();
|
||||
|
||||
locationHandler.onSharedPreferenceChanged(context, PreferencesUtils.getSharedPreferences(context), context.getString(R.string.recording_gps_accuracy_key));
|
||||
locationHandler.onSharedPreferenceChanged(context, PreferencesUtils.getSharedPreferences(context), context.getString(R.string.min_recording_interval_key));
|
||||
locationHandler.onSharedPreferenceChanged(context, sharedPreferences, context.getString(R.string.recording_gps_accuracy_key));
|
||||
locationHandler.onSharedPreferenceChanged(context, sharedPreferences, context.getString(R.string.min_recording_interval_key));
|
||||
locationHandler.onStart(context);
|
||||
}
|
||||
|
||||
@@ -85,7 +85,7 @@ public class LocationHandlerTest {
|
||||
@Test
|
||||
public void testOnLocationChanged_poorAccuracy() {
|
||||
// given
|
||||
int prefAccuracy = PreferencesUtils.getRecordingGPSAccuracy(context);
|
||||
int prefAccuracy = PreferencesUtils.getRecordingGPSAccuracy(sharedPreferences, context);
|
||||
|
||||
// when
|
||||
locationHandler.onLocationChanged(createLocation(45f, 35f, prefAccuracy + 1, 5, System.currentTimeMillis()));
|
||||
|
||||
+131
-4
@@ -1,27 +1,36 @@
|
||||
package de.dennisguse.opentracks.stats;
|
||||
|
||||
import androidx.test.ext.junit.runners.AndroidJUnit4;
|
||||
|
||||
import org.junit.Ignore;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
|
||||
import java.time.Duration;
|
||||
import java.time.Instant;
|
||||
|
||||
import de.dennisguse.opentracks.content.data.TestDataUtil;
|
||||
import de.dennisguse.opentracks.content.data.Track;
|
||||
import de.dennisguse.opentracks.content.data.TrackPoint;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
|
||||
@RunWith(AndroidJUnit4.class)
|
||||
public class TrackStatisticsUpdaterTest {
|
||||
|
||||
private static final int GPS_DISTANCE = 50;
|
||||
|
||||
@Test
|
||||
public void addTrackPoint() {
|
||||
public void addTrackPoint_TestingTrack() {
|
||||
// given
|
||||
TestDataUtil.TrackData data = TestDataUtil.createTestingTrack(new Track.Id(1));
|
||||
|
||||
// when
|
||||
TrackStatisticsUpdater updater = new TrackStatisticsUpdater();
|
||||
data.trackPoints.forEach(it -> updater.addTrackPoint(it, 50));
|
||||
TrackStatisticsUpdater subject = new TrackStatisticsUpdater();
|
||||
data.trackPoints.forEach(it -> subject.addTrackPoint(it, GPS_DISTANCE));
|
||||
|
||||
// then
|
||||
TrackStatistics statistics = updater.getTrackStatistics();
|
||||
TrackStatistics statistics = subject.getTrackStatistics();
|
||||
assertEquals(85.35, statistics.getTotalDistance(), 0.01);
|
||||
assertEquals(Duration.ofMillis(13999), statistics.getTotalTime());
|
||||
assertEquals(Duration.ofSeconds(6), statistics.getMovingTime());
|
||||
@@ -35,4 +44,122 @@ public class TrackStatisticsUpdaterTest {
|
||||
assertEquals(14.226, statistics.getAverageMovingSpeed(), 0.01);
|
||||
assertEquals(6.566, statistics.getAverageSpeed(), 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, 5.0, Instant.ofEpochMilli(2000));
|
||||
TrackPoint tp3 = new TrackPoint(0.00001, 0, 5.0, Instant.ofEpochMilli(3000));
|
||||
|
||||
// when
|
||||
subject.addTrackPoint(tp1, GPS_DISTANCE);
|
||||
subject.addTrackPoint(tp2, GPS_DISTANCE);
|
||||
subject.addTrackPoint(tp3, GPS_DISTANCE);
|
||||
|
||||
// then
|
||||
assertEquals(0, subject.getTrackStatistics().getTotalDistance(), 0.01);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void addTrackPoint_distance_from_GPS_moving() {
|
||||
// given
|
||||
TrackStatisticsUpdater subject = new TrackStatisticsUpdater();
|
||||
|
||||
TrackPoint tp1 = new TrackPoint(TrackPoint.Type.SEGMENT_START_MANUAL, Instant.ofEpochMilli(1000));
|
||||
TrackPoint tp2 = new TrackPoint(0, 0, 5.0, Instant.ofEpochMilli(2000));
|
||||
TrackPoint tp3 = new TrackPoint(0.00001, 0, 5.0, Instant.ofEpochMilli(3000));
|
||||
tp3.setSpeed(5f);
|
||||
|
||||
// when
|
||||
subject.addTrackPoint(tp1, GPS_DISTANCE);
|
||||
subject.addTrackPoint(tp2, GPS_DISTANCE);
|
||||
subject.addTrackPoint(tp3, GPS_DISTANCE);
|
||||
|
||||
// then
|
||||
assertEquals(1.10, subject.getTrackStatistics().getTotalDistance(), 0.01);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void addTrackPoint_distance_from_GPS_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, 5.0, Instant.ofEpochMilli(2000));
|
||||
tp2.setSpeed(5f);
|
||||
TrackPoint tp3 = new TrackPoint(0.001, 0, 5.0, Instant.ofEpochMilli(3000));
|
||||
tp2.setSpeed(5f);
|
||||
TrackPoint tp4 = new TrackPoint(0.001, 0, 5.0, Instant.ofEpochMilli(4000));
|
||||
tp2.setSpeed(5f);
|
||||
tp4.setSensorDistance(5f);
|
||||
TrackPoint tp5 = new TrackPoint(TrackPoint.Type.SEGMENT_END_MANUAL, Instant.ofEpochMilli(5000));
|
||||
tp5.setSensorDistance(10f);
|
||||
|
||||
// when
|
||||
subject.addTrackPoint(tp1, GPS_DISTANCE);
|
||||
subject.addTrackPoint(tp2, GPS_DISTANCE);
|
||||
subject.addTrackPoint(tp3, GPS_DISTANCE);
|
||||
|
||||
// then
|
||||
assertEquals(110.57, subject.getTrackStatistics().getTotalDistance(), 0.01);
|
||||
|
||||
// when
|
||||
subject.addTrackPoint(tp4, GPS_DISTANCE);
|
||||
subject.addTrackPoint(tp5, GPS_DISTANCE);
|
||||
|
||||
// then
|
||||
assertEquals(125.57, subject.getTrackStatistics().getTotalDistance(), 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, 5.0, Instant.ofEpochMilli(2000));
|
||||
TrackPoint tp3 = new TrackPoint(0.00001, 0, 5.0, Instant.ofEpochMilli(3000));
|
||||
TrackPoint tp4 = new TrackPoint(0.00001, 0, 5.0, Instant.ofEpochMilli(4000));
|
||||
tp4.setSensorDistance(5f);
|
||||
TrackPoint tp5 = new TrackPoint(TrackPoint.Type.SEGMENT_END_MANUAL, Instant.ofEpochMilli(5000));
|
||||
tp5.setSensorDistance(10f);
|
||||
|
||||
// when
|
||||
subject.addTrackPoint(tp1, GPS_DISTANCE);
|
||||
subject.addTrackPoint(tp2, GPS_DISTANCE);
|
||||
subject.addTrackPoint(tp3, GPS_DISTANCE);
|
||||
|
||||
// then
|
||||
assertEquals(0, subject.getTrackStatistics().getTotalDistance(), 0.01);
|
||||
|
||||
// when
|
||||
subject.addTrackPoint(tp4, GPS_DISTANCE);
|
||||
subject.addTrackPoint(tp5, GPS_DISTANCE);
|
||||
|
||||
// then
|
||||
assertEquals(15, subject.getTrackStatistics().getTotalDistance(), 0.01);
|
||||
}
|
||||
|
||||
@Ignore("TODO: create a concept ont to compute speed from GPS and sensor")
|
||||
@Test
|
||||
public void addTrackPoint_speed_from_GPS_not_moving() {
|
||||
}
|
||||
|
||||
@Ignore("TODO: create a concept ont to compute speed from GPS and sensor")
|
||||
@Test
|
||||
public void addTrackPoint_speed_from_GPS_moving() {
|
||||
}
|
||||
|
||||
@Ignore("TODO: create a concept ont to compute speed from GPS and sensor")
|
||||
@Test
|
||||
public void addTrackPoint_speed_from_GPS_not_moving_and_sensor_speed() {
|
||||
}
|
||||
|
||||
@Ignore("TODO: create a concept ont to compute speed from GPS and sensor")
|
||||
@Test
|
||||
public void addTrackPoint_speed_from_GPS_moving_and_sensor_speed() {
|
||||
}
|
||||
}
|
||||
@@ -11,7 +11,6 @@ import org.junit.runner.RunWith;
|
||||
import java.time.Duration;
|
||||
import java.util.List;
|
||||
|
||||
import de.dennisguse.opentracks.R;
|
||||
import de.dennisguse.opentracks.content.data.TestDataUtil;
|
||||
import de.dennisguse.opentracks.content.data.Track;
|
||||
import de.dennisguse.opentracks.content.data.TrackPoint;
|
||||
@@ -27,30 +26,26 @@ public class AnnouncementUtilsTest {
|
||||
|
||||
@Test
|
||||
public void getAnnouncement_metric() {
|
||||
PreferencesUtils.setString(context, R.string.stats_units_key, context.getString(R.string.stats_units_metric));
|
||||
|
||||
TrackStatistics stats = new TrackStatistics();
|
||||
stats.setTotalDistance(20000);
|
||||
stats.setTotalTime(Duration.ofMillis(600000));
|
||||
stats.setMovingTime(Duration.ofMillis(300000));
|
||||
stats.setTotalTime(Duration.ofHours(2).plusMinutes(5).plusSeconds(10));
|
||||
stats.setMovingTime(Duration.ofHours(1).plusMinutes(5).plusSeconds(10));
|
||||
stats.setMaxSpeed(100);
|
||||
stats.setTotalElevationGain(6000f);
|
||||
|
||||
// when
|
||||
String announcement = AnnouncementUtils.getAnnouncement(context, stats, "airplane", null);
|
||||
String announcement = AnnouncementUtils.getAnnouncement(context, stats, true, true, null);
|
||||
|
||||
// then
|
||||
assertEquals("OpenTracks total distance 20.00 kilometers in 5 minutes 0 seconds at 240.0 kilometers per hour", announcement);
|
||||
assertEquals("OpenTracks total distance 20.00 kilometers in 1 hour 5 minutes 10 seconds at 18.4 kilometers per hour", announcement);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void getAnnouncement_withInterval_metric() {
|
||||
PreferencesUtils.setString(context, R.string.stats_units_key, context.getString(R.string.stats_units_metric));
|
||||
|
||||
TrackStatistics stats = new TrackStatistics();
|
||||
stats.setTotalDistance(20000);
|
||||
stats.setTotalTime(Duration.ofMillis(600000));
|
||||
stats.setMovingTime(Duration.ofMillis(300000));
|
||||
stats.setTotalTime(Duration.ofHours(2).plusMinutes(5).plusSeconds(10));
|
||||
stats.setMovingTime(Duration.ofHours(1).plusMinutes(5).plusSeconds(10));
|
||||
stats.setMaxSpeed(100);
|
||||
stats.setTotalElevationGain(6000f);
|
||||
|
||||
@@ -58,12 +53,10 @@ public class AnnouncementUtilsTest {
|
||||
IntervalStatistics intervalStatistics = new IntervalStatistics(trackPoints, 1000);
|
||||
IntervalStatistics.Interval lastInterval = intervalStatistics.getIntervalList().get(intervalStatistics.getIntervalList().size() - 1);
|
||||
|
||||
String expected = "OpenTracks total distance 20.00 kilometers in 5 minutes 0 seconds at 240.0 kilometers per hour Lap speed of 51.2 kilometers per hour";
|
||||
|
||||
// when
|
||||
String announcement = AnnouncementUtils.getAnnouncement(context, stats, "airplane", lastInterval);
|
||||
String announcement = AnnouncementUtils.getAnnouncement(context, stats, true, true, lastInterval);
|
||||
|
||||
// then
|
||||
assertEquals(expected, announcement);
|
||||
assertEquals("OpenTracks total distance 20.00 kilometers in 1 hour 5 minutes 10 seconds at 18.4 kilometers per hour Lap speed of 51.2 kilometers per hour", announcement);
|
||||
}
|
||||
}
|
||||
@@ -47,7 +47,7 @@ public class BluetoothUtilsTest {
|
||||
|
||||
// then
|
||||
assertEquals(200, sensor.getCadence().getCrankRevolutionsCount());
|
||||
assertNull(sensor.getSpeed());
|
||||
assertNull(sensor.getDistanceSpeed());
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -60,7 +60,7 @@ public class BluetoothUtilsTest {
|
||||
|
||||
// then
|
||||
assertNull(sensor.getCadence());
|
||||
assertEquals(225, sensor.getSpeed().getWheelRevolutionsCount());
|
||||
assertEquals(225, sensor.getDistanceSpeed().getWheelRevolutionsCount());
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -73,7 +73,7 @@ public class BluetoothUtilsTest {
|
||||
|
||||
// then
|
||||
assertEquals(200, sensor.getCadence().getCrankRevolutionsCount());
|
||||
assertEquals(225, sensor.getSpeed().getWheelRevolutionsCount());
|
||||
assertEquals(225, sensor.getDistanceSpeed().getWheelRevolutionsCount());
|
||||
}
|
||||
|
||||
@Test
|
||||
|
||||
@@ -0,0 +1,60 @@
|
||||
package de.dennisguse.opentracks.util;
|
||||
|
||||
import junit.framework.TestCase;
|
||||
|
||||
import org.junit.Test;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.UUID;
|
||||
|
||||
public class ExportUtilsTest extends TestCase {
|
||||
|
||||
@Test
|
||||
public void testIsExportFileExists_exists() {
|
||||
// given
|
||||
UUID uuid = UUID.randomUUID();
|
||||
String format = "kmz";
|
||||
List<String> fileNames = new ArrayList<>();
|
||||
fileNames.add(uuid.toString().substring(0, 8) + "_name.gpx");
|
||||
fileNames.add(uuid.toString().substring(0, 8) + "_other name name.kmz");
|
||||
|
||||
// when
|
||||
boolean exists = ExportUtils.isExportFileExists(uuid, format, fileNames);
|
||||
|
||||
// then
|
||||
assertTrue(exists);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testIsExportFileExists_not_exists1() {
|
||||
// given
|
||||
UUID uuid = UUID.randomUUID();
|
||||
String format = "gpx";
|
||||
List<String> fileNames = new ArrayList<>();
|
||||
fileNames.add(UUID.randomUUID().toString().substring(0, 8) + "_name.gpx");
|
||||
fileNames.add(UUID.randomUUID().toString().substring(0, 8) + "_other name name.gpx");
|
||||
|
||||
// when
|
||||
boolean exists = ExportUtils.isExportFileExists(uuid, format, fileNames);
|
||||
|
||||
// then
|
||||
assertFalse(exists);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testIsExportFileExists_not_exists2() {
|
||||
// given
|
||||
UUID uuid = UUID.randomUUID();
|
||||
String format = "kmz";
|
||||
List<String> fileNames = new ArrayList<>();
|
||||
fileNames.add(uuid.toString().substring(0, 8) + "_name.gpx");
|
||||
fileNames.add(UUID.randomUUID().toString().substring(0, 8) + "_other name name.gpx");
|
||||
|
||||
// when
|
||||
boolean exists = ExportUtils.isExportFileExists(uuid, format, fileNames);
|
||||
|
||||
// then
|
||||
assertFalse(exists);
|
||||
}
|
||||
}
|
||||
@@ -22,12 +22,14 @@ public class PreferencesUtilsTest {
|
||||
@Test
|
||||
public void ExportTrackFileFormat_ok() {
|
||||
// given
|
||||
SharedPreferences.Editor editor = PreferencesUtils.getSharedPreferences(context).edit();
|
||||
SharedPreferences sharedPreferences = PreferencesUtils.getSharedPreferences(context);
|
||||
SharedPreferences.Editor editor = sharedPreferences.edit();
|
||||
|
||||
editor.putString(context.getString(R.string.export_trackfileformat_key), TrackFileFormat.KMZ_WITH_TRACKDETAIL.name());
|
||||
editor.commit();
|
||||
|
||||
// when
|
||||
TrackFileFormat trackFileFormat = PreferencesUtils.getExportTrackFileFormat(context);
|
||||
TrackFileFormat trackFileFormat = PreferencesUtils.getExportTrackFileFormat(sharedPreferences, context);
|
||||
|
||||
// then
|
||||
assertEquals(TrackFileFormat.KMZ_WITH_TRACKDETAIL, trackFileFormat);
|
||||
@@ -36,12 +38,13 @@ public class PreferencesUtilsTest {
|
||||
@Test
|
||||
public void ExportTrackFileFormat_invalid() {
|
||||
// given
|
||||
SharedPreferences.Editor editor = PreferencesUtils.getSharedPreferences(context).edit();
|
||||
SharedPreferences sharedPreferences = PreferencesUtils.getSharedPreferences(context);
|
||||
SharedPreferences.Editor editor = sharedPreferences.edit();
|
||||
editor.putString(context.getString(R.string.export_trackfileformat_key), "invalid");
|
||||
editor.commit();
|
||||
|
||||
// when
|
||||
TrackFileFormat trackFileFormat = PreferencesUtils.getExportTrackFileFormat(context);
|
||||
TrackFileFormat trackFileFormat = PreferencesUtils.getExportTrackFileFormat(sharedPreferences, context);
|
||||
|
||||
// then
|
||||
assertEquals(TrackFileFormat.KMZ_WITH_TRACKDETAIL_AND_SENSORDATA, trackFileFormat);
|
||||
@@ -50,12 +53,13 @@ public class PreferencesUtilsTest {
|
||||
@Test
|
||||
public void ExportTrackFileFormat_noValue() {
|
||||
// given
|
||||
SharedPreferences.Editor editor = PreferencesUtils.getSharedPreferences(context).edit();
|
||||
SharedPreferences sharedPreferences = PreferencesUtils.getSharedPreferences(context);
|
||||
SharedPreferences.Editor editor = sharedPreferences.edit();
|
||||
editor.clear();
|
||||
editor.commit();
|
||||
|
||||
// when
|
||||
TrackFileFormat trackFileFormat = PreferencesUtils.getExportTrackFileFormat(context);
|
||||
TrackFileFormat trackFileFormat = PreferencesUtils.getExportTrackFileFormat(sharedPreferences, context);
|
||||
|
||||
// then
|
||||
assertEquals(TrackFileFormat.KMZ_WITH_TRACKDETAIL_AND_SENSORDATA, trackFileFormat);
|
||||
|
||||
@@ -17,6 +17,7 @@
|
||||
package de.dennisguse.opentracks.util;
|
||||
|
||||
import android.content.Context;
|
||||
import android.content.SharedPreferences;
|
||||
|
||||
import androidx.test.core.app.ApplicationProvider;
|
||||
import androidx.test.ext.junit.runners.AndroidJUnit4;
|
||||
@@ -46,12 +47,14 @@ public class TrackNameUtilsTest {
|
||||
|
||||
private static final Context CONTEXT = ApplicationProvider.getApplicationContext();
|
||||
|
||||
private static final SharedPreferences SHARED_PREFERENCES = PreferencesUtils.getSharedPreferences(CONTEXT);
|
||||
|
||||
/**
|
||||
* Tests when the track_name_key is settings_recording_track_name_date_local_value.
|
||||
*/
|
||||
@Test
|
||||
public void testTrackName_date_local() {
|
||||
PreferencesUtils.setString(CONTEXT, R.string.track_name_key, CONTEXT.getString(R.string.settings_recording_track_name_date_local_value));
|
||||
PreferencesUtils.setString(SHARED_PREFERENCES, CONTEXT, R.string.track_name_key, CONTEXT.getString(R.string.settings_recording_track_name_date_local_value));
|
||||
assertEquals(StringUtils.formatDateTime(CONTEXT, START_TIME), TrackNameUtils.getTrackName(CONTEXT, TRACK_ID, START_TIME));
|
||||
}
|
||||
|
||||
@@ -60,7 +63,7 @@ public class TrackNameUtilsTest {
|
||||
*/
|
||||
@Test
|
||||
public void testTrackName_date_iso_8601() {
|
||||
PreferencesUtils.setString(CONTEXT, R.string.track_name_key, CONTEXT.getString(R.string.settings_recording_track_name_date_iso_8601_value));
|
||||
PreferencesUtils.setString(SHARED_PREFERENCES, CONTEXT, R.string.track_name_key, CONTEXT.getString(R.string.settings_recording_track_name_date_iso_8601_value));
|
||||
SimpleDateFormat simpleDateFormat = new SimpleDateFormat(TrackNameUtils.ISO_8601_FORMAT, Locale.US);
|
||||
assertEquals(simpleDateFormat.format(START_TIME.toEpochMilli()), TrackNameUtils.getTrackName(CONTEXT, TRACK_ID, START_TIME));
|
||||
}
|
||||
@@ -70,7 +73,7 @@ public class TrackNameUtilsTest {
|
||||
*/
|
||||
@Test
|
||||
public void testTrackName_number() {
|
||||
PreferencesUtils.setString(CONTEXT, R.string.track_name_key, CONTEXT.getString(R.string.settings_recording_track_name_number_value));
|
||||
PreferencesUtils.setString(SHARED_PREFERENCES, CONTEXT, R.string.track_name_key, CONTEXT.getString(R.string.settings_recording_track_name_number_value));
|
||||
assertEquals("Track " + TRACK_ID.getId(), TrackNameUtils.getTrackName(CONTEXT, TRACK_ID, START_TIME));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -73,11 +73,13 @@ limitations under the License.
|
||||
|
||||
<intent-filter>
|
||||
<action android:name="android.intent.action.SEND" />
|
||||
<action android:name="android.intent.action.SEND_MULTIPLE" />
|
||||
<category android:name="android.intent.category.DEFAULT" />
|
||||
<data android:mimeType="application/gpx+xml" />
|
||||
</intent-filter>
|
||||
<intent-filter>
|
||||
<action android:name="android.intent.action.SEND" />
|
||||
<action android:name="android.intent.action.SEND_MULTIPLE" />
|
||||
<category android:name="android.intent.category.DEFAULT" />
|
||||
<data android:mimeType="application/gpx" />
|
||||
</intent-filter>
|
||||
@@ -135,6 +137,7 @@ limitations under the License.
|
||||
|
||||
<intent-filter>
|
||||
<action android:name="android.intent.action.SEND" />
|
||||
<action android:name="android.intent.action.SEND_MULTIPLE" />
|
||||
<category android:name="android.intent.category.DEFAULT" />
|
||||
<data android:mimeType="application/vnd.google-earth.kmz" />
|
||||
</intent-filter>
|
||||
@@ -192,6 +195,8 @@ limitations under the License.
|
||||
|
||||
<intent-filter>
|
||||
<action android:name="android.intent.action.SEND" />
|
||||
<action android:name="android.intent.action.SEND_MULTIPLE" />
|
||||
|
||||
<category android:name="android.intent.category.DEFAULT" />
|
||||
<data android:mimeType="application/vnd.google-earth.kml+xml" />
|
||||
</intent-filter>
|
||||
|
||||
@@ -66,7 +66,7 @@ public abstract class AbstractListActivity extends AbstractActivity implements C
|
||||
public void onConfirmDeleteDone(Track.Id... trackIds) {
|
||||
boolean stopRecording = false;
|
||||
|
||||
Track.Id recordingTrackId = PreferencesUtils.getRecordingTrackId(this);
|
||||
Track.Id recordingTrackId = PreferencesUtils.getRecordingTrackId(PreferencesUtils.getSharedPreferences(this), this);
|
||||
for (Track.Id trackId : trackIds) {
|
||||
if (trackId.equals(recordingTrackId)) {
|
||||
stopRecording = true;
|
||||
|
||||
@@ -66,12 +66,12 @@ public class MarkerListActivity extends AbstractActivity implements DeleteMarker
|
||||
|
||||
private final OnSharedPreferenceChangeListener sharedPreferenceChangeListener = new OnSharedPreferenceChangeListener() {
|
||||
@Override
|
||||
public void onSharedPreferenceChanged(SharedPreferences preferences, String key) {
|
||||
public void onSharedPreferenceChanged(SharedPreferences sharedPreferences, String key) {
|
||||
if (PreferencesUtils.isKey(MarkerListActivity.this, R.string.recording_track_id_key, key)) {
|
||||
recordingTrackId = PreferencesUtils.getRecordingTrackId(MarkerListActivity.this);
|
||||
recordingTrackId = PreferencesUtils.getRecordingTrackId(sharedPreferences, MarkerListActivity.this);
|
||||
}
|
||||
if (PreferencesUtils.isKey(MarkerListActivity.this, R.string.recording_track_paused_key, key)) {
|
||||
recordingTrackPaused = PreferencesUtils.isRecordingTrackPaused(MarkerListActivity.this);
|
||||
recordingTrackPaused = PreferencesUtils.isRecordingTrackPaused(sharedPreferences, MarkerListActivity.this);
|
||||
}
|
||||
if (key != null) {
|
||||
runOnUiThread(MarkerListActivity.this::invalidateOptionsMenu);
|
||||
@@ -138,7 +138,7 @@ public class MarkerListActivity extends AbstractActivity implements DeleteMarker
|
||||
protected void onStart() {
|
||||
super.onStart();
|
||||
sharedPreferences.registerOnSharedPreferenceChangeListener(sharedPreferenceChangeListener);
|
||||
sharedPreferenceChangeListener.onSharedPreferenceChanged(null, null);
|
||||
sharedPreferenceChangeListener.onSharedPreferenceChanged(sharedPreferences, null);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
package de.dennisguse.opentracks;
|
||||
|
||||
import android.app.Application;
|
||||
import android.content.SharedPreferences;
|
||||
import android.os.StrictMode;
|
||||
import android.util.Log;
|
||||
|
||||
@@ -27,31 +28,24 @@ public class Startup extends Application {
|
||||
Log.i(TAG, BuildConfig.APPLICATION_ID + "; BuildType: " + BuildConfig.BUILD_TYPE + "; VersionName: " + BuildConfig.VERSION_NAME + "/" + BuildConfig.VERSION_NAME_FULL + " VersionCode: " + BuildConfig.VERSION_CODE);
|
||||
|
||||
//Set default values of preferences on first start.
|
||||
PreferencesUtils.resetPreferences(this, false);
|
||||
if (PreferencesUtils.getString(this, R.string.stats_units_key, "").equals("")) {
|
||||
SharedPreferences sharedPreferences = PreferencesUtils.resetPreferences(this, false);
|
||||
if (PreferencesUtils.getString(sharedPreferences, this, R.string.stats_units_key, "").equals("")) {
|
||||
String statsUnits = getString(Locale.US.equals(Locale.getDefault()) ? R.string.stats_units_imperial : R.string.stats_units_metric);
|
||||
PreferencesUtils.setString(this, R.string.stats_units_key, statsUnits);
|
||||
PreferencesUtils.setString(sharedPreferences, this, R.string.stats_units_key, statsUnits);
|
||||
}
|
||||
|
||||
ActivityUtils.applyNightMode(this);
|
||||
ActivityUtils.applyNightMode(sharedPreferences, this);
|
||||
|
||||
//TODO Workaround to reset recordingTrackId on app startup as the TrackRecordingService (likely) crashed.
|
||||
if (PreferencesUtils.isRecording(this)) {
|
||||
if (PreferencesUtils.isRecording(sharedPreferences, this)) {
|
||||
Log.e(TAG, "Reset recordingTrackId; likely the TrackRecordingService crashed.");
|
||||
PreferencesUtils.setLong(this, R.string.recording_track_id_key, PreferencesUtils.RECORDING_TRACK_ID_DEFAULT);
|
||||
PreferencesUtils.setLong(sharedPreferences, this, R.string.recording_track_id_key, PreferencesUtils.RECORDING_TRACK_ID_DEFAULT);
|
||||
}
|
||||
|
||||
//In debug builds: show thread and VM warnings.
|
||||
if (BuildConfig.DEBUG) {
|
||||
Log.d(TAG, "Enabling strict mode");
|
||||
StrictMode.setThreadPolicy(new StrictMode.ThreadPolicy.Builder()
|
||||
.detectAll()
|
||||
.penaltyLog()
|
||||
.build());
|
||||
StrictMode.setVmPolicy(new StrictMode.VmPolicy.Builder()
|
||||
.detectAll()
|
||||
.penaltyLog()
|
||||
.build());
|
||||
StrictMode.enableDefaults();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -17,6 +17,7 @@
|
||||
package de.dennisguse.opentracks;
|
||||
|
||||
import android.Manifest;
|
||||
import android.app.ActivityOptions;
|
||||
import android.app.SearchManager;
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
@@ -29,6 +30,7 @@ import android.location.LocationManager;
|
||||
import android.os.Bundle;
|
||||
import android.provider.Settings;
|
||||
import android.util.Log;
|
||||
import android.util.Pair;
|
||||
import android.view.KeyEvent;
|
||||
import android.view.Menu;
|
||||
import android.view.MenuItem;
|
||||
@@ -107,18 +109,18 @@ public class TrackListActivity extends AbstractListActivity implements ConfirmDe
|
||||
|
||||
private final OnSharedPreferenceChangeListener sharedPreferenceChangeListener = new OnSharedPreferenceChangeListener() {
|
||||
@Override
|
||||
public void onSharedPreferenceChanged(SharedPreferences preferences, String key) {
|
||||
public void onSharedPreferenceChanged(SharedPreferences sharedPreferences, String key) {
|
||||
if (PreferencesUtils.isKey(TrackListActivity.this, R.string.stats_units_key, key)) {
|
||||
metricUnits = PreferencesUtils.isMetricUnits(TrackListActivity.this);
|
||||
metricUnits = PreferencesUtils.isMetricUnits(sharedPreferences, TrackListActivity.this);
|
||||
}
|
||||
if (PreferencesUtils.isKey(TrackListActivity.this, R.string.recording_track_id_key, key)) {
|
||||
recordingTrackId = PreferencesUtils.getRecordingTrackId(TrackListActivity.this);
|
||||
recordingTrackId = PreferencesUtils.getRecordingTrackId(sharedPreferences, TrackListActivity.this);
|
||||
if (key != null && PreferencesUtils.isRecording(recordingTrackId)) {
|
||||
trackRecordingServiceConnection.startAndBind(TrackListActivity.this);
|
||||
}
|
||||
}
|
||||
if (PreferencesUtils.isKey(TrackListActivity.this, R.string.recording_track_paused_key, key)) {
|
||||
recordingTrackPaused = PreferencesUtils.isRecordingTrackPaused(TrackListActivity.this);
|
||||
recordingTrackPaused = PreferencesUtils.isRecordingTrackPaused(sharedPreferences, TrackListActivity.this);
|
||||
}
|
||||
if (key != null) {
|
||||
runOnUiThread(() -> {
|
||||
@@ -195,17 +197,20 @@ public class TrackListActivity extends AbstractListActivity implements ConfirmDe
|
||||
|
||||
viewBinding.trackList.setEmptyView(viewBinding.trackListEmptyView);
|
||||
viewBinding.trackList.setOnItemClickListener((parent, view, position, trackId) -> {
|
||||
Intent newIntent;
|
||||
if (trackId == recordingTrackId.getId()) {
|
||||
// Is recording -> open record activity.
|
||||
newIntent = IntentUtils.newIntent(TrackListActivity.this, TrackRecordingActivity.class)
|
||||
Intent newIntent = IntentUtils.newIntent(TrackListActivity.this, TrackRecordingActivity.class)
|
||||
.putExtra(TrackRecordedActivity.EXTRA_TRACK_ID, new Track.Id(trackId));
|
||||
startActivity(newIntent);
|
||||
} else {
|
||||
// Not recording -> open detail activity.
|
||||
newIntent = IntentUtils.newIntent(TrackListActivity.this, TrackRecordedActivity.class)
|
||||
Intent newIntent = IntentUtils.newIntent(TrackListActivity.this, TrackRecordedActivity.class)
|
||||
.putExtra(TrackRecordedActivity.EXTRA_TRACK_ID, new Track.Id(trackId));
|
||||
ActivityOptions activityOptions = ActivityOptions.makeSceneTransitionAnimation(
|
||||
this,
|
||||
new Pair<>(view.findViewById(R.id.list_item_icon), TrackRecordedActivity.VIEW_TRACK_ICON));
|
||||
startActivity(newIntent, activityOptions.toBundle());
|
||||
}
|
||||
startActivity(newIntent);
|
||||
});
|
||||
|
||||
resourceCursorAdapter = new ResourceCursorAdapter(this, R.layout.list_item, null, 0) {
|
||||
@@ -257,7 +262,7 @@ public class TrackListActivity extends AbstractListActivity implements ConfirmDe
|
||||
super.onStart();
|
||||
|
||||
sharedPreferences.registerOnSharedPreferenceChangeListener(sharedPreferenceChangeListener);
|
||||
sharedPreferenceChangeListener.onSharedPreferenceChanged(null, null);
|
||||
sharedPreferenceChangeListener.onSharedPreferenceChanged(sharedPreferences, null);
|
||||
trackRecordingServiceConnection.startConnection(this);
|
||||
}
|
||||
|
||||
|
||||
@@ -24,6 +24,7 @@ import android.view.MenuItem;
|
||||
import android.view.View;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.core.view.ViewCompat;
|
||||
import androidx.fragment.app.Fragment;
|
||||
import androidx.fragment.app.FragmentActivity;
|
||||
import androidx.viewpager2.adapter.FragmentStateAdapter;
|
||||
@@ -55,6 +56,8 @@ public class TrackRecordedActivity extends AbstractListActivity implements Confi
|
||||
|
||||
private static final String TAG = TrackRecordedActivity.class.getSimpleName();
|
||||
|
||||
public static final String VIEW_TRACK_ICON = "track_icon";
|
||||
|
||||
public static final String EXTRA_TRACK_ID = "track_id";
|
||||
public static final String EXTRA_MARKER_ID = "marker_id";
|
||||
|
||||
@@ -84,6 +87,8 @@ public class TrackRecordedActivity extends AbstractListActivity implements Confi
|
||||
if (savedInstanceState != null) {
|
||||
viewBinding.trackDetailActivityViewPager.setCurrentItem(savedInstanceState.getInt(CURRENT_TAB_TAG_KEY));
|
||||
}
|
||||
|
||||
postponeEnterTransition();
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -138,7 +143,7 @@ public class TrackRecordedActivity extends AbstractListActivity implements Confi
|
||||
@Override
|
||||
public boolean onPrepareOptionsMenu(Menu menu) {
|
||||
menu.findItem(R.id.track_detail_markers).setShowAsAction(MenuItem.SHOW_AS_ACTION_IF_ROOM);
|
||||
menu.findItem(R.id.track_detail_resume_track).setVisible(!PreferencesUtils.isRecording(this));
|
||||
menu.findItem(R.id.track_detail_resume_track).setVisible(!PreferencesUtils.isRecording(PreferencesUtils.getSharedPreferences(this), this));
|
||||
Track track = contentProviderUtils.getTrack(trackId);
|
||||
setTitle(track != null ? track.getName() : "");
|
||||
return super.onPrepareOptionsMenu(menu);
|
||||
@@ -275,4 +280,9 @@ public class TrackRecordedActivity extends AbstractListActivity implements Confi
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void startPostponedEnterTransitionWith(View viewIcon, View viewName) {
|
||||
ViewCompat.setTransitionName(viewIcon, TrackRecordedActivity.VIEW_TRACK_ICON);
|
||||
startPostponedEnterTransition();
|
||||
}
|
||||
}
|
||||
@@ -97,9 +97,9 @@ public class TrackRecordingActivity extends AbstractActivity implements ChooseAc
|
||||
|
||||
private final OnSharedPreferenceChangeListener sharedPreferenceChangeListener = new OnSharedPreferenceChangeListener() {
|
||||
@Override
|
||||
public void onSharedPreferenceChanged(SharedPreferences preferences, String key) {
|
||||
public void onSharedPreferenceChanged(SharedPreferences sharedPreferences, String key) {
|
||||
if (PreferencesUtils.isKey(TrackRecordingActivity.this, R.string.recording_track_paused_key, key)) {
|
||||
recordingTrackPaused = PreferencesUtils.isRecordingTrackPaused(TrackRecordingActivity.this);
|
||||
recordingTrackPaused = PreferencesUtils.isRecordingTrackPaused(sharedPreferences, TrackRecordingActivity.this);
|
||||
setLockscreenPolicy();
|
||||
setScreenOnPolicy();
|
||||
}
|
||||
@@ -170,7 +170,7 @@ public class TrackRecordingActivity extends AbstractActivity implements ChooseAc
|
||||
}
|
||||
|
||||
private void setLockscreenPolicy() {
|
||||
boolean showOnLockScreen = PreferencesUtils.shouldShowStatsOnLockscreen(TrackRecordingActivity.this);
|
||||
boolean showOnLockScreen = PreferencesUtils.shouldShowStatsOnLockscreen(sharedPreferences, TrackRecordingActivity.this);
|
||||
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O_MR1) {
|
||||
setShowWhenLocked(showOnLockScreen);
|
||||
@@ -182,7 +182,7 @@ public class TrackRecordingActivity extends AbstractActivity implements ChooseAc
|
||||
}
|
||||
|
||||
private void setScreenOnPolicy() {
|
||||
boolean keepScreenOn = PreferencesUtils.shouldKeepScreenOn(TrackRecordingActivity.this);
|
||||
boolean keepScreenOn = PreferencesUtils.shouldKeepScreenOn(sharedPreferences, TrackRecordingActivity.this);
|
||||
|
||||
if (keepScreenOn) {
|
||||
getWindow().addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON);
|
||||
@@ -192,7 +192,7 @@ public class TrackRecordingActivity extends AbstractActivity implements ChooseAc
|
||||
}
|
||||
|
||||
private void setFullscreenPolicy() {
|
||||
boolean keepScreenOn = PreferencesUtils.shouldUseFullscreen(TrackRecordingActivity.this);
|
||||
boolean keepScreenOn = PreferencesUtils.shouldUseFullscreen(sharedPreferences, TrackRecordingActivity.this);
|
||||
|
||||
if (keepScreenOn) {
|
||||
getWindow().addFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN);
|
||||
@@ -206,7 +206,7 @@ public class TrackRecordingActivity extends AbstractActivity implements ChooseAc
|
||||
super.onStart();
|
||||
|
||||
sharedPreferences.registerOnSharedPreferenceChangeListener(sharedPreferenceChangeListener);
|
||||
sharedPreferenceChangeListener.onSharedPreferenceChanged(null, null);
|
||||
sharedPreferenceChangeListener.onSharedPreferenceChanged(sharedPreferences, null);
|
||||
|
||||
trackRecordingServiceConnection.startConnection(this);
|
||||
trackDataHub.start();
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
package de.dennisguse.opentracks.adapters;
|
||||
|
||||
import android.content.Context;
|
||||
import android.content.SharedPreferences;
|
||||
import android.util.Pair;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.View;
|
||||
@@ -134,11 +135,13 @@ public class AggregatedStatisticsAdapter extends BaseAdapter {
|
||||
}
|
||||
}
|
||||
|
||||
//TODO Check preference handling.
|
||||
private void setCommonValues(AggregatedStatistics.AggregatedStatistic aggregatedStatistic) {
|
||||
String category = aggregatedStatistic.getCategory();
|
||||
|
||||
reportSpeed = PreferencesUtils.isReportSpeed(context, category);
|
||||
metricsUnits = PreferencesUtils.isMetricUnits(context);
|
||||
SharedPreferences sharedPreferences = PreferencesUtils.getSharedPreferences(context);
|
||||
reportSpeed = PreferencesUtils.isReportSpeed(sharedPreferences, context, category);
|
||||
metricsUnits = PreferencesUtils.isMetricUnits(sharedPreferences, context);
|
||||
|
||||
sportIcon.setImageResource(getIcon(aggregatedStatistic));
|
||||
typeLabel.setText(category);
|
||||
|
||||
@@ -25,11 +25,12 @@ public class IntervalStatisticsAdapter extends ArrayAdapter<IntervalStatistics.I
|
||||
|
||||
public IntervalStatisticsAdapter(Context context, List<IntervalStatistics.Interval> intervalList, String category, StackMode stackMode) {
|
||||
super(context, R.layout.interval_stats_list_item, intervalList);
|
||||
metricUnits = PreferencesUtils.isMetricUnits(context);
|
||||
metricUnits = PreferencesUtils.isMetricUnits(PreferencesUtils.getSharedPreferences(context), context);
|
||||
this.category = category;
|
||||
this.stackMode = stackMode;
|
||||
}
|
||||
|
||||
//TODO Check preference handling! Should not be accessed in getView()
|
||||
@NonNull
|
||||
@Override
|
||||
public View getView(int position, @Nullable View intervalView, @NonNull ViewGroup parent) {
|
||||
@@ -60,7 +61,7 @@ public class IntervalStatisticsAdapter extends ArrayAdapter<IntervalStatistics.I
|
||||
}
|
||||
viewHolder.distance.setText(StringUtils.formatDistance(getContext(), sumDistance_m, metricUnits));
|
||||
|
||||
if (PreferencesUtils.isReportSpeed(getContext(), category)) {
|
||||
if (PreferencesUtils.isReportSpeed(PreferencesUtils.getSharedPreferences(getContext()), getContext(), category)) {
|
||||
viewHolder.rate.setText(StringUtils.formatSpeed(getContext(), interval.getSpeed_ms(), metricUnits, true));
|
||||
} else {
|
||||
viewHolder.rate.setText(StringUtils.formatSpeed(getContext(), interval.getSpeed_ms(), metricUnits, false));
|
||||
|
||||
@@ -34,7 +34,7 @@ public class MarkerResourceCursorAdapter extends ResourceCursorAdapter implement
|
||||
private final Activity activity;
|
||||
|
||||
//TODO Should be Marker.Id
|
||||
private final ExecutorListViewService executorService = new ExecutorListViewService<Long>(LIST_PREFERRED_ITEM_HEIGHT_DEFAULT);
|
||||
private final ExecutorListViewService<Long> executorService = new ExecutorListViewService<Long>(LIST_PREFERRED_ITEM_HEIGHT_DEFAULT);
|
||||
|
||||
private boolean scroll = false;
|
||||
|
||||
|
||||
@@ -71,6 +71,8 @@ public class TrackDataHub implements SharedPreferences.OnSharedPreferenceChangeL
|
||||
private final ContentProviderUtils contentProviderUtils;
|
||||
private final int targetNumPoints;
|
||||
|
||||
private SharedPreferences sharedPreferences;
|
||||
|
||||
private boolean started;
|
||||
private HandlerThread handlerThread;
|
||||
private Handler handler;
|
||||
@@ -140,8 +142,10 @@ public class TrackDataHub implements SharedPreferences.OnSharedPreferenceChangeL
|
||||
contentResolver.registerContentObserver(TrackPointsColumns.CONTENT_URI_BY_ID, false, trackPointsTableObserver);
|
||||
|
||||
|
||||
PreferencesUtils.register(context, this);
|
||||
onSharedPreferenceChanged(null, null);
|
||||
sharedPreferences = PreferencesUtils.getSharedPreferences(context);
|
||||
sharedPreferences.registerOnSharedPreferenceChangeListener(this);
|
||||
onSharedPreferenceChanged(sharedPreferences, null);
|
||||
|
||||
handler.post(() -> {
|
||||
if (started) {
|
||||
loadDataForAll();
|
||||
@@ -155,7 +159,8 @@ public class TrackDataHub implements SharedPreferences.OnSharedPreferenceChangeL
|
||||
return;
|
||||
}
|
||||
|
||||
PreferencesUtils.unregister(context, this);
|
||||
sharedPreferences.unregisterOnSharedPreferenceChangeListener(this);
|
||||
sharedPreferences = null;
|
||||
|
||||
started = false;
|
||||
|
||||
@@ -224,10 +229,10 @@ public class TrackDataHub implements SharedPreferences.OnSharedPreferenceChangeL
|
||||
public void onSharedPreferenceChanged(SharedPreferences sharedPreferences, final String key) {
|
||||
handler.post(() -> {
|
||||
if (PreferencesUtils.isKey(context, R.string.recording_track_id_key, key)) {
|
||||
recordingTrackId = PreferencesUtils.getRecordingTrackId(context);
|
||||
recordingTrackId = PreferencesUtils.getRecordingTrackId(sharedPreferences, context);
|
||||
}
|
||||
if (PreferencesUtils.isKey(context, R.string.recording_track_paused_key, key)) {
|
||||
recordingTrackPaused = PreferencesUtils.isRecordingTrackPaused(context);
|
||||
recordingTrackPaused = PreferencesUtils.isRecordingTrackPaused(sharedPreferences, context);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@@ -109,6 +109,19 @@ public class Track {
|
||||
this.trackStatistics = trackStatistics;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object o) {
|
||||
if (this == o) return true;
|
||||
if (o == null || getClass() != o.getClass()) return false;
|
||||
Track track = (Track) o;
|
||||
return id.equals(track.id);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return Objects.hash(id);
|
||||
}
|
||||
|
||||
public static class Id implements Parcelable {
|
||||
|
||||
private final long id;
|
||||
|
||||
@@ -57,6 +57,7 @@ public class TrackPoint {
|
||||
private Double altitude_m;
|
||||
private Float speed_mps;
|
||||
private Float bearing;
|
||||
private Float sensorDistance_m;
|
||||
|
||||
public enum Type {
|
||||
SEGMENT_START_MANUAL(-2), //Start of a segment due to user interaction (start, resume)
|
||||
@@ -224,7 +225,7 @@ public class TrackPoint {
|
||||
return elevationGain != null;
|
||||
}
|
||||
|
||||
public Float getElevationGain() {
|
||||
public float getElevationGain() {
|
||||
return elevationGain;
|
||||
}
|
||||
|
||||
@@ -310,8 +311,12 @@ public class TrackPoint {
|
||||
this.accuracy = horizontalAccuracy;
|
||||
}
|
||||
|
||||
public float distanceTo(@NonNull TrackPoint dest) {
|
||||
return getLocation().distanceTo(dest.getLocation());
|
||||
public float distanceToPrevious(@NonNull TrackPoint previous) {
|
||||
if (hasSensorDistance()) {
|
||||
return getSensorDistance();
|
||||
}
|
||||
|
||||
return getLocation().distanceTo(previous.getLocation());
|
||||
}
|
||||
|
||||
public boolean fulfillsAccuracy(int poorAccuracy) {
|
||||
@@ -327,6 +332,18 @@ public class TrackPoint {
|
||||
}
|
||||
|
||||
// Sensor data
|
||||
public boolean hasSensorDistance() {
|
||||
return sensorDistance_m != null;
|
||||
}
|
||||
|
||||
public Float getSensorDistance() {
|
||||
return sensorDistance_m;
|
||||
}
|
||||
|
||||
public void setSensorDistance(Float distance_m) {
|
||||
this.sensorDistance_m = distance_m;
|
||||
}
|
||||
|
||||
public boolean hasSensorData() {
|
||||
return hasHeartRate() || hasCyclingCadence() || hasPower();
|
||||
}
|
||||
@@ -347,7 +364,7 @@ public class TrackPoint {
|
||||
return cyclingCadence_rpm != null;
|
||||
}
|
||||
|
||||
public Float getCyclingCadence_rpm() {
|
||||
public float getCyclingCadence_rpm() {
|
||||
return cyclingCadence_rpm;
|
||||
}
|
||||
|
||||
@@ -359,7 +376,7 @@ public class TrackPoint {
|
||||
return power != null;
|
||||
}
|
||||
|
||||
public Float getPower() {
|
||||
public float getPower() {
|
||||
return power;
|
||||
}
|
||||
|
||||
|
||||
@@ -50,6 +50,7 @@ public interface TrackPointsColumns extends BaseColumns {
|
||||
String BEARING = "bearing";
|
||||
String SENSOR_HEARTRATE = "sensor_heartrate";
|
||||
String SENSOR_CADENCE = "sensor_cadence";
|
||||
String SENSOR_DISTANCE = "sensor_distance"; //DISTANCE from previous TrackPoint
|
||||
String SENSOR_POWER = "sensor_power";
|
||||
String ELEVATION_GAIN = "elevation_gain";
|
||||
String ELEVATION_LOSS = "elevation_loss";
|
||||
@@ -77,6 +78,7 @@ public interface TrackPointsColumns extends BaseColumns {
|
||||
+ ELEVATION_GAIN + " FLOAT, "
|
||||
+ ELEVATION_LOSS + " FLOAT, "
|
||||
+ TYPE + " TEXT CHECK(type IN (-2, -1, 0, 1)), "
|
||||
+ SENSOR_DISTANCE + " FLOAT, "
|
||||
+ "FOREIGN KEY (" + TRACKID + ") REFERENCES " + TracksColumns.TABLE_NAME + "(" + TracksColumns._ID + ") ON UPDATE CASCADE ON DELETE CASCADE"
|
||||
+ ")";
|
||||
|
||||
|
||||
@@ -19,6 +19,7 @@ class CachedTrackPointsIndexes {
|
||||
final int bearingIndex;
|
||||
final int sensorHeartRateIndex;
|
||||
final int sensorCadenceIndex;
|
||||
final int sensorDistanceIndex;
|
||||
final int sensorPowerIndex;
|
||||
final int elevationGainIndex;
|
||||
final int elevationLossIndex;
|
||||
@@ -35,6 +36,7 @@ class CachedTrackPointsIndexes {
|
||||
bearingIndex = cursor.getColumnIndexOrThrow(TrackPointsColumns.BEARING);
|
||||
sensorHeartRateIndex = cursor.getColumnIndexOrThrow(TrackPointsColumns.SENSOR_HEARTRATE);
|
||||
sensorCadenceIndex = cursor.getColumnIndexOrThrow(TrackPointsColumns.SENSOR_CADENCE);
|
||||
sensorDistanceIndex = cursor.getColumnIndexOrThrow(TrackPointsColumns.SENSOR_DISTANCE);
|
||||
sensorPowerIndex = cursor.getColumnIndexOrThrow(TrackPointsColumns.SENSOR_POWER);
|
||||
elevationGainIndex = cursor.getColumnIndexOrThrow(TrackPointsColumns.ELEVATION_GAIN);
|
||||
elevationLossIndex = cursor.getColumnIndexOrThrow(TrackPointsColumns.ELEVATION_LOSS);
|
||||
|
||||
@@ -70,7 +70,6 @@ public class ContentProviderUtils {
|
||||
private static final String ID_SEPARATOR = ",";
|
||||
|
||||
private final ContentResolver contentResolver;
|
||||
private int defaultCursorBatchSize = 2000;
|
||||
|
||||
public ContentProviderUtils(Context context) {
|
||||
contentResolver = context.getContentResolver();
|
||||
@@ -176,7 +175,7 @@ public class ContentProviderUtils {
|
||||
|
||||
// Delete track last since it triggers a database vacuum call
|
||||
String whereClause = String.format(TracksColumns._ID + " IN (%s)", TextUtils.join(",", Collections.nCopies(trackIds.size(), "?")));
|
||||
contentResolver.delete(TracksColumns.CONTENT_URI, whereClause, trackIds.stream().map(id->Long.toString(id.getId())).toArray(String[]::new));
|
||||
contentResolver.delete(TracksColumns.CONTENT_URI, whereClause, trackIds.stream().map(id -> Long.toString(id.getId())).toArray(String[]::new));
|
||||
}
|
||||
|
||||
public void deleteTrack(Context context, @NonNull Track.Id trackId) {
|
||||
@@ -556,6 +555,9 @@ public class ContentProviderUtils {
|
||||
if (!cursor.isNull(indexes.sensorCadenceIndex)) {
|
||||
trackPoint.setCyclingCadence_rpm(cursor.getFloat(indexes.sensorCadenceIndex));
|
||||
}
|
||||
if (!cursor.isNull(indexes.sensorDistanceIndex)) {
|
||||
trackPoint.setSensorDistance(cursor.getFloat(indexes.sensorDistanceIndex));
|
||||
}
|
||||
if (!cursor.isNull(indexes.sensorPowerIndex)) {
|
||||
trackPoint.setPower(cursor.getFloat(indexes.sensorPowerIndex));
|
||||
}
|
||||
@@ -629,9 +631,9 @@ public class ContentProviderUtils {
|
||||
*
|
||||
* @param trackId the track id
|
||||
* @param startTrackPointId the starting trackPoint id. `null` to ignore
|
||||
* @param maxLocations maximum number of locations to return. `null` for no limit
|
||||
*/
|
||||
public Cursor getTrackPointCursor(Track.Id trackId, TrackPoint.Id startTrackPointId, Integer maxLocations) {
|
||||
@NonNull
|
||||
public Cursor getTrackPointCursor(@NonNull Track.Id trackId, TrackPoint.Id startTrackPointId) {
|
||||
String selection;
|
||||
String[] selectionArgs;
|
||||
if (startTrackPointId != null) {
|
||||
@@ -642,11 +644,7 @@ public class ContentProviderUtils {
|
||||
selectionArgs = new String[]{Long.toString(trackId.getId())};
|
||||
}
|
||||
|
||||
String sortOrder = TrackPointsColumns.DEFAULT_SORT_ORDER;
|
||||
if (maxLocations != null) {
|
||||
sortOrder += " LIMIT " + maxLocations;
|
||||
}
|
||||
return getTrackPointCursor(null, selection, selectionArgs, sortOrder);
|
||||
return getTrackPointCursor(null, selection, selectionArgs, TrackPointsColumns.DEFAULT_SORT_ORDER);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -708,6 +706,9 @@ public class ContentProviderUtils {
|
||||
if (trackPoint.hasCyclingCadence()) {
|
||||
values.put(TrackPointsColumns.SENSOR_CADENCE, trackPoint.getCyclingCadence_rpm());
|
||||
}
|
||||
if (trackPoint.hasSensorDistance()) {
|
||||
values.put(TrackPointsColumns.SENSOR_DISTANCE, trackPoint.getSensorDistance());
|
||||
}
|
||||
if (trackPoint.hasPower()) {
|
||||
values.put(TrackPointsColumns.SENSOR_POWER, trackPoint.getPower());
|
||||
}
|
||||
@@ -757,39 +758,6 @@ public class ContentProviderUtils {
|
||||
return contentResolver.query(TrackPointsColumns.CONTENT_URI_BY_ID, projection, selection, selectionArgs, sortOrder);
|
||||
}
|
||||
|
||||
@VisibleForTesting
|
||||
public List<TrackPoint> getTrackPoints(Track.Id trackId) {
|
||||
List<TrackPoint> trackPoints = null;
|
||||
|
||||
try (Cursor trackPointCursor = getTrackPointCursor(trackId, null, null)) {
|
||||
if (trackPointCursor != null) {
|
||||
trackPointCursor.moveToFirst();
|
||||
trackPoints = new ArrayList<>(trackPointCursor.getCount());
|
||||
for (int i = 0; i < trackPointCursor.getCount(); i++) {
|
||||
trackPoints.add(createTrackPoint(trackPointCursor));
|
||||
trackPointCursor.moveToNext();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return trackPoints;
|
||||
}
|
||||
|
||||
int getDefaultCursorBatchSize() {
|
||||
return defaultCursorBatchSize;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the default cursor batch size. For testing purpose.
|
||||
*
|
||||
* @param defaultCursorBatchSize the default cursor batch size
|
||||
*/
|
||||
@VisibleForTesting
|
||||
void setDefaultCursorBatchSize(int defaultCursorBatchSize) {
|
||||
this.defaultCursorBatchSize = defaultCursorBatchSize;
|
||||
}
|
||||
|
||||
|
||||
public static String formatIdListForUri(Track.Id... trackIds) {
|
||||
long[] ids = new long[trackIds.length];
|
||||
for (int i = 0; i < trackIds.length; i++) {
|
||||
|
||||
@@ -259,8 +259,7 @@ public class CustomContentProvider extends ContentProvider {
|
||||
break;
|
||||
case TRACKS_SENSOR_STATS:
|
||||
long trackId = ContentUris.parseId(url);
|
||||
Cursor cursor = db.rawQuery(SENSOR_STATS_QUERY, new String[]{String.valueOf(trackId), String.valueOf(trackId)});
|
||||
return cursor;
|
||||
return db.rawQuery(SENSOR_STATS_QUERY, new String[]{String.valueOf(trackId), String.valueOf(trackId)});
|
||||
case MARKERS:
|
||||
queryBuilder.setTables(MarkerColumns.TABLE_NAME);
|
||||
sortOrder = sort != null ? sort : MarkerColumns.DEFAULT_SORT_ORDER;
|
||||
|
||||
+35
-2
@@ -25,7 +25,7 @@ public class CustomSQLiteOpenHelper extends SQLiteOpenHelper {
|
||||
|
||||
private static final String TAG = CustomSQLiteOpenHelper.class.getSimpleName();
|
||||
|
||||
private static final int DATABASE_VERSION = 30;
|
||||
private static final int DATABASE_VERSION = 31;
|
||||
|
||||
@VisibleForTesting
|
||||
public static final String DATABASE_NAME = "database.db";
|
||||
@@ -82,6 +82,9 @@ public class CustomSQLiteOpenHelper extends SQLiteOpenHelper {
|
||||
case 30:
|
||||
upgradeFrom29to30(db);
|
||||
break;
|
||||
case 31:
|
||||
upgradeFrom30to31(db);
|
||||
break;
|
||||
|
||||
default:
|
||||
throw new RuntimeException("Not implemented: upgrade to " + toVersion);
|
||||
@@ -115,7 +118,9 @@ public class CustomSQLiteOpenHelper extends SQLiteOpenHelper {
|
||||
case 29:
|
||||
downgradeFrom30to29(db);
|
||||
break;
|
||||
|
||||
case 30:
|
||||
downgradeFrom31to30(db);
|
||||
break;
|
||||
default:
|
||||
throw new RuntimeException("Not implemented: downgrade to " + toVersion);
|
||||
}
|
||||
@@ -394,4 +399,32 @@ public class CustomSQLiteOpenHelper extends SQLiteOpenHelper {
|
||||
db.setTransactionSuccessful();
|
||||
db.endTransaction();
|
||||
}
|
||||
|
||||
/**
|
||||
* Add distance column to TrackPoint.
|
||||
*/
|
||||
private void upgradeFrom30to31(SQLiteDatabase db) {
|
||||
db.beginTransaction();
|
||||
|
||||
// TrackPoints
|
||||
db.execSQL("ALTER TABLE trackpoints ADD COLUMN sensor_distance FLOAT");
|
||||
|
||||
db.setTransactionSuccessful();
|
||||
db.endTransaction();
|
||||
}
|
||||
|
||||
private void downgradeFrom31to30(SQLiteDatabase db) {
|
||||
db.beginTransaction();
|
||||
|
||||
// TrackPoints; identical to upgradeFrom27to28()
|
||||
db.execSQL("ALTER TABLE trackpoints RENAME TO trackpoints_old");
|
||||
db.execSQL("CREATE TABLE trackpoints (_id INTEGER PRIMARY KEY AUTOINCREMENT, trackid INTEGER NOT NULL, longitude INTEGER, latitude INTEGER, time INTEGER, elevation FLOAT, accuracy FLOAT, speed FLOAT, bearing FLOAT, sensor_heartrate FLOAT, sensor_cadence FLOAT, sensor_power FLOAT, elevation_gain FLOAT, elevation_loss FLOAT, type TEXT CHECK(type IN (-2, -1, 0, 1)), FOREIGN KEY (trackid) REFERENCES tracks(_id) ON UPDATE CASCADE ON DELETE CASCADE)");
|
||||
db.execSQL("INSERT INTO trackpoints SELECT _id, trackid, longitude, latitude, time, elevation, accuracy, speed, bearing, sensor_heartrate, sensor_cadence, sensor_power, elevation_gain, elevation_gain, type FROM trackpoints_old");
|
||||
db.execSQL("DROP TABLE trackpoints_old");
|
||||
|
||||
db.execSQL("CREATE INDEX trackpoints_trackid_index ON trackpoints(trackid)");
|
||||
|
||||
db.setTransactionSuccessful();
|
||||
db.endTransaction();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,7 +1,9 @@
|
||||
package de.dennisguse.opentracks.content.provider;
|
||||
|
||||
import android.database.Cursor;
|
||||
import android.util.Log;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.annotation.VisibleForTesting;
|
||||
|
||||
import java.util.Iterator;
|
||||
import java.util.NoSuchElementException;
|
||||
@@ -10,9 +12,8 @@ import de.dennisguse.opentracks.content.data.Track;
|
||||
import de.dennisguse.opentracks.content.data.TrackPoint;
|
||||
|
||||
/**
|
||||
* A lightweight wrapper around the original {@link Cursor} with a method to clean up.
|
||||
* A lightweight wrapper around the original {@link Cursor}.
|
||||
*/
|
||||
//TODO Remove batching; that should be handled by the database/contentprovider (i.e., already in place as we use a cursor)!
|
||||
public class TrackPointIterator implements Iterator<TrackPoint>, AutoCloseable {
|
||||
|
||||
private static final String TAG = TrackPointIterator.class.getSimpleName();
|
||||
@@ -20,7 +21,6 @@ public class TrackPointIterator implements Iterator<TrackPoint>, AutoCloseable {
|
||||
private final ContentProviderUtils contentProviderUtils;
|
||||
private final Track.Id trackId;
|
||||
private final CachedTrackPointsIndexes indexes;
|
||||
private TrackPoint.Id lastTrackPointId = null;
|
||||
private Cursor cursor;
|
||||
|
||||
public TrackPointIterator(ContentProviderUtils contentProviderUtils, Track.Id trackId, TrackPoint.Id startTrackPointId) {
|
||||
@@ -28,28 +28,11 @@ public class TrackPointIterator implements Iterator<TrackPoint>, AutoCloseable {
|
||||
this.trackId = trackId;
|
||||
|
||||
cursor = getCursor(startTrackPointId);
|
||||
indexes = cursor != null ? new CachedTrackPointsIndexes(cursor)
|
||||
: null;
|
||||
indexes = new CachedTrackPointsIndexes(cursor);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the track point cursor.
|
||||
*
|
||||
* @param trackPointId the starting track point id
|
||||
*/
|
||||
private Cursor getCursor(TrackPoint.Id trackPointId) {
|
||||
return contentProviderUtils.getTrackPointCursor(trackId, trackPointId, contentProviderUtils.getDefaultCursorBatchSize());
|
||||
}
|
||||
|
||||
/**
|
||||
* Advances the cursor to the next batch. Returns true if successful.
|
||||
*/
|
||||
private boolean advanceCursorToNextBatch() {
|
||||
TrackPoint.Id trackPointId = lastTrackPointId == null ? null : new TrackPoint.Id(lastTrackPointId.getId() + 1);
|
||||
Log.d(TAG, "Advancing track point id: " + trackPointId);
|
||||
cursor.close();
|
||||
cursor = getCursor(trackPointId);
|
||||
return cursor != null;
|
||||
return contentProviderUtils.getTrackPointCursor(trackId, trackPointId);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -57,32 +40,23 @@ public class TrackPointIterator implements Iterator<TrackPoint>, AutoCloseable {
|
||||
if (cursor == null) {
|
||||
return false;
|
||||
}
|
||||
if (cursor.isAfterLast()) {
|
||||
return false;
|
||||
}
|
||||
if (cursor.isLast()) {
|
||||
if (cursor.getCount() != contentProviderUtils.getDefaultCursorBatchSize()) {
|
||||
return false;
|
||||
}
|
||||
return advanceCursorToNextBatch() && !cursor.isAfterLast();
|
||||
}
|
||||
return true;
|
||||
return !cursor.isLast() && !cursor.isAfterLast();
|
||||
}
|
||||
|
||||
@Override
|
||||
@NonNull
|
||||
public TrackPoint next() {
|
||||
if (cursor == null) {
|
||||
if (cursor == null || !cursor.moveToNext()) {
|
||||
throw new NoSuchElementException();
|
||||
}
|
||||
if (!cursor.moveToNext()) {
|
||||
if (!advanceCursorToNextBatch() || !cursor.moveToNext()) {
|
||||
throw new NoSuchElementException();
|
||||
}
|
||||
}
|
||||
lastTrackPointId = new TrackPoint.Id(cursor.getLong(indexes.idIndex));
|
||||
return ContentProviderUtils.fillTrackPoint(cursor, indexes);
|
||||
}
|
||||
|
||||
@VisibleForTesting
|
||||
public int getCount() {
|
||||
return cursor.getCount();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void close() {
|
||||
if (cursor != null) {
|
||||
|
||||
@@ -54,6 +54,12 @@ public abstract class SensorData<T> {
|
||||
return value;
|
||||
}
|
||||
|
||||
/**
|
||||
* Reset long term aggregated values (more than derived from previous SensorData). e.g. overall distance.
|
||||
*/
|
||||
public void reset() {
|
||||
}
|
||||
|
||||
/**
|
||||
* Is the data recent considering the current time.
|
||||
*/
|
||||
@@ -61,4 +67,10 @@ public abstract class SensorData<T> {
|
||||
return Instant.now()
|
||||
.isBefore(time.plus(BluetoothRemoteSensorManager.MAX_SENSOR_DATE_SET_AGE_MS));
|
||||
}
|
||||
|
||||
@NonNull
|
||||
@Override
|
||||
public String toString() {
|
||||
return "sensorAddress='" + sensorAddress;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -41,7 +41,7 @@ public final class SensorDataCycling {
|
||||
/**
|
||||
* Workaround for Wahoo CADENCE: provides speed instead of cadence
|
||||
*/
|
||||
public Cadence(@NonNull SensorDataCycling.Speed speed) {
|
||||
public Cadence(@NonNull DistanceSpeed speed) {
|
||||
this(speed.getSensorAddress(), speed.getSensorName(), speed.wheelRevolutionsCount, speed.wheelRevolutionsTime);
|
||||
}
|
||||
|
||||
@@ -74,7 +74,7 @@ public final class SensorDataCycling {
|
||||
@NonNull
|
||||
@Override
|
||||
public String toString() {
|
||||
return "cadence=" + value + " time=" + crankRevolutionsTime + " count=" + crankRevolutionsCount;
|
||||
return super.toString() + " cadence=" + value + " time=" + crankRevolutionsTime + " count=" + crankRevolutionsCount;
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -90,18 +90,18 @@ public final class SensorDataCycling {
|
||||
}
|
||||
}
|
||||
|
||||
public static class Speed extends SensorData<Float> {
|
||||
public static class DistanceSpeed extends SensorData<DistanceSpeed.Data> {
|
||||
|
||||
private final Integer wheelRevolutionsCount; // UINT16
|
||||
private final Integer wheelRevolutionsTime; // UINT16; 1/1024s
|
||||
|
||||
public Speed(String sensorAddress) {
|
||||
public DistanceSpeed(String sensorAddress) {
|
||||
super(sensorAddress);
|
||||
this.wheelRevolutionsCount = null;
|
||||
this.wheelRevolutionsTime = null;
|
||||
}
|
||||
|
||||
public Speed(String sensorAddress, String sensorName, int wheelRevolutionsCount, int wheelRevolutionsTime) {
|
||||
public DistanceSpeed(String sensorAddress, String sensorName, int wheelRevolutionsCount, int wheelRevolutionsTime) {
|
||||
super(sensorAddress, sensorName);
|
||||
this.wheelRevolutionsCount = wheelRevolutionsCount;
|
||||
this.wheelRevolutionsTime = wheelRevolutionsTime;
|
||||
@@ -119,7 +119,7 @@ public final class SensorDataCycling {
|
||||
return wheelRevolutionsTime;
|
||||
}
|
||||
|
||||
public void compute(Speed previous, int wheel_circumference_mm) {
|
||||
public void compute(DistanceSpeed previous, int wheel_circumference_mm) {
|
||||
if (hasData() && previous != null && previous.hasData()) {
|
||||
float timeDiff_ms = UintUtils.diff(wheelRevolutionsTime, previous.wheelRevolutionsTime, UintUtils.UINT16_MAX) / 1024f * UnitConversions.S_TO_MS;
|
||||
if (timeDiff_ms <= 0) {
|
||||
@@ -127,43 +127,79 @@ public final class SensorDataCycling {
|
||||
value = null;
|
||||
} else {
|
||||
long wheelDiff = UintUtils.diff(wheelRevolutionsCount, previous.wheelRevolutionsCount, UintUtils.UINT16_MAX);
|
||||
wheelDiff = Math.abs(wheelDiff); //HACK for Garmin Speed 2 as it some of those seem to count backwards
|
||||
|
||||
double timeDiff_s = timeDiff_ms * UnitConversions.MS_TO_S;
|
||||
value = (float) (wheelDiff * wheel_circumference_mm * UnitConversions.MM_TO_M / timeDiff_s);
|
||||
float distance_m = (float) (wheelDiff * wheel_circumference_mm * UnitConversions.MM_TO_M);
|
||||
float distance_overall_m = distance_m;
|
||||
if (previous.hasValue()) {
|
||||
distance_overall_m += previous.getValue().distance_overall_m;
|
||||
}
|
||||
float speed_mps = (float) (distance_m / timeDiff_s);
|
||||
value = new Data(distance_m, distance_overall_m, speed_mps);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void reset() {
|
||||
if (value != null) {
|
||||
value = new Data(value.distance_m, 0, value.speed_mps);
|
||||
}
|
||||
}
|
||||
|
||||
@NonNull
|
||||
@Override
|
||||
public String toString() {
|
||||
return "speed=" + value + " time=" + wheelRevolutionsTime + " count=" + wheelRevolutionsCount;
|
||||
return super.toString() + " data=" + value + " time=" + wheelRevolutionsTime + " count=" + wheelRevolutionsCount;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(@Nullable Object obj) {
|
||||
if (!(obj instanceof Speed)) return false;
|
||||
if (!(obj instanceof DistanceSpeed)) return false;
|
||||
|
||||
Speed comp = (Speed) obj;
|
||||
DistanceSpeed comp = (DistanceSpeed) obj;
|
||||
if (hasData() && comp.hasData() == hasData()) {
|
||||
return getWheelRevolutionsCount() == comp.getWheelRevolutionsCount() && getWheelRevolutionsTime() == comp.getWheelRevolutionsTime();
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
public static class Data {
|
||||
public final float distance_m;
|
||||
public final float distance_overall_m;
|
||||
public final float speed_mps;
|
||||
|
||||
private Data(float distance_m, float distance_overall_m, float speed_mps) {
|
||||
this.distance_m = distance_m;
|
||||
this.distance_overall_m = distance_overall_m;
|
||||
this.speed_mps = speed_mps;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "Data{" +
|
||||
"distance_m=" + distance_m +
|
||||
", distance_overall_m=" + distance_overall_m +
|
||||
", speed_mps=" + speed_mps +
|
||||
'}';
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static class CadenceAndSpeed extends SensorData<Pair<Cadence, Speed>> {
|
||||
public static class CadenceAndSpeed extends SensorData<Pair<Cadence, DistanceSpeed>> {
|
||||
|
||||
public CadenceAndSpeed(String sensorAddress, String sensorName, @NonNull Cadence cadence, @NonNull Speed speed) {
|
||||
public CadenceAndSpeed(String sensorAddress, String sensorName, @Nullable Cadence cadence, @Nullable DistanceSpeed distanceSpeed) {
|
||||
super(sensorAddress, sensorName);
|
||||
this.value = new Pair<>(cadence, speed);
|
||||
this.value = new Pair<>(cadence, distanceSpeed);
|
||||
}
|
||||
|
||||
public Cadence getCadence() {
|
||||
return this.value != null ? this.value.first : null;
|
||||
}
|
||||
|
||||
public Speed getSpeed() {
|
||||
public DistanceSpeed getDistanceSpeed() {
|
||||
return this.value != null ? this.value.second : null;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -16,6 +16,6 @@ public class SensorDataCyclingPower extends SensorData<Float> {
|
||||
@NonNull
|
||||
@Override
|
||||
public String toString() {
|
||||
return "power=" + value;
|
||||
return super.toString() + " power=" + value;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -16,6 +16,6 @@ public class SensorDataHeartRate extends SensorData<Float> {
|
||||
@NonNull
|
||||
@Override
|
||||
public String toString() {
|
||||
return "heart=" + value;
|
||||
return super.toString() + " heart=" + value;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -10,7 +10,7 @@ public final class SensorDataSet {
|
||||
|
||||
private SensorDataCycling.Cadence cyclingCadence;
|
||||
|
||||
private SensorDataCycling.Speed cyclingSpeed;
|
||||
private SensorDataCycling.DistanceSpeed cyclingDistanceSpeed;
|
||||
|
||||
private SensorDataCyclingPower cyclingPower;
|
||||
|
||||
@@ -25,8 +25,8 @@ public final class SensorDataSet {
|
||||
return cyclingCadence;
|
||||
}
|
||||
|
||||
public SensorDataCycling.Speed getCyclingSpeed() {
|
||||
return cyclingSpeed;
|
||||
public SensorDataCycling.DistanceSpeed getCyclingDistanceSpeed() {
|
||||
return cyclingDistanceSpeed;
|
||||
}
|
||||
|
||||
public SensorDataCyclingPower getCyclingPower() {
|
||||
@@ -44,7 +44,7 @@ public final class SensorDataSet {
|
||||
public void clear() {
|
||||
this.heartRate = null;
|
||||
this.cyclingCadence = null;
|
||||
this.cyclingSpeed = null;
|
||||
this.cyclingDistanceSpeed = null;
|
||||
this.cyclingPower = null;
|
||||
}
|
||||
|
||||
@@ -57,8 +57,9 @@ public final class SensorDataSet {
|
||||
trackPoint.setCyclingCadence_rpm(cyclingCadence.getValue());
|
||||
}
|
||||
|
||||
if (cyclingSpeed != null && cyclingSpeed.hasValue()) {
|
||||
trackPoint.setSpeed(cyclingSpeed.getValue());
|
||||
if (cyclingDistanceSpeed != null && cyclingDistanceSpeed.hasValue()) {
|
||||
trackPoint.setSensorDistance(cyclingDistanceSpeed.getValue().distance_overall_m);
|
||||
trackPoint.setSpeed(cyclingDistanceSpeed.getValue().speed_mps);
|
||||
}
|
||||
|
||||
if (cyclingPower != null && cyclingPower.hasValue()) {
|
||||
@@ -66,12 +67,19 @@ public final class SensorDataSet {
|
||||
}
|
||||
}
|
||||
|
||||
public void reset() {
|
||||
if (heartRate != null) heartRate.reset();
|
||||
if (cyclingCadence != null) cyclingCadence.reset();
|
||||
if (cyclingDistanceSpeed != null) cyclingDistanceSpeed.reset();
|
||||
if (cyclingPower != null) cyclingPower.reset();
|
||||
}
|
||||
|
||||
@NonNull
|
||||
@Override
|
||||
public String toString() {
|
||||
return (getHeartRate() != null ? "" + getHeartRate() : "")
|
||||
+ (getCyclingCadence() != null ? " " + getCyclingCadence() : "")
|
||||
+ (getCyclingSpeed() != null ? " " + getCyclingSpeed() : "")
|
||||
+ (getCyclingDistanceSpeed() != null ? " " + getCyclingDistanceSpeed() : "")
|
||||
+ (getCyclingPower() != null ? " " + getCyclingPower() : "");
|
||||
}
|
||||
|
||||
@@ -85,8 +93,8 @@ public final class SensorDataSet {
|
||||
this.cyclingCadence = (SensorDataCycling.Cadence) data;
|
||||
return;
|
||||
}
|
||||
if (type instanceof SensorDataCycling.Speed) {
|
||||
this.cyclingSpeed = (SensorDataCycling.Speed) data;
|
||||
if (type instanceof SensorDataCycling.DistanceSpeed) {
|
||||
this.cyclingDistanceSpeed = (SensorDataCycling.DistanceSpeed) data;
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
@@ -65,31 +65,28 @@ public class ChartFragment extends Fragment implements TrackDataListener {
|
||||
return chartFragment;
|
||||
}
|
||||
|
||||
private final List<ChartPoint> pendingPoints = new ArrayList<>();
|
||||
private SharedPreferences sharedPreferences;
|
||||
private int recordingDistanceInterval;
|
||||
|
||||
private TrackDataHub trackDataHub;
|
||||
|
||||
// Stats gathered from the received data
|
||||
private TrackStatisticsUpdater trackStatisticsUpdater = new TrackStatisticsUpdater();
|
||||
|
||||
private ChartBinding viewBinding;
|
||||
|
||||
//TODO REMOVE: Used to restore the TrackStatistics via TrackStatisticsUpdater; for the diagrams we should consider not this setting and just use all data that is in the database
|
||||
@Deprecated
|
||||
private int recordingDistanceInterval;
|
||||
private final List<ChartPoint> pendingPoints = new ArrayList<>();
|
||||
private String category = "";
|
||||
|
||||
// Modes of operation
|
||||
private boolean chartByDistance;
|
||||
|
||||
private ChartBinding viewBinding;
|
||||
// UI elements
|
||||
private ChartView chartView;
|
||||
|
||||
private final SharedPreferences.OnSharedPreferenceChangeListener sharedPreferenceChangeListener = new SharedPreferences.OnSharedPreferenceChangeListener() {
|
||||
@Override
|
||||
public void onSharedPreferenceChanged(SharedPreferences preferences, String key) {
|
||||
public void onSharedPreferenceChanged(SharedPreferences sharedPreferences, String key) {
|
||||
if (PreferencesUtils.isKey(getContext(), R.string.stats_units_key, key)) {
|
||||
boolean metricUnits = PreferencesUtils.isMetricUnits(getContext());
|
||||
boolean metricUnits = PreferencesUtils.isMetricUnits(sharedPreferences, getContext());
|
||||
if (metricUnits != chartView.getMetricUnits()) {
|
||||
chartView.setMetricUnits(metricUnits);
|
||||
runOnUiThread(() -> {
|
||||
@@ -100,7 +97,7 @@ public class ChartFragment extends Fragment implements TrackDataListener {
|
||||
}
|
||||
}
|
||||
if (PreferencesUtils.isKey(getContext(), R.string.stats_rate_key, key)) {
|
||||
boolean reportSpeed = PreferencesUtils.isReportSpeed(getContext(), category);
|
||||
boolean reportSpeed = PreferencesUtils.isReportSpeed(sharedPreferences, getContext(), category);
|
||||
if (reportSpeed != chartView.getReportSpeed()) {
|
||||
chartView.setReportSpeed(reportSpeed);
|
||||
chartView.applyReportSpeed();
|
||||
@@ -114,7 +111,7 @@ public class ChartFragment extends Fragment implements TrackDataListener {
|
||||
}
|
||||
|
||||
if (PreferencesUtils.isKey(getContext(), R.string.recording_distance_interval_key, key)) {
|
||||
recordingDistanceInterval = PreferencesUtils.getRecordingDistanceInterval(getContext());
|
||||
recordingDistanceInterval = PreferencesUtils.getRecordingDistanceInterval(sharedPreferences, getContext());
|
||||
}
|
||||
}
|
||||
};
|
||||
@@ -165,8 +162,9 @@ public class ChartFragment extends Fragment implements TrackDataListener {
|
||||
public void onResume() {
|
||||
super.onResume();
|
||||
resumeTrackDataHub();
|
||||
PreferencesUtils.register(getContext(), sharedPreferenceChangeListener);
|
||||
sharedPreferenceChangeListener.onSharedPreferenceChanged(null, null);
|
||||
sharedPreferences = PreferencesUtils.getSharedPreferences(getContext());
|
||||
sharedPreferences.registerOnSharedPreferenceChangeListener(sharedPreferenceChangeListener);
|
||||
sharedPreferenceChangeListener.onSharedPreferenceChanged(sharedPreferences, null);
|
||||
|
||||
checkChartSettings();
|
||||
getActivity().runOnUiThread(updateChart);
|
||||
@@ -176,7 +174,7 @@ public class ChartFragment extends Fragment implements TrackDataListener {
|
||||
public void onPause() {
|
||||
super.onPause();
|
||||
pauseTrackDataHub();
|
||||
PreferencesUtils.unregister(getContext(), sharedPreferenceChangeListener);
|
||||
sharedPreferences.unregisterOnSharedPreferenceChangeListener(sharedPreferenceChangeListener);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -192,6 +190,12 @@ public class ChartFragment extends Fragment implements TrackDataListener {
|
||||
viewBinding = null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onDestroy() {
|
||||
super.onDestroy();
|
||||
sharedPreferences = null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onTrackUpdated(Track track) {
|
||||
if (isResumed()) {
|
||||
@@ -201,7 +205,7 @@ public class ChartFragment extends Fragment implements TrackDataListener {
|
||||
}
|
||||
|
||||
category = track.getCategory();
|
||||
boolean reportSpeed = PreferencesUtils.isReportSpeed(getContext(), category);
|
||||
boolean reportSpeed = PreferencesUtils.isReportSpeed(PreferencesUtils.getSharedPreferences(getContext()), getContext(), category);
|
||||
if (reportSpeed != chartView.getReportSpeed()) {
|
||||
chartView.setReportSpeed(reportSpeed);
|
||||
chartView.applyReportSpeed();
|
||||
|
||||
@@ -44,14 +44,16 @@ public class IntervalsFragment extends Fragment implements TrackDataListener {
|
||||
private IntervalStatisticsAdapter adapter;
|
||||
private ArrayAdapter<IntervalStatisticsModel.IntervalOption> spinnerAdapter;
|
||||
|
||||
private SharedPreferences sharedPreferences;
|
||||
|
||||
private TrackDataHub trackDataHub;
|
||||
private String category;
|
||||
|
||||
private IntervalListViewBinding viewBinding;
|
||||
|
||||
protected final SharedPreferences.OnSharedPreferenceChangeListener sharedPreferenceChangeListener = (preferences, key) -> {
|
||||
protected final SharedPreferences.OnSharedPreferenceChangeListener sharedPreferenceChangeListener = (sharedPreferences, key) -> {
|
||||
if (PreferencesUtils.isKey(getContext(), R.string.stats_units_key, key) || PreferencesUtils.isKey(getContext(), R.string.stats_rate_key, key)) {
|
||||
metricUnits = PreferencesUtils.isMetricUnits(getContext());
|
||||
metricUnits = PreferencesUtils.isMetricUnits(sharedPreferences, getContext());
|
||||
if (adapter != null) {
|
||||
adapter.notifyDataSetChanged();
|
||||
spinnerAdapter.notifyDataSetChanged();
|
||||
@@ -73,8 +75,9 @@ public class IntervalsFragment extends Fragment implements TrackDataListener {
|
||||
public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) {
|
||||
super.onViewCreated(view, savedInstanceState);
|
||||
|
||||
PreferencesUtils.register(getContext(), sharedPreferenceChangeListener);
|
||||
metricUnits = PreferencesUtils.isMetricUnits(getContext());
|
||||
sharedPreferences = PreferencesUtils.getSharedPreferences(getContext());
|
||||
sharedPreferences.registerOnSharedPreferenceChangeListener(sharedPreferenceChangeListener);
|
||||
sharedPreferenceChangeListener.onSharedPreferenceChanged(sharedPreferences, null);
|
||||
|
||||
viewBinding.intervalList.setEmptyView(viewBinding.intervalListEmptyView);
|
||||
|
||||
@@ -125,6 +128,8 @@ public class IntervalsFragment extends Fragment implements TrackDataListener {
|
||||
public void onPause() {
|
||||
super.onPause();
|
||||
pauseTrackDataHub();
|
||||
|
||||
sharedPreferences.unregisterOnSharedPreferenceChangeListener(sharedPreferenceChangeListener);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -137,7 +142,7 @@ public class IntervalsFragment extends Fragment implements TrackDataListener {
|
||||
public void onDestroy() {
|
||||
super.onDestroy();
|
||||
|
||||
PreferencesUtils.unregister(getContext(), sharedPreferenceChangeListener);
|
||||
sharedPreferences = null;
|
||||
|
||||
adapter = null;
|
||||
viewModel = null;
|
||||
@@ -183,7 +188,7 @@ public class IntervalsFragment extends Fragment implements TrackDataListener {
|
||||
category = track != null ? track.getCategory() : "";
|
||||
|
||||
// Set rate label.
|
||||
boolean reportSpeed = PreferencesUtils.isReportSpeed(getContext(), category);
|
||||
boolean reportSpeed = PreferencesUtils.isReportSpeed(sharedPreferences, getContext(), category); //TODO Handle sharedPreferenceChangeListener
|
||||
viewBinding.intervalRate.setText(reportSpeed ? R.string.stats_speed : R.string.stats_pace);
|
||||
}
|
||||
});
|
||||
|
||||
@@ -34,6 +34,7 @@ import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import de.dennisguse.opentracks.R;
|
||||
import de.dennisguse.opentracks.TrackRecordedActivity;
|
||||
import de.dennisguse.opentracks.adapters.SensorsAdapter;
|
||||
import de.dennisguse.opentracks.content.data.Track;
|
||||
import de.dennisguse.opentracks.content.provider.ContentProviderUtils;
|
||||
@@ -64,7 +65,6 @@ public class StatisticsRecordedFragment extends Fragment {
|
||||
return fragment;
|
||||
}
|
||||
|
||||
|
||||
private TrackStatistics trackStatistics;
|
||||
private SensorStatistics sensorStatistics;
|
||||
private SensorsAdapter sensorsAdapter;
|
||||
@@ -152,26 +152,35 @@ public class StatisticsRecordedFragment extends Fragment {
|
||||
getActivity().runOnUiThread(() -> {
|
||||
if (isResumed()) {
|
||||
Track track = contentProviderUtils.getTrack(trackId);
|
||||
trackStatistics = track != null ? track.getTrackStatistics() : null;
|
||||
trackStatistics = track.getTrackStatistics();
|
||||
sensorStatistics = contentProviderUtils.getSensorStats(trackId);
|
||||
|
||||
String newCategory = track != null ? track.getCategory() : "";
|
||||
String newCategory = track.getCategory();
|
||||
if (!category.equals(newCategory)) {
|
||||
category = newCategory;
|
||||
sharedPreferenceChangeListener.onSharedPreferenceChanged(sharedPreferences, getString(R.string.stats_rate_key));
|
||||
}
|
||||
|
||||
loadTrackDescription(track);
|
||||
updateUI();
|
||||
updateSensorUI();
|
||||
|
||||
((TrackRecordedActivity) getActivity()).startPostponedEnterTransitionWith(viewBinding.statsActivityTypeIcon, viewBinding.statsNameValue);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
private void loadTrackDescription(@NonNull Track track) {
|
||||
viewBinding.statsNameValue.setText(track.getName());
|
||||
viewBinding.statsDescriptionValue.setText(track.getDescription());
|
||||
viewBinding.statsStartDatetimeValue.setText(StringUtils.formatDateTime(getContext(), trackStatistics.getStartTime()));
|
||||
}
|
||||
|
||||
private void updateUI() {
|
||||
// Set total distance
|
||||
{
|
||||
double totalDistance = trackStatistics == null ? Double.NaN : trackStatistics.getTotalDistance();
|
||||
double totalDistance = trackStatistics.getTotalDistance();
|
||||
Pair<String, String> parts = StringUtils.getDistanceParts(getContext(), totalDistance, preferenceMetricUnits);
|
||||
|
||||
viewBinding.statsDistanceValue.setText(parts.first);
|
||||
@@ -185,14 +194,14 @@ public class StatisticsRecordedFragment extends Fragment {
|
||||
}
|
||||
|
||||
// Set time and start datetime
|
||||
if (trackStatistics != null) {
|
||||
{
|
||||
viewBinding.statsMovingTimeValue.setText(StringUtils.formatElapsedTime(trackStatistics.getMovingTime()));
|
||||
viewBinding.statsTotalTimeValue.setText(StringUtils.formatElapsedTime(trackStatistics.getTotalTime()));
|
||||
}
|
||||
|
||||
// Set average speed/pace
|
||||
{
|
||||
double speed = trackStatistics != null ? trackStatistics.getAverageSpeed() : Double.NaN;
|
||||
double speed = trackStatistics.getAverageSpeed();
|
||||
viewBinding.statsAverageSpeedLabel.setText(preferenceReportSpeed ? R.string.stats_average_speed : R.string.stats_average_pace);
|
||||
|
||||
Pair<String, String> parts = StringUtils.getSpeedParts(getContext(), speed, preferenceMetricUnits, preferenceReportSpeed);
|
||||
@@ -202,7 +211,7 @@ public class StatisticsRecordedFragment extends Fragment {
|
||||
|
||||
// Set max speed/pace
|
||||
{
|
||||
double speed = trackStatistics == null ? Double.NaN : trackStatistics.getMaxSpeed();
|
||||
double speed = trackStatistics.getMaxSpeed();
|
||||
|
||||
viewBinding.statsMaxSpeedLabel.setText(preferenceReportSpeed ? R.string.stats_max_speed : R.string.stats_fastest_pace);
|
||||
|
||||
@@ -213,7 +222,7 @@ public class StatisticsRecordedFragment extends Fragment {
|
||||
|
||||
// Set moving speed/pace
|
||||
{
|
||||
double speed = trackStatistics != null ? trackStatistics.getAverageMovingSpeed() : Double.NaN;
|
||||
double speed = trackStatistics.getAverageMovingSpeed();
|
||||
|
||||
viewBinding.statsMovingSpeedLabel.setText(preferenceReportSpeed ? R.string.stats_average_moving_speed : R.string.stats_average_moving_pace);
|
||||
|
||||
@@ -225,11 +234,11 @@ public class StatisticsRecordedFragment extends Fragment {
|
||||
// Set elevation gain and loss
|
||||
{
|
||||
// Make elevation visible?
|
||||
boolean showElevation = PreferencesUtils.isShowStatsElevation(getContext());
|
||||
boolean showElevation = PreferencesUtils.isShowStatsElevation(sharedPreferences, getContext());
|
||||
viewBinding.statsElevationGroup.setVisibility(showElevation ? View.VISIBLE : View.GONE);
|
||||
|
||||
Float elevationGain_m = trackStatistics != null ? trackStatistics.getTotalElevationGain() : null;
|
||||
Float elevationLoss_m = trackStatistics != null ? trackStatistics.getTotalElevationLoss() : null;
|
||||
Float elevationGain_m = trackStatistics.getTotalElevationGain();
|
||||
Float elevationLoss_m = trackStatistics.getTotalElevationLoss();
|
||||
|
||||
Pair<String, String> parts;
|
||||
|
||||
|
||||
@@ -62,6 +62,7 @@ public class StatisticsRecordingFragment extends Fragment implements TrackDataLi
|
||||
private TrackRecordingServiceConnection trackRecordingServiceConnection = new TrackRecordingServiceConnection();
|
||||
private TrackPoint lastTrackPoint;
|
||||
|
||||
//TODO Should not be nullable
|
||||
private TrackStatistics lastTrackStatistics;
|
||||
|
||||
private String category = "";
|
||||
@@ -366,29 +367,26 @@ public class StatisticsRecordingFragment extends Fragment implements TrackDataLi
|
||||
}
|
||||
|
||||
private void setSpeedSensorData(SensorDataSet sensorDataSet) {
|
||||
if (sensorDataSet != null && sensorDataSet.getCyclingSpeed() != null) {
|
||||
SensorDataCycling.Speed data = sensorDataSet.getCyclingSpeed();
|
||||
if (sensorDataSet != null && sensorDataSet.getCyclingDistanceSpeed() != null) {
|
||||
SensorDataCycling.DistanceSpeed data = sensorDataSet.getCyclingDistanceSpeed();
|
||||
|
||||
if (data.hasValue() && data.isRecent()) {
|
||||
setSpeed(data.getValue());
|
||||
setTotalDistance(data.getValue().distance_overall_m);
|
||||
setSpeed(data.getValue().speed_mps);
|
||||
}
|
||||
if (data.hasValue() && data.isRecent()) {
|
||||
setSpeed(data.getValue().speed_mps);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void updateStats() {
|
||||
String trackIconValue = TrackIconUtils.getIconValue(getContext(), category);
|
||||
|
||||
// Set total distance
|
||||
{
|
||||
double totalDistance = lastTrackStatistics == null ? Double.NaN : lastTrackStatistics.getTotalDistance();
|
||||
Pair<String, String> parts = StringUtils.getDistanceParts(getContext(), totalDistance, preferenceMetricUnits);
|
||||
|
||||
viewBinding.statsDistanceValue.setText(parts.first);
|
||||
viewBinding.statsDistanceUnit.setText(parts.second);
|
||||
}
|
||||
setTotalDistance(0);
|
||||
|
||||
// Set activity type
|
||||
{
|
||||
String trackIconValue = TrackIconUtils.getIconValue(getContext(), category);
|
||||
viewBinding.statsActivityTypeIcon.setEnabled(isSelectedTrackRecording());
|
||||
viewBinding.statsActivityTypeIcon.setImageResource(TrackIconUtils.getIconDrawable(trackIconValue));
|
||||
}
|
||||
@@ -479,6 +477,14 @@ public class StatisticsRecordingFragment extends Fragment implements TrackDataLi
|
||||
}
|
||||
}
|
||||
|
||||
private void setTotalDistance(double sensorDistanceSinceLastTrackpoint) {
|
||||
double totalDistance = lastTrackStatistics != null ? (lastTrackStatistics.getTotalDistance() + sensorDistanceSinceLastTrackpoint) : Double.NaN;
|
||||
Pair<String, String> parts = StringUtils.getDistanceParts(getContext(), totalDistance, preferenceMetricUnits);
|
||||
|
||||
viewBinding.statsDistanceValue.setText(parts.first);
|
||||
viewBinding.statsDistanceUnit.setText(parts.second);
|
||||
}
|
||||
|
||||
private void setSpeed(double speed) {
|
||||
viewBinding.statsSpeedLabel.setText(preferenceReportSpeed ? R.string.stats_speed : R.string.stats_pace);
|
||||
|
||||
|
||||
@@ -91,7 +91,7 @@ public class ExportActivity extends FragmentActivity implements ExportServiceRes
|
||||
private ContentProviderUtils contentProviderUtils;
|
||||
|
||||
// List of tracks to be exported.
|
||||
private final ArrayList<Track.Id> trackIds = new ArrayList<>();
|
||||
private final ArrayList<Track> tracks = new ArrayList<>();
|
||||
|
||||
private final LinkedBlockingQueue<PendingConflict> conflictsQueue = new LinkedBlockingQueue<>();
|
||||
private final Handler conflictsHandler = new Handler();
|
||||
@@ -187,7 +187,7 @@ public class ExportActivity extends FragmentActivity implements ExportServiceRes
|
||||
protected void onDestroy() {
|
||||
super.onDestroy();
|
||||
conflictsQueue.clear();
|
||||
trackIds.clear();
|
||||
tracks.clear();
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -215,11 +215,11 @@ public class ExportActivity extends FragmentActivity implements ExportServiceRes
|
||||
for (int i = from; i < trackExportTotalCount; i++) {
|
||||
cursor.moveToPosition(i);
|
||||
Track track = ContentProviderUtils.createTrack(cursor);
|
||||
trackIds.add(track.getId());
|
||||
tracks.add(track);
|
||||
}
|
||||
|
||||
if (!trackIds.isEmpty()) {
|
||||
export(trackIds.get(0));
|
||||
if (!tracks.isEmpty()) {
|
||||
export(tracks.get(0));
|
||||
} else {
|
||||
onExportEnded();
|
||||
}
|
||||
@@ -227,26 +227,26 @@ public class ExportActivity extends FragmentActivity implements ExportServiceRes
|
||||
}
|
||||
|
||||
/**
|
||||
* Enqueue track identified by trackId to be exported if not exported already or there is a conflict resolution.
|
||||
* Enqueue track identified by UUID to be exported if not exported already or there is a conflict resolution.
|
||||
*
|
||||
* @param trackId the track's id.
|
||||
* @param track Track object.
|
||||
* @param conflictResolution conflict resolution to be applied if needed.
|
||||
*/
|
||||
private void export(Track.Id trackId, int conflictResolution) {
|
||||
boolean fileExists = ExportUtils.isExportFileExists(trackId, trackFileFormat.getExtension(), directoryFiles);
|
||||
private void export(Track track, int conflictResolution) {
|
||||
boolean fileExists = ExportUtils.isExportFileExists(track.getUuid(), trackFileFormat.getExtension(), directoryFiles);
|
||||
|
||||
if (fileExists && conflictResolution == CONFLICT_NONE) {
|
||||
conflict(trackId);
|
||||
conflict(track);
|
||||
} else if (fileExists && conflictResolution == CONFLICT_SKIP) {
|
||||
trackExportSkippedCount++;
|
||||
onExportCompleted(trackId);
|
||||
onExportCompleted(track);
|
||||
} else {
|
||||
ExportService.enqueue(this, resultReceiver, trackId, trackFileFormat, directoryUri);
|
||||
ExportService.enqueue(this, resultReceiver, track.getId(), trackFileFormat, directoryUri);
|
||||
}
|
||||
}
|
||||
|
||||
private void export(Track.Id trackId) {
|
||||
export(trackId, autoConflict);
|
||||
private void export(Track track) {
|
||||
export(track, autoConflict);
|
||||
}
|
||||
|
||||
private void setConflictVisibility(int visibility) {
|
||||
@@ -278,14 +278,15 @@ public class ExportActivity extends FragmentActivity implements ExportServiceRes
|
||||
viewBinding.exportProgressSummaryErrorsGroup.setVisibility(trackExportErrorCount > 0 ? View.VISIBLE : View.GONE);
|
||||
}
|
||||
|
||||
private void onExportCompleted(Track.Id trackId) {
|
||||
trackIds.remove(trackId);
|
||||
private void onExportCompleted(Track track) {
|
||||
tracks.remove(track);
|
||||
|
||||
setProgress();
|
||||
if (trackIds.isEmpty()) {
|
||||
if (tracks.isEmpty()) {
|
||||
onExportEnded();
|
||||
return;
|
||||
}
|
||||
export(trackIds.get(0));
|
||||
export(tracks.get(0));
|
||||
}
|
||||
|
||||
private void onExportEnded() {
|
||||
@@ -317,15 +318,15 @@ public class ExportActivity extends FragmentActivity implements ExportServiceRes
|
||||
}
|
||||
|
||||
Track.Id trackId = resultData.getParcelable(ExportServiceResultReceiver.RESULT_EXTRA_TRACK_ID);
|
||||
Track track = contentProviderUtils.getTrack(trackId);
|
||||
|
||||
switch (resultCode) {
|
||||
case ExportServiceResultReceiver.RESULT_CODE_ERROR:
|
||||
trackExportErrorCount++;
|
||||
Track track = contentProviderUtils.getTrack(trackId);
|
||||
trackErrors.add(track.getName());
|
||||
break;
|
||||
case ExportServiceResultReceiver.RESULT_CODE_SUCCESS:
|
||||
if (ExportUtils.isExportFileExists(trackId, trackFileFormat.getExtension(), directoryFiles)) {
|
||||
if (ExportUtils.isExportFileExists(track.getUuid(), trackFileFormat.getExtension(), directoryFiles)) {
|
||||
trackExportOverwrittenCount++;
|
||||
} else {
|
||||
trackExportSuccessCount++;
|
||||
@@ -335,11 +336,11 @@ public class ExportActivity extends FragmentActivity implements ExportServiceRes
|
||||
throw new RuntimeException(TAG + ": export service result code invalid: " + resultCode);
|
||||
}
|
||||
|
||||
onExportCompleted(trackId);
|
||||
onExportCompleted(track);
|
||||
}
|
||||
|
||||
private void conflict(Track.Id trackId) {
|
||||
PendingConflict newConflict = new PendingConflict(trackId);
|
||||
private void conflict(Track track) {
|
||||
PendingConflict newConflict = new PendingConflict(track);
|
||||
conflictsQueue.add(newConflict);
|
||||
|
||||
if (conflictsQueue.size() == 1) {
|
||||
@@ -351,10 +352,10 @@ public class ExportActivity extends FragmentActivity implements ExportServiceRes
|
||||
* Handle conflicts (exporting file already exists).
|
||||
*/
|
||||
private class PendingConflict {
|
||||
private final Track.Id trackId;
|
||||
private final Track track;
|
||||
|
||||
public PendingConflict(Track.Id trackId) {
|
||||
this.trackId = trackId;
|
||||
public PendingConflict(Track track) {
|
||||
this.track = track;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -365,14 +366,13 @@ public class ExportActivity extends FragmentActivity implements ExportServiceRes
|
||||
*/
|
||||
public boolean resolve() {
|
||||
if (autoConflict == CONFLICT_NONE) {
|
||||
Track track = contentProviderUtils.getTrack(trackId);
|
||||
viewBinding.exportProgressAlertIcon.setImageDrawable(ContextCompat.getDrawable(ExportActivity.this, R.drawable.ic_report_problem_24));
|
||||
viewBinding.exportProgressAlertMsg.setText(getString(R.string.export_track_already_exists_msg, track.getName()));
|
||||
setConflictVisibility(View.VISIBLE);
|
||||
return false;
|
||||
}
|
||||
|
||||
export(trackId);
|
||||
export(track);
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -380,7 +380,7 @@ public class ExportActivity extends FragmentActivity implements ExportServiceRes
|
||||
* Overwrite the export file and set the autoConflict if user set the "do it for all" switch button.
|
||||
*/
|
||||
public void overwrite() {
|
||||
export(trackId, CONFLICT_OVERWRITE);
|
||||
export(track, CONFLICT_OVERWRITE);
|
||||
|
||||
if (viewBinding.exportProgressApplyToAll.isChecked()) {
|
||||
autoConflict = CONFLICT_OVERWRITE;
|
||||
@@ -391,7 +391,7 @@ public class ExportActivity extends FragmentActivity implements ExportServiceRes
|
||||
* Skip the export file and set the autoConflict if user set the "do it for all" switch button.
|
||||
*/
|
||||
public void skip() {
|
||||
export(trackId, CONFLICT_SKIP);
|
||||
export(track, CONFLICT_SKIP);
|
||||
|
||||
if (viewBinding.exportProgressApplyToAll.isChecked()) {
|
||||
autoConflict = CONFLICT_SKIP;
|
||||
|
||||
@@ -49,8 +49,10 @@ public class GPXTrackExporter implements TrackExporter {
|
||||
private static final NumberFormat ELEVATION_FORMAT = NumberFormat.getInstance(Locale.US);
|
||||
private static final NumberFormat COORDINATE_FORMAT = NumberFormat.getInstance(Locale.US);
|
||||
private static final NumberFormat SPEED_FORMAT = NumberFormat.getInstance(Locale.US);
|
||||
private static final NumberFormat DISTANCE_FORMAT = NumberFormat.getInstance(Locale.US);
|
||||
private static final NumberFormat HEARTRATE_FORMAT = NumberFormat.getInstance(Locale.US);
|
||||
private static final NumberFormat CADENCE_FORMAT = NumberFormat.getInstance(Locale.US);
|
||||
private static final NumberFormat POWER_FORMAT = NumberFormat.getInstance(Locale.US);
|
||||
|
||||
static {
|
||||
/*
|
||||
@@ -72,6 +74,9 @@ public class GPXTrackExporter implements TrackExporter {
|
||||
|
||||
CADENCE_FORMAT.setMaximumFractionDigits(0);
|
||||
CADENCE_FORMAT.setGroupingUsed(false);
|
||||
|
||||
POWER_FORMAT.setMaximumFractionDigits(0);
|
||||
POWER_FORMAT.setGroupingUsed(false);
|
||||
}
|
||||
|
||||
private final ContentProviderUtils contentProviderUtils;
|
||||
@@ -91,15 +96,16 @@ public class GPXTrackExporter implements TrackExporter {
|
||||
|
||||
@Override
|
||||
public boolean writeTrack(Track[] tracks, @NonNull OutputStream outputStream) {
|
||||
Track mainTrack = tracks[0];
|
||||
try {
|
||||
prepare(outputStream);
|
||||
writeHeader(mainTrack);
|
||||
|
||||
writeMarkers(mainTrack);
|
||||
writeHeader();
|
||||
|
||||
for (Track track : tracks) {
|
||||
writeLocations(track);
|
||||
writeMarkers(track);
|
||||
}
|
||||
|
||||
for (Track track : tracks) {
|
||||
writeTrackPoints(track);
|
||||
}
|
||||
|
||||
writeFooter();
|
||||
@@ -112,7 +118,7 @@ public class GPXTrackExporter implements TrackExporter {
|
||||
}
|
||||
}
|
||||
|
||||
private void writeLocations(Track track) throws InterruptedException {
|
||||
private void writeTrackPoints(Track track) throws InterruptedException {
|
||||
boolean wroteTrack = false;
|
||||
boolean wroteSegment = false;
|
||||
|
||||
@@ -183,7 +189,7 @@ public class GPXTrackExporter implements TrackExporter {
|
||||
}
|
||||
|
||||
|
||||
public void writeHeader(Track track) {
|
||||
public void writeHeader() {
|
||||
if (printWriter != null) {
|
||||
printWriter.println("<?xml version=\"1.0\" encoding=\"UTF-8\"?>");
|
||||
printWriter.println("<gpx");
|
||||
@@ -195,17 +201,13 @@ public class GPXTrackExporter implements TrackExporter {
|
||||
printWriter.println("xmlns:atom=\"http://www.w3.org/2005/Atom\"");
|
||||
printWriter.println("xmlns:opentracks=\"http://opentracksapp.com/xmlschemas/v1\"");
|
||||
printWriter.println("xmlns:gpxtpx=\"http://www.garmin.com/xmlschemes/TrackPointExtension/v2\"");
|
||||
printWriter.println("xmlns:pwr=\"http://www.garmin.com/xmlschemas/PowerExtension/v1\"");
|
||||
printWriter.println("xsi:schemaLocation=" +
|
||||
"\"http://www.topografix.com/GPX/1/1 http://www.topografix.com/GPX/1/1/gpx.xsd"
|
||||
+ " http://www.topografix.com/GPX/Private/TopoGrafix/0/1 http://www.topografix.com/GPX/Private/TopoGrafix/0/1/topografix.xsd"
|
||||
+ " http://www.garmin.com/xmlschemas/TrackPointExtension/v2 https://www8.garmin.com/xmlschemas/TrackPointExtensionv2.xsd"
|
||||
+ " http://www.garmin.com/xmlschemas/PowerExtension/v1 https://www8.garmin.com/xmlschemas/PowerExtensionv1.xsd"
|
||||
+ " http://opentracksapp.com/xmlschemas/v1 http://opentracksapp.com/xmlschemas/OpenTracks_v1.xsd\">");
|
||||
|
||||
printWriter.println("<metadata>");
|
||||
|
||||
printWriter.println("<name>" + StringUtils.formatCData(track.getName()) + "</name>");
|
||||
printWriter.println("<desc>" + StringUtils.formatCData(track.getDescription()) + "</desc>");
|
||||
printWriter.println("</metadata>");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -299,6 +301,10 @@ public class GPXTrackExporter implements TrackExporter {
|
||||
printWriter.println("<gpxtpx:cad>" + CADENCE_FORMAT.format(trackPoint.getCyclingCadence_rpm()) + "</gpxtpx:cad>");
|
||||
}
|
||||
|
||||
if (trackPoint.hasPower()) {
|
||||
printWriter.println("<pwr:PowerInWatts>" + POWER_FORMAT.format(trackPoint.getPower()) + "</pwr:PowerInWatts>");
|
||||
}
|
||||
|
||||
if (trackPoint.hasElevationGain()) {
|
||||
printWriter.println("<opentracks:gain>" + ELEVATION_FORMAT.format(trackPoint.getElevationGain()) + "</opentracks:gain>");
|
||||
}
|
||||
@@ -307,6 +313,10 @@ public class GPXTrackExporter implements TrackExporter {
|
||||
printWriter.println("<opentracks:loss>" + ELEVATION_FORMAT.format(trackPoint.getElevationLoss()) + "</opentracks:loss>");
|
||||
}
|
||||
|
||||
if (trackPoint.hasSensorDistance()) {
|
||||
printWriter.println("<opentracks:distance>" + DISTANCE_FORMAT.format(trackPoint.getElevationLoss()) + "</opentracks:distance>");
|
||||
}
|
||||
|
||||
printWriter.println("</gpxtpx:TrackPointExtension></extensions>");
|
||||
}
|
||||
|
||||
|
||||
@@ -57,6 +57,7 @@ public class KMLTrackExporter implements TrackExporter {
|
||||
private static final String SCHEMA_ID = "schema";
|
||||
|
||||
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";
|
||||
public static final String EXTENDED_DATA_TYPE_HEART_RATE = "heart_rate";
|
||||
public static final String EXTENDED_DATA_TYPE_POWER = "power";
|
||||
@@ -74,6 +75,7 @@ public class KMLTrackExporter implements TrackExporter {
|
||||
|
||||
private PrintWriter printWriter;
|
||||
private final List<Float> speedList = new ArrayList<>();
|
||||
private final List<Float> distanceList = new ArrayList<>();
|
||||
private final List<Float> powerList = new ArrayList<>();
|
||||
private final List<Float> cadenceList = new ArrayList<>();
|
||||
private final List<Float> heartRateList = new ArrayList<>();
|
||||
@@ -338,6 +340,7 @@ public class KMLTrackExporter implements TrackExporter {
|
||||
if (printWriter != null) {
|
||||
printWriter.println("<gx:Track>");
|
||||
speedList.clear();
|
||||
distanceList.clear();
|
||||
powerList.clear();
|
||||
cadenceList.clear();
|
||||
heartRateList.clear();
|
||||
@@ -355,6 +358,9 @@ public class KMLTrackExporter implements TrackExporter {
|
||||
writeSimpleArrayData(speedList, EXTENDED_DATA_TYPE_SPEED);
|
||||
}
|
||||
if (exportSensorData) {
|
||||
if (distanceList.stream().anyMatch(Objects::nonNull)) {
|
||||
writeSimpleArrayData(distanceList, EXTENDED_DATA_TYPE_DISTANCE);
|
||||
}
|
||||
if (powerList.stream().anyMatch(Objects::nonNull)) {
|
||||
writeSimpleArrayData(powerList, EXTENDED_DATA_TYPE_POWER);
|
||||
}
|
||||
@@ -392,6 +398,7 @@ public class KMLTrackExporter implements TrackExporter {
|
||||
speedList.add(trackPoint.hasSpeed() ? trackPoint.getSpeed() : null);
|
||||
|
||||
if (exportSensorData) {
|
||||
distanceList.add(trackPoint.hasSensorDistance() ? trackPoint.getSensorDistance() : null);
|
||||
heartRateList.add(trackPoint.hasHeartRate() ? trackPoint.getHeartRate_bpm() : null);
|
||||
cadenceList.add(trackPoint.hasCyclingCadence() ? trackPoint.getCyclingCadence_rpm() : null);
|
||||
powerList.add(trackPoint.hasPower() ? trackPoint.getPower() : null);
|
||||
|
||||
+28
-10
@@ -17,6 +17,7 @@
|
||||
package de.dennisguse.opentracks.io.file.importer;
|
||||
|
||||
import android.content.Context;
|
||||
import android.content.SharedPreferences;
|
||||
import android.database.sqlite.SQLiteConstraintException;
|
||||
import android.net.Uri;
|
||||
import android.util.Log;
|
||||
@@ -90,6 +91,7 @@ abstract class AbstractFileTrackImporter extends DefaultHandler implements Track
|
||||
protected String uuid;
|
||||
protected String gain;
|
||||
protected String loss;
|
||||
protected String distance;
|
||||
|
||||
// The current track data
|
||||
private TrackData trackData;
|
||||
@@ -97,10 +99,13 @@ abstract class AbstractFileTrackImporter extends DefaultHandler implements Track
|
||||
// The SAX locator to get the current line information
|
||||
private Locator locator;
|
||||
|
||||
private final SharedPreferences sharedPreferences;
|
||||
|
||||
AbstractFileTrackImporter(Context context, ContentProviderUtils contentProviderUtils) {
|
||||
this.context = context;
|
||||
this.contentProviderUtils = contentProviderUtils;
|
||||
this.recordingDistanceInterval = PreferencesUtils.getRecordingDistanceInterval(context);
|
||||
sharedPreferences = PreferencesUtils.getSharedPreferences(context);
|
||||
this.recordingDistanceInterval = PreferencesUtils.getRecordingDistanceInterval(sharedPreferences, context);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -120,10 +125,11 @@ abstract class AbstractFileTrackImporter extends DefaultHandler implements Track
|
||||
}
|
||||
|
||||
@Override
|
||||
public Track.Id importFile(InputStream inputStream) {
|
||||
@NonNull
|
||||
public List<Track.Id> importFile(InputStream inputStream) {
|
||||
try {
|
||||
SAXParserFactory.newInstance().newSAXParser().parse(inputStream, this);
|
||||
return trackIds.get(0);
|
||||
return trackIds;
|
||||
} catch (IOException | SAXException | ParserConfigurationException | ParsingException e) {
|
||||
Log.e(TAG, "Unable to import file", e);
|
||||
if (trackIds.size() > 0) {
|
||||
@@ -136,9 +142,6 @@ abstract class AbstractFileTrackImporter extends DefaultHandler implements Track
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* On file end.
|
||||
*/
|
||||
protected void onFileEnd() {
|
||||
// Add markers to the last imported track
|
||||
int size = trackIds.size();
|
||||
@@ -261,7 +264,7 @@ abstract class AbstractFileTrackImporter extends DefaultHandler implements Track
|
||||
|
||||
Track track = contentProviderUtils.getTrack(trackData.track.getUuid());
|
||||
if (track != null) {
|
||||
if (PreferencesUtils.getPreventReimportTracks(context)) {
|
||||
if (PreferencesUtils.getPreventReimportTracks(sharedPreferences, context)) {
|
||||
throw new ImportAlreadyExistsException(context.getString(R.string.import_prevent_reimport));
|
||||
}
|
||||
|
||||
@@ -371,7 +374,7 @@ abstract class AbstractFileTrackImporter extends DefaultHandler implements Track
|
||||
* GPS points tend to have some inherent imprecision, speed and bearing will likely be off, so the statistics for things like max speed will also be off.
|
||||
*/
|
||||
if (trackPoint.hasLocation() && trackData.lastLocationInCurrentSegment.hasLocation()) {
|
||||
float speed = trackData.lastLocationInCurrentSegment.distanceTo(trackPoint) / timeDifference.toMillis();
|
||||
float speed = trackData.lastLocationInCurrentSegment.distanceToPrevious(trackPoint) / timeDifference.toMillis();
|
||||
trackPoint.setSpeed(speed);
|
||||
}
|
||||
}
|
||||
@@ -379,8 +382,8 @@ abstract class AbstractFileTrackImporter extends DefaultHandler implements Track
|
||||
if (trackPoint.hasLocation() && trackData.lastLocationInCurrentSegment.hasLocation()) {
|
||||
trackPoint.setBearing(trackData.lastLocationInCurrentSegment.bearingTo(trackPoint));
|
||||
|
||||
long maxRecordingDistance = PreferencesUtils.getMaxRecordingDistance(context);
|
||||
double distanceToLastTrackLocation = trackPoint.distanceTo(trackData.lastLocationInCurrentSegment);
|
||||
long maxRecordingDistance = PreferencesUtils.getMaxRecordingDistance(sharedPreferences, context); //TODO Should only be read once!
|
||||
double distanceToLastTrackLocation = trackPoint.distanceToPrevious(trackData.lastLocationInCurrentSegment);
|
||||
if (distanceToLastTrackLocation > maxRecordingDistance) {
|
||||
trackPoint.setType(TrackPoint.Type.SEGMENT_START_AUTOMATIC);
|
||||
}
|
||||
@@ -478,6 +481,14 @@ abstract class AbstractFileTrackImporter extends DefaultHandler implements Track
|
||||
}
|
||||
}
|
||||
|
||||
if (power != null) {
|
||||
try {
|
||||
trackPoint.setPower(Float.parseFloat(power));
|
||||
} catch (Exception e) {
|
||||
throw new ParsingException(createErrorMessage(String.format(Locale.US, "Unable to parse power: %s", power)), e);
|
||||
}
|
||||
}
|
||||
|
||||
if (gain != null) {
|
||||
try {
|
||||
trackPoint.setElevationGain(Float.parseFloat(gain));
|
||||
@@ -492,6 +503,13 @@ abstract class AbstractFileTrackImporter extends DefaultHandler implements Track
|
||||
throw new ParsingException(createErrorMessage(String.format(Locale.US, "Unable to parse elevation loss: %s", loss)), e);
|
||||
}
|
||||
}
|
||||
if (distance != null) {
|
||||
try {
|
||||
trackPoint.setSensorDistance(Float.parseFloat(distance));
|
||||
} catch (Exception e) {
|
||||
throw new ParsingException(createErrorMessage(String.format(Locale.US, "Unable to parse distance: %s", distance)), e);
|
||||
}
|
||||
}
|
||||
|
||||
return trackPoint;
|
||||
}
|
||||
|
||||
+17
-6
@@ -2,6 +2,7 @@ package de.dennisguse.opentracks.io.file.importer;
|
||||
|
||||
import android.app.Activity;
|
||||
import android.content.Intent;
|
||||
import android.content.SharedPreferences;
|
||||
import android.net.Uri;
|
||||
import android.os.Bundle;
|
||||
import android.provider.DocumentsContract;
|
||||
@@ -18,10 +19,14 @@ public abstract class DirectoryChooserActivity extends AppCompatActivity {
|
||||
|
||||
private static final int DIRECTORY_PICKER_REQUEST_CODE = 6;
|
||||
|
||||
protected SharedPreferences sharedPreferences;
|
||||
|
||||
@Override
|
||||
public void onCreate(Bundle savedInstanceState) {
|
||||
super.onCreate(savedInstanceState);
|
||||
|
||||
sharedPreferences = PreferencesUtils.getSharedPreferences(this);
|
||||
|
||||
Intent intent = new Intent(Intent.ACTION_OPEN_DOCUMENT_TREE);
|
||||
DocumentFile directoryUri = configureDirectoryChooserIntent(intent);
|
||||
if (directoryUri == null) {
|
||||
@@ -38,6 +43,12 @@ public abstract class DirectoryChooserActivity extends AppCompatActivity {
|
||||
onActivityResultCustom(requestCode, resultCode, resultData);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onDestroy() {
|
||||
super.onDestroy();
|
||||
sharedPreferences = null;
|
||||
}
|
||||
|
||||
protected void onActivityResultCustom(int requestCode, int resultCode, @Nullable Intent resultData) {
|
||||
if (requestCode == DIRECTORY_PICKER_REQUEST_CODE) {
|
||||
if (resultCode == Activity.RESULT_OK) {
|
||||
@@ -80,14 +91,14 @@ public abstract class DirectoryChooserActivity extends AppCompatActivity {
|
||||
super.configureDirectoryChooserIntent(intent);
|
||||
intent.addFlags(Intent.FLAG_GRANT_WRITE_URI_PERMISSION | Intent.FLAG_GRANT_PERSISTABLE_URI_PERMISSION);
|
||||
|
||||
return PreferencesUtils.getDefaultExportDirectoryUri(this);
|
||||
return PreferencesUtils.getDefaultExportDirectoryUri(sharedPreferences, this);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Intent createNextActivityIntent(Uri directoryUri) {
|
||||
Intent intent = IntentUtils.newIntent(this, ExportActivity.class);
|
||||
intent.putExtra(ExportActivity.EXTRA_DIRECTORY_URI_KEY, directoryUri);
|
||||
intent.putExtra(ExportActivity.EXTRA_TRACKFILEFORMAT_KEY, PreferencesUtils.getExportTrackFileFormat(this));
|
||||
intent.putExtra(ExportActivity.EXTRA_TRACKFILEFORMAT_KEY, PreferencesUtils.getExportTrackFileFormat(sharedPreferences, this));
|
||||
return intent;
|
||||
}
|
||||
}
|
||||
@@ -101,11 +112,11 @@ public abstract class DirectoryChooserActivity extends AppCompatActivity {
|
||||
case RESULT_OK:
|
||||
Uri directoryUri = resultData.getData();
|
||||
|
||||
PreferencesUtils.setDefaultExportDirectoryUri(this, directoryUri);
|
||||
PreferencesUtils.setDefaultExportDirectoryUri(sharedPreferences, this, directoryUri);
|
||||
IntentUtils.persistDirectoryAccessPermission(getApplicationContext(), resultData, directoryUri);
|
||||
break;
|
||||
case RESULT_CANCELED:
|
||||
PreferencesUtils.setDefaultExportDirectoryUri(this, null);
|
||||
PreferencesUtils.setDefaultExportDirectoryUri(sharedPreferences, this, null);
|
||||
//TODO Remove stored permission contentResolver.releasePersistableUriPermission
|
||||
break;
|
||||
}
|
||||
@@ -118,8 +129,8 @@ public abstract class DirectoryChooserActivity extends AppCompatActivity {
|
||||
protected DocumentFile configureDirectoryChooserIntent(Intent intent) {
|
||||
super.configureDirectoryChooserIntent(intent);
|
||||
intent.addFlags(Intent.FLAG_GRANT_WRITE_URI_PERMISSION);
|
||||
if (PreferencesUtils.isDefaultExportDirectoryUri(this)) {
|
||||
intent.putExtra(DocumentsContract.EXTRA_INITIAL_URI, PreferencesUtils.getDefaultExportDirectoryUri(this).getUri());
|
||||
if (PreferencesUtils.isDefaultExportDirectoryUri(sharedPreferences, this)) {
|
||||
intent.putExtra(DocumentsContract.EXTRA_INITIAL_URI, PreferencesUtils.getDefaultExportDirectoryUri(sharedPreferences, this).getUri());
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
@@ -28,7 +28,9 @@ import de.dennisguse.opentracks.content.provider.ContentProviderUtils;
|
||||
|
||||
/**
|
||||
* Imports a GPX file.
|
||||
* Uses https://www8.garmin.com/xmlschemas/TrackPointExtensionv2.xsd
|
||||
* Uses:
|
||||
* * https://www8.garmin.com/xmlschemas/TrackPointExtensionv2.xsd
|
||||
* * https://www8.garmin.com/xmlschemas/PowerExtensionv1.xsd
|
||||
* <p>
|
||||
* {@link de.dennisguse.opentracks.io.file.exporter.GPXTrackExporter} does not export information if a segment was started automatic or manually.
|
||||
* Therefore, all segments starts are marked as SEGMENT_START_AUTOMATIC.
|
||||
@@ -57,9 +59,11 @@ public class GpxFileTrackImporter extends AbstractFileTrackImporter {
|
||||
private static final String TAG_EXTENSION_SPEED = "gpxtpx:speed";
|
||||
private static final String TAG_EXTENSION_HEARTRATE = "gpxtpx:hr";
|
||||
private static final String TAG_EXTENSION_CADENCE = "gpxtpx:cad";
|
||||
private static final String TAG_EXTENSION_POWER = "pwr:PowerInWatts";
|
||||
|
||||
private static final String TAG_EXTENSION_GAIN = "opentracks:gain";
|
||||
private static final String TAG_EXTENSION_LOSS = "opentracks:loss";
|
||||
private static final String TAG_EXTENSION_DISTANCE = "opentracks:distance";
|
||||
|
||||
/**
|
||||
* Constructor.
|
||||
@@ -156,6 +160,11 @@ public class GpxFileTrackImporter extends AbstractFileTrackImporter {
|
||||
cadence = content.trim();
|
||||
}
|
||||
break;
|
||||
case TAG_EXTENSION_POWER:
|
||||
if (content != null) {
|
||||
power = content.trim();
|
||||
}
|
||||
break;
|
||||
case TAG_ID:
|
||||
if (content != null) {
|
||||
uuid = content.trim();
|
||||
@@ -171,6 +180,11 @@ public class GpxFileTrackImporter extends AbstractFileTrackImporter {
|
||||
loss = content.trim();
|
||||
}
|
||||
break;
|
||||
case TAG_EXTENSION_DISTANCE:
|
||||
if (content != null) {
|
||||
distance = content.trim();
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
// Reset element content
|
||||
@@ -196,11 +210,12 @@ public class GpxFileTrackImporter extends AbstractFileTrackImporter {
|
||||
altitude = null;
|
||||
time = null;
|
||||
speed = null;
|
||||
power = null;
|
||||
gain = null;
|
||||
loss = null;
|
||||
}
|
||||
|
||||
private void onTrackPointEnd() throws SAXException {
|
||||
private void onTrackPointEnd() {
|
||||
boolean isFirstTrackPointInSegment = isFirstTrackPointInSegment();
|
||||
TrackPoint trackPoint = getTrackPoint();
|
||||
if (isFirstTrackPointInSegment) {
|
||||
@@ -221,7 +236,7 @@ public class GpxFileTrackImporter extends AbstractFileTrackImporter {
|
||||
markerType = null;
|
||||
}
|
||||
|
||||
private void onMarkerEnd() throws SAXException {
|
||||
private void onMarkerEnd() {
|
||||
addMarker();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -16,6 +16,7 @@
|
||||
|
||||
package de.dennisguse.opentracks.io.file.importer;
|
||||
|
||||
import android.content.ClipData;
|
||||
import android.content.Intent;
|
||||
import android.net.Uri;
|
||||
import android.os.Bundle;
|
||||
@@ -30,6 +31,10 @@ import androidx.documentfile.provider.DocumentFile;
|
||||
import androidx.fragment.app.FragmentActivity;
|
||||
import androidx.lifecycle.ViewModelProvider;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import de.dennisguse.opentracks.R;
|
||||
import de.dennisguse.opentracks.TrackRecordedActivity;
|
||||
import de.dennisguse.opentracks.databinding.ImportActivityBinding;
|
||||
@@ -47,17 +52,15 @@ public class ImportActivity extends FragmentActivity {
|
||||
|
||||
public static final String EXTRA_DIRECTORY_URI_KEY = "directory_uri";
|
||||
|
||||
private static final String BUNDLE_TOOLBAR_TITLE = "toolbar_title";
|
||||
private static final String BUNDLE_DOCUMENT_URI = "document_uri";
|
||||
private static final String BUNDLE_DOCUMENT_URIS = "document_uris";
|
||||
private static final String BUNDLE_IS_DIRECTORY = "is_directory";
|
||||
|
||||
private ImportActivityBinding viewBinding;
|
||||
|
||||
boolean doubleBackToCancel = false;
|
||||
|
||||
private Uri documentUri;
|
||||
private ArrayList<Uri> documentUris = new ArrayList<>();
|
||||
private boolean isDirectory;
|
||||
private String toolbarTitle;
|
||||
|
||||
private ImportViewModel viewModel;
|
||||
private ImportViewModel.Summary summary;
|
||||
@@ -70,35 +73,46 @@ public class ImportActivity extends FragmentActivity {
|
||||
|
||||
Toolbar toolbar = findViewById(R.id.toolbar);
|
||||
|
||||
final DocumentFile documentFile;
|
||||
final Intent intent = getIntent();
|
||||
final ClipData intentClipData = intent.getClipData();
|
||||
|
||||
if (savedInstanceState == null) {
|
||||
if (getIntent().getData() != null) {
|
||||
documentUri = getIntent().getData();
|
||||
isDirectory = false;
|
||||
} else if (getIntent().getClipData() != null && getIntent().getClipData().getItemCount() > 0) {
|
||||
documentUri = getIntent().getClipData().getItemAt(0).getUri();
|
||||
if (intent.getData() != null) {
|
||||
documentUris.add(intent.getData());
|
||||
isDirectory = false;
|
||||
} else {
|
||||
// Started from DirectoryChooserActivity
|
||||
documentUri = getIntent().getParcelableExtra(EXTRA_DIRECTORY_URI_KEY);
|
||||
isDirectory = true;
|
||||
if (intentClipData != null && intentClipData.getItemCount() > 0) {
|
||||
for (int i = 0; i < intentClipData.getItemCount(); i++) {
|
||||
documentUris.add(intentClipData.getItemAt(i).getUri());
|
||||
}
|
||||
isDirectory = false;
|
||||
} else {
|
||||
// Started from DirectoryChooserActivity
|
||||
documentUris.add(intent.getParcelableExtra(EXTRA_DIRECTORY_URI_KEY));
|
||||
isDirectory = true;
|
||||
}
|
||||
}
|
||||
|
||||
documentFile = isDirectory ? DocumentFile.fromTreeUri(this, documentUri) : DocumentFile.fromSingleUri(this, documentUri);
|
||||
toolbarTitle = getString(R.string.import_progress_message, documentFile.getName());
|
||||
} else {
|
||||
documentUri = savedInstanceState.getParcelable(BUNDLE_DOCUMENT_URI);
|
||||
toolbarTitle = savedInstanceState.getString(BUNDLE_TOOLBAR_TITLE);
|
||||
documentUris = savedInstanceState.getParcelable(BUNDLE_DOCUMENT_URIS);
|
||||
isDirectory = savedInstanceState.getBoolean(BUNDLE_IS_DIRECTORY);
|
||||
|
||||
documentFile = isDirectory ? DocumentFile.fromTreeUri(this, documentUri) : DocumentFile.fromSingleUri(this, documentUri);
|
||||
}
|
||||
|
||||
toolbar.setTitle(toolbarTitle);
|
||||
final List<DocumentFile> documentFiles;
|
||||
if (isDirectory) {
|
||||
documentFiles = new ArrayList<>();
|
||||
documentFiles.add(DocumentFile.fromTreeUri(this, documentUris.get(0)));
|
||||
} else {
|
||||
documentFiles = documentUris.stream().map(it -> DocumentFile.fromSingleUri(this, it)).collect(Collectors.toList());
|
||||
}
|
||||
|
||||
//Works for a directory, but we might have received multiple files via SEND_MULTIPLE.
|
||||
toolbar.setTitle(getString(R.string.import_progress_message, documentFiles.get(0).getName()));
|
||||
|
||||
initViews();
|
||||
|
||||
viewModel = new ViewModelProvider(this).get(ImportViewModel.class);
|
||||
viewModel.getImportData(documentFile).observe(this, data -> {
|
||||
viewModel.getImportData(documentFiles).observe(this, data -> {
|
||||
summary = data;
|
||||
setProgress();
|
||||
});
|
||||
@@ -107,9 +121,8 @@ public class ImportActivity extends FragmentActivity {
|
||||
@Override
|
||||
protected void onSaveInstanceState(@NonNull Bundle outState) {
|
||||
super.onSaveInstanceState(outState);
|
||||
outState.putParcelable(BUNDLE_DOCUMENT_URI, documentUri);
|
||||
outState.putParcelableArrayList(BUNDLE_DOCUMENT_URIS, documentUris);
|
||||
outState.putBoolean(BUNDLE_IS_DIRECTORY, isDirectory);
|
||||
outState.putString(BUNDLE_TOOLBAR_TITLE, toolbarTitle);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -14,6 +14,7 @@ import androidx.documentfile.provider.DocumentFile;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.util.ArrayList;
|
||||
|
||||
import de.dennisguse.opentracks.R;
|
||||
import de.dennisguse.opentracks.content.data.Track;
|
||||
@@ -62,11 +63,12 @@ public class ImportService extends JobIntentService {
|
||||
}
|
||||
|
||||
try (InputStream inputStream = getContentResolver().openInputStream(file.getUri())) {
|
||||
Track.Id trackId = trackImporter.importFile(inputStream);
|
||||
if (trackId != null) {
|
||||
sendResult(ImportServiceResultReceiver.RESULT_CODE_IMPORTED, trackId, file, getString(R.string.import_file_imported, file.getName()));
|
||||
ArrayList<Track.Id> trackIds = new ArrayList<>(trackImporter.importFile(inputStream));
|
||||
|
||||
if (!trackIds.isEmpty()) {
|
||||
sendResult(ImportServiceResultReceiver.RESULT_CODE_IMPORTED, trackIds, file, getString(R.string.import_file_imported, file.getName()));
|
||||
} else {
|
||||
sendResult(ImportServiceResultReceiver.RESULT_CODE_ERROR, trackId, file, getString(R.string.import_unable_to_import_file, file.getName()));
|
||||
sendResult(ImportServiceResultReceiver.RESULT_CODE_ERROR, trackIds, file, getString(R.string.import_unable_to_import_file, file.getName()));
|
||||
}
|
||||
} catch (IOException e) {
|
||||
Log.d(TAG, "Unable to import file", e);
|
||||
@@ -80,9 +82,9 @@ public class ImportService extends JobIntentService {
|
||||
}
|
||||
}
|
||||
|
||||
private void sendResult(int resultCode, Track.Id trackId, DocumentFile file, String message) {
|
||||
private void sendResult(int resultCode, ArrayList<Track.Id> trackId, DocumentFile file, String message) {
|
||||
Bundle bundle = new Bundle();
|
||||
bundle.putParcelable(ImportServiceResultReceiver.RESULT_EXTRA_TRACK_ID, trackId);
|
||||
bundle.putParcelableArrayList(ImportServiceResultReceiver.RESULT_EXTRA_LIST_TRACK_ID, trackId);
|
||||
bundle.putString(ImportServiceResultReceiver.RESULT_EXTRA_FILENAME, file.getName());
|
||||
bundle.putString(ImportServiceResultReceiver.RESULT_EXTRA_MESSAGE, message);
|
||||
resultReceiver.send(resultCode, bundle);
|
||||
|
||||
+1
-1
@@ -16,7 +16,7 @@ public class ImportServiceResultReceiver extends ResultReceiver {
|
||||
public static final int RESULT_CODE_IMPORTED = 1;
|
||||
public static final int RESULT_CODE_ALREADY_EXISTS = 2;
|
||||
|
||||
public static final String RESULT_EXTRA_TRACK_ID = "result_track_id";
|
||||
public static final String RESULT_EXTRA_LIST_TRACK_ID = "result_track_id";
|
||||
public static final String RESULT_EXTRA_FILENAME = "result_extra_filename";
|
||||
public static final String RESULT_EXTRA_MESSAGE = "result_extra_message";
|
||||
|
||||
|
||||
@@ -11,7 +11,9 @@ import androidx.lifecycle.LiveData;
|
||||
import androidx.lifecycle.MutableLiveData;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import de.dennisguse.opentracks.R;
|
||||
import de.dennisguse.opentracks.content.data.Track;
|
||||
@@ -33,20 +35,20 @@ public class ImportViewModel extends AndroidViewModel implements ImportServiceRe
|
||||
summary = new Summary();
|
||||
}
|
||||
|
||||
public LiveData<Summary> getImportData(DocumentFile documentFile) {
|
||||
LiveData<Summary> getImportData(List<DocumentFile> documentFiles) {
|
||||
if (importData == null) {
|
||||
importData = new MutableLiveData<>();
|
||||
loadData(documentFile);
|
||||
loadData(documentFiles);
|
||||
}
|
||||
return importData;
|
||||
}
|
||||
|
||||
public void cancel() {
|
||||
void cancel() {
|
||||
cancel = true;
|
||||
}
|
||||
|
||||
private void loadData(DocumentFile documentFile) {
|
||||
List<DocumentFile> fileList = FileUtils.getFiles(documentFile);
|
||||
private void loadData(List<DocumentFile> documentFiles) {
|
||||
List<DocumentFile> fileList = documentFiles.stream().map(FileUtils::getFiles).flatMap(Collection::stream).collect(Collectors.toList());
|
||||
summary.totalCount = fileList.size();
|
||||
filesToImport.addAll(fileList);
|
||||
importNextFile();
|
||||
@@ -66,7 +68,7 @@ public class ImportViewModel extends AndroidViewModel implements ImportServiceRe
|
||||
throw new RuntimeException(TAG + ": onReceiveResult resultData NULL");
|
||||
}
|
||||
|
||||
Track.Id trackId = resultData.getParcelable(ImportServiceResultReceiver.RESULT_EXTRA_TRACK_ID);
|
||||
ArrayList<Track.Id> trackIds = resultData.getParcelableArrayList(ImportServiceResultReceiver.RESULT_EXTRA_LIST_TRACK_ID);
|
||||
String fileName = resultData.getString(ImportServiceResultReceiver.RESULT_EXTRA_FILENAME);
|
||||
String message = resultData.getString(ImportServiceResultReceiver.RESULT_EXTRA_MESSAGE);
|
||||
|
||||
@@ -76,7 +78,7 @@ public class ImportViewModel extends AndroidViewModel implements ImportServiceRe
|
||||
summary.fileErrors.add(getApplication().getString(R.string.import_error_info, fileName, message));
|
||||
break;
|
||||
case ImportServiceResultReceiver.RESULT_CODE_IMPORTED:
|
||||
summary.importedTrackIds.add(trackId);
|
||||
summary.importedTrackIds.addAll(trackIds);
|
||||
summary.successCount++;
|
||||
break;
|
||||
case ImportServiceResultReceiver.RESULT_CODE_ALREADY_EXISTS:
|
||||
@@ -90,7 +92,7 @@ public class ImportViewModel extends AndroidViewModel implements ImportServiceRe
|
||||
importNextFile();
|
||||
}
|
||||
|
||||
public static class Summary {
|
||||
static class Summary {
|
||||
private int totalCount;
|
||||
private int successCount;
|
||||
private int existsCount;
|
||||
|
||||
@@ -35,6 +35,7 @@ import de.dennisguse.opentracks.io.file.exporter.KMLTrackExporter;
|
||||
*
|
||||
* @author Jimmy Shih
|
||||
*/
|
||||
//TODO Do not use AbstractFileTrackImporter as TrackPoint sensor generation is done differently here.
|
||||
public class KmlFileTrackImporter extends AbstractFileTrackImporter {
|
||||
|
||||
private static final String TAG = KmlFileTrackImporter.class.getSimpleName();
|
||||
@@ -65,6 +66,7 @@ public class KmlFileTrackImporter extends AbstractFileTrackImporter {
|
||||
private String extendedDataType;
|
||||
private final ArrayList<TrackPoint> trackPoints = new ArrayList<>();
|
||||
private final ArrayList<Float> speedList = new ArrayList<>();
|
||||
private final ArrayList<Float> distanceList = new ArrayList<>();
|
||||
private final ArrayList<Float> cadenceList = new ArrayList<>();
|
||||
private final ArrayList<Float> heartRateList = new ArrayList<>();
|
||||
private final ArrayList<Float> powerList = new ArrayList<>();
|
||||
@@ -215,6 +217,7 @@ public class KmlFileTrackImporter extends AbstractFileTrackImporter {
|
||||
super.onTrackSegmentStart();
|
||||
trackPoints.clear();
|
||||
speedList.clear();
|
||||
distanceList.clear();
|
||||
heartRateList.clear();
|
||||
cadenceList.clear();
|
||||
powerList.clear();
|
||||
@@ -231,6 +234,9 @@ public class KmlFileTrackImporter extends AbstractFileTrackImporter {
|
||||
if (i < speedList.size()) {
|
||||
trackPoint.setSpeed(speedList.get(i));
|
||||
}
|
||||
if (i < distanceList.size()) {
|
||||
trackPoint.setSensorDistance(distanceList.get(i));
|
||||
}
|
||||
if (i < heartRateList.size()) {
|
||||
trackPoint.setHeartRate_bpm(heartRateList.get(i));
|
||||
}
|
||||
@@ -309,6 +315,9 @@ public class KmlFileTrackImporter extends AbstractFileTrackImporter {
|
||||
case KMLTrackExporter.EXTENDED_DATA_TYPE_SPEED:
|
||||
speedList.add(value);
|
||||
break;
|
||||
case KMLTrackExporter.EXTENDED_DATA_TYPE_DISTANCE:
|
||||
distanceList.add(value);
|
||||
break;
|
||||
case KMLTrackExporter.EXTENDED_DATA_TYPE_POWER:
|
||||
powerList.add(value);
|
||||
break;
|
||||
|
||||
@@ -20,6 +20,8 @@ import android.content.Context;
|
||||
import android.net.Uri;
|
||||
import android.util.Log;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
|
||||
import java.io.ByteArrayInputStream;
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.io.File;
|
||||
@@ -66,17 +68,23 @@ public class KmzTrackImporter implements TrackImporter {
|
||||
}
|
||||
|
||||
@Override
|
||||
public Track.Id importFile(InputStream inputStream) {
|
||||
Track.Id trackId = findAndParseKmlFile(inputStream);
|
||||
public @NonNull
|
||||
List<Track.Id> importFile(InputStream inputStream) {
|
||||
List<Track.Id> trackIds = findAndParseKmlFile(inputStream);
|
||||
|
||||
if (!copyKmzImages(trackId)) {
|
||||
cleanImport(context, trackId);
|
||||
return null;
|
||||
ArrayList<Track.Id> trackIdsWithImages = new ArrayList<>();
|
||||
|
||||
for (Track.Id trackId : trackIds) {
|
||||
if (copyKmzImages(trackId)) {
|
||||
trackIdsWithImages.add(trackId);
|
||||
deleteOrphanImages(context, trackId);
|
||||
} else {
|
||||
cleanImport(context, trackId);
|
||||
return new ArrayList<>();
|
||||
}
|
||||
}
|
||||
|
||||
deleteOrphanImages(context, trackId);
|
||||
|
||||
return trackId;
|
||||
return trackIdsWithImages;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -150,14 +158,13 @@ public class KmzTrackImporter implements TrackImporter {
|
||||
|
||||
/**
|
||||
* Finds KmzTrackExporter.KMZ_KML_FILE file inside kmz file (inputStream) and it parses it.
|
||||
* TODO: May load multiple tracks, but only returns the last Track.Id.
|
||||
*
|
||||
* @param inputStream kmz input stream.
|
||||
*/
|
||||
private Track.Id findAndParseKmlFile(InputStream inputStream) {
|
||||
private List<Track.Id> findAndParseKmlFile(InputStream inputStream) {
|
||||
try (ZipInputStream zipInputStream = new ZipInputStream(inputStream)) {
|
||||
ZipEntry zipEntry;
|
||||
Track.Id trackId = null;
|
||||
ArrayList<Track.Id> trackIds = new ArrayList<>();
|
||||
|
||||
while ((zipEntry = zipInputStream.getNextEntry()) != null) {
|
||||
if (Thread.interrupted()) {
|
||||
@@ -167,20 +174,21 @@ public class KmzTrackImporter implements TrackImporter {
|
||||
|
||||
String fileName = zipEntry.getName();
|
||||
if (KmzTrackExporter.KMZ_KML_FILE.equals(fileName)) {
|
||||
trackId = parseKml(zipInputStream);
|
||||
if (trackId == null) {
|
||||
List<Track.Id> trackId = parseKml(zipInputStream);
|
||||
if (trackId.isEmpty()) {
|
||||
Log.d(TAG, "Unable to parse kml in kmz");
|
||||
throw new ImportParserException(context.getString(R.string.import_unable_to_import_file, fileName));
|
||||
}
|
||||
trackIds.addAll(trackId);
|
||||
}
|
||||
|
||||
zipInputStream.closeEntry();
|
||||
}
|
||||
if (trackId == null) {
|
||||
if (trackIds.isEmpty()) {
|
||||
Log.d(TAG, "Unable to find doc.kml in kmz");
|
||||
throw new ImportParserException(context.getString(R.string.import_no_kml_file_found));
|
||||
}
|
||||
return trackId;
|
||||
return trackIds;
|
||||
} catch (ImportParserException | ImportAlreadyExistsException e) {
|
||||
Log.e(TAG, "Unable to import file", e);
|
||||
throw e;
|
||||
@@ -234,13 +242,7 @@ public class KmzTrackImporter implements TrackImporter {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Parses kml
|
||||
*
|
||||
* @param zipInputStream the zip input stream
|
||||
* @return the imported track id or -1L
|
||||
*/
|
||||
private Track.Id parseKml(ZipInputStream zipInputStream) {
|
||||
private List<Track.Id> parseKml(ZipInputStream zipInputStream) {
|
||||
KmlFileTrackImporter kmlFileTrackImporter = new KmlFileTrackImporter(context);
|
||||
|
||||
try (ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(getKml(zipInputStream))) {
|
||||
|
||||
@@ -15,7 +15,10 @@
|
||||
*/
|
||||
package de.dennisguse.opentracks.io.file.importer;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
|
||||
import java.io.InputStream;
|
||||
import java.util.List;
|
||||
|
||||
import de.dennisguse.opentracks.content.data.Track;
|
||||
|
||||
@@ -30,8 +33,7 @@ public interface TrackImporter {
|
||||
* Import a file.
|
||||
*
|
||||
* @param inputStream the file's input stream
|
||||
* @return the imported track id or null.
|
||||
*/
|
||||
//TODO Figure out how can make the import an atomic operation (incl. database transaction rollback).
|
||||
Track.Id importFile(InputStream inputStream);
|
||||
@NonNull
|
||||
List<Track.Id> importFile(InputStream inputStream) throws ImportParserException;
|
||||
}
|
||||
|
||||
@@ -82,39 +82,40 @@ public class TrackRecordingService extends Service implements HandlerServer.Hand
|
||||
private PeriodicTaskExecutor voiceExecutor;
|
||||
private TrackRecordingServiceNotificationManager notificationManager;
|
||||
|
||||
private SharedPreferences sharedPreferences;
|
||||
private Track.Id recordingTrackId;
|
||||
private boolean recordingTrackPaused;
|
||||
private int recordingDistanceInterval;
|
||||
private int maxRecordingDistance;
|
||||
|
||||
private final OnSharedPreferenceChangeListener sharedPreferenceChangeListener = new OnSharedPreferenceChangeListener() {
|
||||
@Override
|
||||
public void onSharedPreferenceChanged(SharedPreferences preferences, String key) {
|
||||
public void onSharedPreferenceChanged(SharedPreferences sharedPreferences, String key) {
|
||||
Context context = TrackRecordingService.this;
|
||||
if (PreferencesUtils.isKey(TrackRecordingService.this, R.string.recording_track_id_key, key)) {
|
||||
// Only through the TrackRecordingService can one stop a recording and set the recordingTrackId to -1L.
|
||||
if (PreferencesUtils.isRecording(TrackRecordingService.this)) {
|
||||
recordingTrackId = PreferencesUtils.getRecordingTrackId(TrackRecordingService.this);
|
||||
if (PreferencesUtils.isRecording(sharedPreferences, TrackRecordingService.this)) {
|
||||
recordingTrackId = PreferencesUtils.getRecordingTrackId(sharedPreferences, TrackRecordingService.this);
|
||||
}
|
||||
}
|
||||
if (PreferencesUtils.isKey(context, R.string.recording_track_paused_key, key)) {
|
||||
recordingTrackPaused = PreferencesUtils.isRecordingTrackPaused(context);
|
||||
recordingTrackPaused = PreferencesUtils.isRecordingTrackPaused(sharedPreferences, context);
|
||||
}
|
||||
if (PreferencesUtils.isKey(context, R.string.stats_units_key, key)) {
|
||||
boolean metricUnits = PreferencesUtils.isMetricUnits(context);
|
||||
boolean metricUnits = PreferencesUtils.isMetricUnits(sharedPreferences, context);
|
||||
voiceExecutor.setMetricUnits(metricUnits);
|
||||
notificationManager.setMetricUnits(metricUnits);
|
||||
}
|
||||
if (PreferencesUtils.isKey(context, R.string.voice_frequency_key, key)) {
|
||||
voiceExecutor.setTaskFrequency(PreferencesUtils.getVoiceFrequency(context));
|
||||
voiceExecutor.setTaskFrequency(PreferencesUtils.getVoiceFrequency(sharedPreferences, context));
|
||||
}
|
||||
if (PreferencesUtils.isKey(context, R.string.recording_distance_interval_key, key)) {
|
||||
recordingDistanceInterval = PreferencesUtils.getRecordingDistanceInterval(context);
|
||||
recordingDistanceInterval = PreferencesUtils.getRecordingDistanceInterval(sharedPreferences, context);
|
||||
}
|
||||
if (PreferencesUtils.isKey(context, R.string.max_recording_distance_key, key)) {
|
||||
maxRecordingDistance = PreferencesUtils.getMaxRecordingDistance(context);
|
||||
maxRecordingDistance = PreferencesUtils.getMaxRecordingDistance(sharedPreferences, context);
|
||||
}
|
||||
|
||||
handlerServer.onSharedPreferenceChanged(context, preferences, key);
|
||||
handlerServer.onSharedPreferenceChanged(context, sharedPreferences, key);
|
||||
}
|
||||
};
|
||||
|
||||
@@ -147,8 +148,9 @@ public class TrackRecordingService extends Service implements HandlerServer.Hand
|
||||
// onSharedPreferenceChanged might not set recordingTrackId.
|
||||
recordingTrackId = null;
|
||||
|
||||
PreferencesUtils.register(this, sharedPreferenceChangeListener);
|
||||
sharedPreferenceChangeListener.onSharedPreferenceChanged(null, null);
|
||||
sharedPreferences = PreferencesUtils.getSharedPreferences(this);
|
||||
sharedPreferences.registerOnSharedPreferenceChangeListener(sharedPreferenceChangeListener);
|
||||
sharedPreferenceChangeListener.onSharedPreferenceChanged(sharedPreferences, null);
|
||||
|
||||
restartTrackAfterServiceRestart();
|
||||
}
|
||||
@@ -189,8 +191,8 @@ public class TrackRecordingService extends Service implements HandlerServer.Hand
|
||||
// Reverse order from onCreate
|
||||
showNotification(false); //TODO Why?
|
||||
|
||||
PreferencesUtils.unregister(this, sharedPreferenceChangeListener);
|
||||
|
||||
sharedPreferences.unregisterOnSharedPreferenceChangeListener(sharedPreferenceChangeListener);
|
||||
sharedPreferences = null;
|
||||
|
||||
try {
|
||||
voiceExecutor.shutdown();
|
||||
@@ -305,7 +307,7 @@ public class TrackRecordingService extends Service implements HandlerServer.Hand
|
||||
//TODO Pass TrackPoint
|
||||
track.setName(TrackNameUtils.getTrackName(this, trackId, segmentStartTrackPoint.getTime()));
|
||||
|
||||
String category = PreferencesUtils.getDefaultActivity(this);
|
||||
String category = PreferencesUtils.getDefaultActivity(sharedPreferences, this);
|
||||
track.setCategory(category);
|
||||
track.setIcon(TrackIconUtils.getIconValue(this, category));
|
||||
track.setTrackStatistics(trackStatisticsUpdater.getTrackStatistics());
|
||||
@@ -536,9 +538,9 @@ public class TrackRecordingService extends Service implements HandlerServer.Hand
|
||||
private void updateRecordingState(Track.Id trackId, boolean paused) {
|
||||
recordingTrackId = trackId;
|
||||
long currentTrackId = trackId != null ? trackId.getId() : PreferencesUtils.RECORDING_TRACK_ID_DEFAULT;
|
||||
PreferencesUtils.setLong(this, R.string.recording_track_id_key, currentTrackId);
|
||||
PreferencesUtils.setLong(sharedPreferences, this, R.string.recording_track_id_key, currentTrackId);
|
||||
recordingTrackPaused = paused;
|
||||
PreferencesUtils.setBoolean(this, R.string.recording_track_paused_key, recordingTrackPaused);
|
||||
PreferencesUtils.setBoolean(sharedPreferences, this, R.string.recording_track_paused_key, recordingTrackPaused);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -556,7 +558,7 @@ public class TrackRecordingService extends Service implements HandlerServer.Hand
|
||||
|
||||
fillWithSensorDataSet(trackPoint);
|
||||
|
||||
notificationManager.updateTrackPoint(this, trackPoint, recordingGpsAccuracy);
|
||||
notificationManager.updateTrackPoint(this, track.getTrackStatistics(), trackPoint, recordingGpsAccuracy);
|
||||
|
||||
//TODO Figure out how to avoid loading the lastValidTrackPoint from the database
|
||||
TrackPoint lastValidTrackPoint = getLastValidTrackPointInCurrentSegment(track.getId());
|
||||
@@ -570,7 +572,7 @@ public class TrackRecordingService extends Service implements HandlerServer.Hand
|
||||
return;
|
||||
}
|
||||
|
||||
double distanceToLastTrackLocation = trackPoint.distanceTo(lastValidTrackPoint);
|
||||
double distanceToLastTrackLocation = trackPoint.distanceToPrevious(lastValidTrackPoint);
|
||||
if (distanceToLastTrackLocation > maxRecordingDistance) {
|
||||
insertTrackPointIfNewer(track, lastTrackPoint);
|
||||
|
||||
@@ -663,6 +665,10 @@ public class TrackRecordingService extends Service implements HandlerServer.Hand
|
||||
trackPoint.setElevationLoss(elevationSumManager.getElevationLoss_m());
|
||||
elevationSumManager.reset();
|
||||
}
|
||||
if (remoteSensorManager != null) {
|
||||
fillWithSensorDataSet(trackPoint);
|
||||
remoteSensorManager.reset();
|
||||
}
|
||||
contentProviderUtils.insertTrackPoint(trackPoint, track.getId());
|
||||
trackStatisticsUpdater.addTrackPoint(trackPoint, recordingDistanceInterval);
|
||||
|
||||
|
||||
+4
-2
@@ -20,6 +20,7 @@ import android.content.ComponentName;
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.content.ServiceConnection;
|
||||
import android.content.SharedPreferences;
|
||||
import android.os.IBinder;
|
||||
import android.os.IBinder.DeathRecipient;
|
||||
import android.os.RemoteException;
|
||||
@@ -209,9 +210,10 @@ public class TrackRecordingServiceConnection implements ServiceConnection, Death
|
||||
try {
|
||||
if (showEditor) {
|
||||
// Need to remember the recordingTrackId before calling endCurrentTrack() as endCurrentTrack() sets the value to -1L.
|
||||
Track.Id recordingTrackId = PreferencesUtils.getRecordingTrackId(context);
|
||||
SharedPreferences sharedPreferences = PreferencesUtils.getSharedPreferences(context);
|
||||
Track.Id recordingTrackId = PreferencesUtils.getRecordingTrackId(sharedPreferences, context);
|
||||
trackRecordingService.endCurrentTrack();
|
||||
if (PreferencesUtils.isRecording(context)) {
|
||||
if (PreferencesUtils.isRecording(sharedPreferences, context)) {
|
||||
Intent intent = IntentUtils.newIntent(context, TrackEditActivity.class)
|
||||
.putExtra(TrackEditActivity.EXTRA_TRACK_ID, recordingTrackId)
|
||||
.putExtra(TrackEditActivity.EXTRA_NEW_TRACK, true);
|
||||
|
||||
+13
-5
@@ -12,7 +12,7 @@ import androidx.core.app.NotificationCompat;
|
||||
|
||||
import de.dennisguse.opentracks.R;
|
||||
import de.dennisguse.opentracks.content.data.TrackPoint;
|
||||
import de.dennisguse.opentracks.util.PreferencesUtils;
|
||||
import de.dennisguse.opentracks.stats.TrackStatistics;
|
||||
import de.dennisguse.opentracks.util.StringUtils;
|
||||
|
||||
/**
|
||||
@@ -30,6 +30,8 @@ class TrackRecordingServiceNotificationManager {
|
||||
|
||||
private boolean previousLocationWasAccurate = true;
|
||||
|
||||
private Boolean metricUnits = null;
|
||||
|
||||
TrackRecordingServiceNotificationManager(Context context) {
|
||||
notificationManager = (NotificationManager) context.getSystemService(Context.NOTIFICATION_SERVICE);
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
|
||||
@@ -59,14 +61,14 @@ class TrackRecordingServiceNotificationManager {
|
||||
}
|
||||
|
||||
void updateContent(String content) {
|
||||
notificationBuilder.setContentText(content);
|
||||
notificationBuilder.setSubText(content);
|
||||
updateNotification();
|
||||
}
|
||||
|
||||
void updateTrackPoint(Context context, TrackPoint trackPoint, int recordingGpsAccuracy) {
|
||||
void updateTrackPoint(Context context, TrackStatistics trackStatistics, TrackPoint trackPoint, int recordingGpsAccuracy) {
|
||||
String formattedAccuracy = context.getString(R.string.value_none);
|
||||
if (trackPoint.hasAccuracy()) {
|
||||
formattedAccuracy = StringUtils.formatDistance(context, trackPoint.getAccuracy(), PreferencesUtils.isMetricUnits(context));
|
||||
formattedAccuracy = StringUtils.formatDistance(context, trackPoint.getAccuracy(), metricUnits);
|
||||
|
||||
boolean currentLocationWasAccurate = trackPoint.getAccuracy() < recordingGpsAccuracy;
|
||||
boolean shouldAlert = !currentLocationWasAccurate && previousLocationWasAccurate;
|
||||
@@ -74,7 +76,9 @@ class TrackRecordingServiceNotificationManager {
|
||||
previousLocationWasAccurate = currentLocationWasAccurate;
|
||||
}
|
||||
|
||||
notificationBuilder.setContentText(context.getString(R.string.track_recording_notification_accuracy, formattedAccuracy));
|
||||
notificationBuilder.setContentTitle(context.getString(R.string.track_distance_notification, StringUtils.formatDistance(context, trackStatistics.getTotalDistance(), metricUnits)));
|
||||
notificationBuilder.setContentText(context.getString(R.string.track_speed_notification, StringUtils.formatSpeed(context, trackPoint.getSpeed(), metricUnits, true)));
|
||||
notificationBuilder.setSubText(context.getString(R.string.track_recording_notification_accuracy, formattedAccuracy));
|
||||
updateNotification();
|
||||
|
||||
notificationBuilder.setOnlyAlertOnce(true);
|
||||
@@ -93,6 +97,10 @@ class TrackRecordingServiceNotificationManager {
|
||||
return notificationBuilder.build();
|
||||
}
|
||||
|
||||
void setMetricUnits(boolean metricUnits) {
|
||||
this.metricUnits = metricUnits;
|
||||
}
|
||||
|
||||
private void updateNotification() {
|
||||
notificationManager.notify(NOTIFICATION_ID, getNotification());
|
||||
}
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
package de.dennisguse.opentracks.services.handlers;
|
||||
|
||||
import android.content.Context;
|
||||
import android.content.SharedPreferences;
|
||||
import android.location.LocationManager;
|
||||
import android.os.Handler;
|
||||
|
||||
@@ -15,6 +16,7 @@ import de.dennisguse.opentracks.util.PreferencesUtils;
|
||||
/**
|
||||
* This class handle GPS status according to received locations and some thresholds.
|
||||
*/
|
||||
//TODO should handle sharedpreference changes
|
||||
class GpsStatus {
|
||||
|
||||
private static final String TAG = GpsStatus.class.getSimpleName();
|
||||
@@ -60,16 +62,16 @@ class GpsStatus {
|
||||
private final Handler gpsStatusHandler;
|
||||
private GpsStatusRunner gpsStatusRunner = null;
|
||||
|
||||
/**
|
||||
* @param context The context object.
|
||||
* @param client The client.
|
||||
* @param minRecordingInterval Value of min recording interval preference.
|
||||
*/
|
||||
public GpsStatus(Context context, GpsStatusListener client, Duration minRecordingInterval) {
|
||||
public GpsStatus(Context context, GpsStatusListener client) {
|
||||
this.client = client;
|
||||
this.context = context;
|
||||
signalBadThreshold = PreferencesUtils.getRecordingDistanceInterval(context);
|
||||
|
||||
SharedPreferences sharedPreferences = PreferencesUtils.getSharedPreferences(context);
|
||||
signalBadThreshold = PreferencesUtils.getRecordingDistanceInterval(sharedPreferences, context);
|
||||
|
||||
Duration minRecordingInterval = Duration.ofSeconds(PreferencesUtils.getMinRecordingInterval(sharedPreferences, context));
|
||||
signalLostThreshold = !minRecordingInterval.isNegative() ? SIGNAL_LOST_THRESHOLD.plus(minRecordingInterval) : SIGNAL_LOST_THRESHOLD;
|
||||
|
||||
gpsStatusHandler = new Handler();
|
||||
}
|
||||
|
||||
|
||||
@@ -3,12 +3,14 @@ package de.dennisguse.opentracks.services.handlers;
|
||||
import android.content.Context;
|
||||
import android.content.SharedPreferences;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.annotation.VisibleForTesting;
|
||||
|
||||
import java.util.concurrent.ExecutorService;
|
||||
import java.util.concurrent.Executors;
|
||||
|
||||
import de.dennisguse.opentracks.content.data.TrackPoint;
|
||||
import de.dennisguse.opentracks.util.PreferencesUtils;
|
||||
|
||||
public class HandlerServer {
|
||||
|
||||
@@ -32,8 +34,9 @@ public class HandlerServer {
|
||||
public void start(Context context) {
|
||||
serviceExecutor = Executors.newSingleThreadExecutor();
|
||||
|
||||
SharedPreferences sharedPreferences = PreferencesUtils.getSharedPreferences(context);
|
||||
locationHandler.onStart(context);
|
||||
locationHandler.onSharedPreferenceChanged(context, null, null);
|
||||
locationHandler.onSharedPreferenceChanged(context, sharedPreferences, null);
|
||||
}
|
||||
|
||||
public void stop(Context context) {
|
||||
@@ -45,7 +48,7 @@ public class HandlerServer {
|
||||
serviceExecutor = null;
|
||||
}
|
||||
|
||||
public void onSharedPreferenceChanged(Context context, SharedPreferences preferences, String key) {
|
||||
public void onSharedPreferenceChanged(@NonNull Context context, @NonNull SharedPreferences preferences, String key) {
|
||||
locationHandler.onSharedPreferenceChanged(context, preferences, key);
|
||||
}
|
||||
|
||||
@@ -70,8 +73,10 @@ public class HandlerServer {
|
||||
}
|
||||
|
||||
public interface Handler {
|
||||
void onStart(Context context);
|
||||
void onStop(Context context);
|
||||
void onSharedPreferenceChanged(Context context, SharedPreferences preferences, String key);
|
||||
void onStart(@NonNull Context context);
|
||||
|
||||
void onStop(@NonNull Context context);
|
||||
|
||||
void onSharedPreferenceChanged(@NonNull Context context, @NonNull SharedPreferences preferences, String key);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -34,14 +34,14 @@ class LocationHandler implements HandlerServer.Handler, LocationListener, GpsSta
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onStart(Context context) {
|
||||
gpsStatus = new GpsStatus(context, this, Duration.ofSeconds(PreferencesUtils.getMinRecordingInterval(context)));
|
||||
public void onStart(@NonNull Context context) {
|
||||
gpsStatus = new GpsStatus(context, this);
|
||||
locationManager = (LocationManager) context.getSystemService(Context.LOCATION_SERVICE);
|
||||
registerLocationListener();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onStop(Context context) {
|
||||
public void onStop(@NonNull Context context) {
|
||||
unregisterLocationListener();
|
||||
locationManager = null;
|
||||
if (gpsStatus != null) {
|
||||
@@ -51,9 +51,9 @@ class LocationHandler implements HandlerServer.Handler, LocationListener, GpsSta
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onSharedPreferenceChanged(Context context, SharedPreferences preferences, String key) {
|
||||
public void onSharedPreferenceChanged(@NonNull Context context, @NonNull SharedPreferences sharedPreferences, String key) {
|
||||
if (PreferencesUtils.isKey(context, R.string.min_recording_interval_key, key)) {
|
||||
int minRecordingInterval = PreferencesUtils.getMinRecordingInterval(context);
|
||||
int minRecordingInterval = PreferencesUtils.getMinRecordingInterval(sharedPreferences, context);
|
||||
if (minRecordingInterval == PreferencesUtils.getMinRecordingIntervalAdaptBatteryLife(context)) {
|
||||
// Choose battery life over moving time accuracy.
|
||||
locationListenerPolicy = new AdaptiveLocationListenerPolicy(Duration.ofSeconds(30), Duration.ofSeconds(5), 5);
|
||||
@@ -69,16 +69,16 @@ class LocationHandler implements HandlerServer.Handler, LocationListener, GpsSta
|
||||
}
|
||||
}
|
||||
if (PreferencesUtils.isKey(context, R.string.recording_gps_accuracy_key, key)) {
|
||||
recordingGpsAccuracy = PreferencesUtils.getRecordingGPSAccuracy(context);
|
||||
recordingGpsAccuracy = PreferencesUtils.getRecordingGPSAccuracy(sharedPreferences, context);
|
||||
}
|
||||
if (PreferencesUtils.isKey(context, R.string.min_recording_interval_key, key)) {
|
||||
if (gpsStatus != null) {
|
||||
gpsStatus.onMinRecordingIntervalChanged(PreferencesUtils.getMinRecordingInterval(context));
|
||||
gpsStatus.onMinRecordingIntervalChanged(PreferencesUtils.getMinRecordingInterval(sharedPreferences, context));
|
||||
}
|
||||
}
|
||||
if (PreferencesUtils.isKey(context, R.string.recording_distance_interval_key, key)) {
|
||||
if (gpsStatus != null) {
|
||||
gpsStatus.onRecordingDistanceChanged(PreferencesUtils.getRecordingDistanceInterval(context));
|
||||
gpsStatus.onRecordingDistanceChanged(PreferencesUtils.getRecordingDistanceInterval(sharedPreferences, context));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+25
-19
@@ -101,11 +101,11 @@ public abstract class BluetoothConnectionManager {
|
||||
|
||||
@Override
|
||||
public void onCharacteristicChanged(BluetoothGatt gatt, @NonNull BluetoothGattCharacteristic characteristic) {
|
||||
String sensorName = gatt.getDevice().getName();
|
||||
Log.d(TAG, "Received data from " + sensorName);
|
||||
Log.d(TAG, "Received data from " + gatt.getDevice().getAddress());
|
||||
|
||||
SensorData sensorData = parsePayload(sensorName, gatt.getDevice().getAddress(), characteristic);
|
||||
SensorData<?> sensorData = parsePayload(gatt.getDevice().getName(), gatt.getDevice().getAddress(), characteristic);
|
||||
if (sensorData != null) {
|
||||
Log.d(TAG, "Decoded data from " + gatt.getDevice().getAddress() + ": " + sensorData);
|
||||
observer.onChanged(sensorData);
|
||||
}
|
||||
}
|
||||
@@ -125,7 +125,7 @@ public abstract class BluetoothConnectionManager {
|
||||
Log.d(TAG, "Connecting to: " + device);
|
||||
|
||||
bluetoothGatt = device.connectGatt(context, true, connectCallback);
|
||||
SensorData sensorData = createEmptySensorData(bluetoothGatt.getDevice().getAddress());
|
||||
SensorData<?> sensorData = createEmptySensorData(bluetoothGatt.getDevice().getAddress());
|
||||
observer.onChanged(sensorData);
|
||||
}
|
||||
|
||||
@@ -148,12 +148,12 @@ public abstract class BluetoothConnectionManager {
|
||||
return address.equals(bluetoothGatt.getDevice().getAddress());
|
||||
}
|
||||
|
||||
protected abstract SensorData createEmptySensorData(String address);
|
||||
protected abstract SensorData<?> createEmptySensorData(String address);
|
||||
|
||||
/**
|
||||
* @return null if data could not be parsed.
|
||||
*/
|
||||
protected abstract SensorData parsePayload(String sensorName, String address, BluetoothGattCharacteristic characteristic);
|
||||
protected abstract SensorData<?> parsePayload(String sensorName, String address, BluetoothGattCharacteristic characteristic);
|
||||
|
||||
public static class HeartRate extends BluetoothConnectionManager {
|
||||
|
||||
@@ -162,7 +162,7 @@ public abstract class BluetoothConnectionManager {
|
||||
}
|
||||
|
||||
@Override
|
||||
protected SensorData createEmptySensorData(String address) {
|
||||
protected SensorDataHeartRate createEmptySensorData(String address) {
|
||||
return new SensorDataHeartRate(address);
|
||||
}
|
||||
|
||||
@@ -181,7 +181,7 @@ public abstract class BluetoothConnectionManager {
|
||||
}
|
||||
|
||||
@Override
|
||||
protected SensorData createEmptySensorData(String address) {
|
||||
protected SensorDataCycling.Cadence createEmptySensorData(String address) {
|
||||
return new SensorDataCycling.Cadence(address);
|
||||
}
|
||||
|
||||
@@ -197,30 +197,36 @@ public abstract class BluetoothConnectionManager {
|
||||
}
|
||||
|
||||
//Workaround for Wahoo CADENCE: this sensor reports speed (instead of cadence)
|
||||
if (cadenceAndSpeed.getSpeed() != null) {
|
||||
return new SensorDataCycling.Cadence(cadenceAndSpeed.getSpeed());
|
||||
if (cadenceAndSpeed.getDistanceSpeed() != null) {
|
||||
return new SensorDataCycling.Cadence(cadenceAndSpeed.getDistanceSpeed());
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
public static class CyclingSpeed extends BluetoothConnectionManager {
|
||||
public static class CyclingDistanceSpeed extends BluetoothConnectionManager {
|
||||
|
||||
CyclingSpeed(SensorDataObserver observer) {
|
||||
CyclingDistanceSpeed(SensorDataObserver observer) {
|
||||
super(BluetoothUtils.CYCLING_SPEED_CADENCE_SERVICE_UUID, BluetoothUtils.CYCLING_SPEED_CADENCE_MEASUREMENT_CHAR_UUID, observer);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected SensorData createEmptySensorData(String address) {
|
||||
return new SensorDataCycling.Speed(address);
|
||||
protected SensorDataCycling.DistanceSpeed createEmptySensorData(String address) {
|
||||
return new SensorDataCycling.DistanceSpeed(address);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected SensorDataCycling.Speed parsePayload(String sensorName, String address, BluetoothGattCharacteristic characteristic) {
|
||||
protected SensorDataCycling.DistanceSpeed parsePayload(String sensorName, String address, BluetoothGattCharacteristic characteristic) {
|
||||
SensorDataCycling.CadenceAndSpeed cadenceAndSpeed = BluetoothUtils.parseCyclingCrankAndWheel(address, sensorName, characteristic);
|
||||
if (cadenceAndSpeed != null) {
|
||||
return cadenceAndSpeed.getSpeed();
|
||||
// Workaround for Garmin Speed Sensor 2: provides cadence instead of speed
|
||||
SensorDataCycling.DistanceSpeed result = cadenceAndSpeed.getDistanceSpeed();
|
||||
if (result == null && cadenceAndSpeed.getCadence() != null) {
|
||||
result = new SensorDataCycling.DistanceSpeed(cadenceAndSpeed.getSensorAddress(), cadenceAndSpeed.getSensorName(), (int) cadenceAndSpeed.getCadence().getCrankRevolutionsCount(), cadenceAndSpeed.getCadence().getCrankRevolutionsTime());
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
@@ -233,7 +239,7 @@ public abstract class BluetoothConnectionManager {
|
||||
}
|
||||
|
||||
@Override
|
||||
protected SensorData createEmptySensorData(String address) {
|
||||
protected SensorDataCyclingPower createEmptySensorData(String address) {
|
||||
return new SensorDataCyclingPower(address);
|
||||
}
|
||||
|
||||
@@ -247,8 +253,8 @@ public abstract class BluetoothConnectionManager {
|
||||
|
||||
interface SensorDataObserver {
|
||||
|
||||
void onChanged(SensorData sensorData);
|
||||
void onChanged(SensorData<?> sensorData);
|
||||
|
||||
void onDisconnecting(SensorData sensorData);
|
||||
void onDisconnecting(SensorData<?> sensorData);
|
||||
}
|
||||
}
|
||||
|
||||
+26
-15
@@ -53,43 +53,50 @@ public class BluetoothRemoteSensorManager implements BluetoothConnectionManager.
|
||||
|
||||
private final BluetoothAdapter bluetoothAdapter;
|
||||
private final Context context;
|
||||
private final SharedPreferences sharedPreferences;
|
||||
|
||||
private boolean started = false;
|
||||
|
||||
private final SharedPreferences sharedPreferences;
|
||||
private int preferenceWheelCircumference;
|
||||
|
||||
private final BluetoothConnectionManager.HeartRate heartRate = new BluetoothConnectionManager.HeartRate(this);
|
||||
private final BluetoothConnectionManager.CyclingCadence cyclingCadence = new BluetoothConnectionManager.CyclingCadence(this);
|
||||
private final BluetoothConnectionManager.CyclingSpeed cyclingSpeed = new BluetoothConnectionManager.CyclingSpeed(this);
|
||||
private final BluetoothConnectionManager.CyclingDistanceSpeed cyclingSpeed = new BluetoothConnectionManager.CyclingDistanceSpeed(this);
|
||||
private final BluetoothConnectionManager.CyclingPower cyclingPower = new BluetoothConnectionManager.CyclingPower(this);
|
||||
|
||||
private final SensorDataSet sensorDataSet = new SensorDataSet();
|
||||
|
||||
private final SharedPreferences.OnSharedPreferenceChangeListener sharedPreferenceChangeListener = new SharedPreferences.OnSharedPreferenceChangeListener() {
|
||||
@Override
|
||||
public void onSharedPreferenceChanged(SharedPreferences preferences, String key) {
|
||||
public void onSharedPreferenceChanged(SharedPreferences sharedPreferences, String key) {
|
||||
if (!started) return;
|
||||
|
||||
if (PreferencesUtils.isKey(context, R.string.settings_sensor_bluetooth_heart_rate_key, key)) {
|
||||
String address = PreferencesUtils.getBluetoothHeartRateSensorAddress(context);
|
||||
String address = PreferencesUtils.getBluetoothHeartRateSensorAddress(sharedPreferences, context);
|
||||
connect(heartRate, address);
|
||||
}
|
||||
|
||||
if (PreferencesUtils.isKey(context, R.string.settings_sensor_bluetooth_cycling_cadence_key, key)) {
|
||||
String address = PreferencesUtils.getBluetoothCyclingCadenceSensorAddress(context);
|
||||
String address = PreferencesUtils.getBluetoothCyclingCadenceSensorAddress(sharedPreferences, context);
|
||||
connect(cyclingCadence, address);
|
||||
}
|
||||
|
||||
if (PreferencesUtils.isKey(context, R.string.settings_sensor_bluetooth_cycling_cadence_key, key)) {
|
||||
String address = PreferencesUtils.getBluetoothCyclingSpeedSensorAddress(context);
|
||||
String address = PreferencesUtils.getBluetoothCyclingSpeedSensorAddress(sharedPreferences, context);
|
||||
|
||||
connect(cyclingSpeed, address);
|
||||
}
|
||||
|
||||
if (PreferencesUtils.isKey(context, R.string.settings_sensor_bluetooth_cycling_power_key, key)) {
|
||||
String address = PreferencesUtils.getBluetoothCyclingPowerSensorAddress(context);
|
||||
String address = PreferencesUtils.getBluetoothCyclingPowerSensorAddress(sharedPreferences, context);
|
||||
|
||||
connect(cyclingPower, address);
|
||||
}
|
||||
|
||||
if (PreferencesUtils.isKey(context, R.string.settings_sensor_bluetooth_cycling_power_key, key)) {
|
||||
preferenceWheelCircumference = PreferencesUtils.getWheelCircumference(sharedPreferences, context);
|
||||
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
@@ -104,7 +111,7 @@ public class BluetoothRemoteSensorManager implements BluetoothConnectionManager.
|
||||
sharedPreferences.registerOnSharedPreferenceChangeListener(sharedPreferenceChangeListener);
|
||||
|
||||
//Trigger connection startup
|
||||
sharedPreferenceChangeListener.onSharedPreferenceChanged(null, null);
|
||||
sharedPreferenceChangeListener.onSharedPreferenceChanged(sharedPreferences, null);
|
||||
}
|
||||
|
||||
public synchronized void stop() {
|
||||
@@ -155,11 +162,15 @@ public class BluetoothRemoteSensorManager implements BluetoothConnectionManager.
|
||||
return sensorDataSet;
|
||||
}
|
||||
|
||||
public void reset() {
|
||||
sensorDataSet.reset();
|
||||
}
|
||||
|
||||
@Override
|
||||
public synchronized void onChanged(SensorData sensorData) {
|
||||
public synchronized void onChanged(SensorData<?> sensorData) {
|
||||
if (sensorData instanceof SensorDataCycling.Cadence) {
|
||||
SensorDataCycling.Cadence previous = sensorDataSet.getCyclingCadence();
|
||||
Log.d(TAG, "previous " + previous + "; current" + sensorData);
|
||||
Log.d(TAG, "Previous: " + previous + "; current: " + sensorData);
|
||||
|
||||
if (sensorData.equals(previous)) {
|
||||
Log.d(TAG, "onChanged: cadence data repeated.");
|
||||
@@ -167,21 +178,21 @@ public class BluetoothRemoteSensorManager implements BluetoothConnectionManager.
|
||||
}
|
||||
((SensorDataCycling.Cadence) sensorData).compute(previous);
|
||||
}
|
||||
if (sensorData instanceof SensorDataCycling.Speed) {
|
||||
SensorDataCycling.Speed previous = sensorDataSet.getCyclingSpeed();
|
||||
Log.d(TAG, "previous " + previous + "; current" + sensorData);
|
||||
if (sensorData instanceof SensorDataCycling.DistanceSpeed) {
|
||||
SensorDataCycling.DistanceSpeed previous = sensorDataSet.getCyclingDistanceSpeed();
|
||||
Log.d(TAG, "Previous: " + previous + "; Current" + sensorData);
|
||||
if (sensorData.equals(previous)) {
|
||||
Log.d(TAG, "onChanged: speed data repeated.");
|
||||
return;
|
||||
}
|
||||
((SensorDataCycling.Speed) sensorData).compute(previous, PreferencesUtils.getWheelCircumference(context));
|
||||
((SensorDataCycling.DistanceSpeed) sensorData).compute(previous, PreferencesUtils.getWheelCircumference(sharedPreferences, context));
|
||||
}
|
||||
|
||||
sensorDataSet.set(sensorData);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onDisconnecting(SensorData sensorData) {
|
||||
public void onDisconnecting(SensorData<?> sensorData) {
|
||||
sensorDataSet.remove(sensorData);
|
||||
}
|
||||
}
|
||||
|
||||
+23
-5
@@ -17,17 +17,19 @@
|
||||
package de.dennisguse.opentracks.services.tasks;
|
||||
|
||||
import android.content.Context;
|
||||
import android.content.SharedPreferences;
|
||||
import android.media.AudioManager;
|
||||
import android.speech.tts.TextToSpeech;
|
||||
import android.speech.tts.UtteranceProgressListener;
|
||||
import android.util.Log;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Locale;
|
||||
|
||||
import de.dennisguse.opentracks.content.data.Track;
|
||||
import de.dennisguse.opentracks.content.data.TrackPoint;
|
||||
import de.dennisguse.opentracks.content.provider.ContentProviderUtils;
|
||||
import de.dennisguse.opentracks.content.provider.TrackPointIterator;
|
||||
import de.dennisguse.opentracks.services.TrackRecordingService;
|
||||
import de.dennisguse.opentracks.stats.TrackStatistics;
|
||||
import de.dennisguse.opentracks.util.AnnouncementUtils;
|
||||
@@ -102,6 +104,8 @@ public class AnnouncementPeriodicTask implements PeriodicTask {
|
||||
}
|
||||
};
|
||||
|
||||
private SharedPreferences sharedPreferences;
|
||||
|
||||
private TextToSpeech tts;
|
||||
// Response from TTS after its initialization
|
||||
private int ttsInitStatus = TextToSpeech.ERROR;
|
||||
@@ -112,12 +116,15 @@ public class AnnouncementPeriodicTask implements PeriodicTask {
|
||||
this.context = context;
|
||||
audioManager = (AudioManager) context.getSystemService(Context.AUDIO_SERVICE);
|
||||
contentProviderUtils = new ContentProviderUtils(context);
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void start() {
|
||||
Log.d(TAG, "Start");
|
||||
|
||||
sharedPreferences = PreferencesUtils.getSharedPreferences(context);
|
||||
|
||||
if (tts == null) {
|
||||
tts = new TextToSpeech(context, status -> {
|
||||
Log.i(TAG, "TextToSpeech initialized with status " + status);
|
||||
@@ -164,14 +171,23 @@ public class AnnouncementPeriodicTask implements PeriodicTask {
|
||||
return;
|
||||
}
|
||||
|
||||
Track track = contentProviderUtils.getTrack(PreferencesUtils.getRecordingTrackId(context));
|
||||
Track track = contentProviderUtils.getTrack(PreferencesUtils.getRecordingTrackId(sharedPreferences, context));
|
||||
String category = track != null ? track.getCategory() : "";
|
||||
|
||||
List<TrackPoint> trackPoints = contentProviderUtils.getTrackPoints(track.getId());
|
||||
IntervalStatistics intervalStatistics = new IntervalStatistics(trackPoints, (float) (PreferencesUtils.isMetricUnits(context) ? 1d * UnitConversions.KM_TO_M : 1d * UnitConversions.MI_TO_M));
|
||||
//TODO Querying all TrackPoints all the time is inefficient; use TrackDataHub or something else.
|
||||
TrackPointIterator trackPointIterator = contentProviderUtils.getTrackPointLocationIterator(track.getId(), null);
|
||||
ArrayList<TrackPoint> trackPoints = new ArrayList<>();
|
||||
while (trackPointIterator.hasNext()) {
|
||||
trackPoints.add(trackPointIterator.next());
|
||||
}
|
||||
|
||||
boolean isMetricUnits = PreferencesUtils.isMetricUnits(sharedPreferences, context);
|
||||
boolean isReportSpeed = PreferencesUtils.isReportSpeed(sharedPreferences, context, category);
|
||||
|
||||
IntervalStatistics intervalStatistics = new IntervalStatistics(trackPoints, (float) (isMetricUnits ? 1d * UnitConversions.KM_TO_M : 1d * UnitConversions.MI_TO_M));
|
||||
IntervalStatistics.Interval lastInterval = intervalStatistics.getLastInterval();
|
||||
|
||||
String announcement = AnnouncementUtils.getAnnouncement(context, trackStatistics, category, lastInterval);
|
||||
String announcement = AnnouncementUtils.getAnnouncement(context, trackStatistics, isMetricUnits, isReportSpeed, lastInterval);
|
||||
speakAnnouncement(announcement);
|
||||
}
|
||||
|
||||
@@ -181,6 +197,8 @@ public class AnnouncementPeriodicTask implements PeriodicTask {
|
||||
tts.shutdown();
|
||||
tts = null;
|
||||
}
|
||||
|
||||
sharedPreferences = null;
|
||||
}
|
||||
|
||||
private void onTtsReady() {
|
||||
|
||||
@@ -94,7 +94,7 @@ public class PeriodicTaskExecutor {
|
||||
if (timerTaskExecutor == null) {
|
||||
timerTaskExecutor = new TimerTaskExecutor(periodicTask, trackRecordingService);
|
||||
}
|
||||
timerTaskExecutor.scheduleTask(Duration.ofSeconds(taskFrequency));
|
||||
timerTaskExecutor.scheduleTask(Duration.ofMinutes(taskFrequency));
|
||||
} else {
|
||||
// For distance periodic task
|
||||
calculateNextTaskDistance();
|
||||
|
||||
@@ -17,6 +17,7 @@
|
||||
package de.dennisguse.opentracks.settings;
|
||||
|
||||
import android.content.Context;
|
||||
import android.content.SharedPreferences;
|
||||
import android.os.Bundle;
|
||||
import android.util.AttributeSet;
|
||||
import android.view.View;
|
||||
@@ -48,7 +49,7 @@ public class ActivityTypePreference extends DialogPreference {
|
||||
setDialogIcon(null);
|
||||
setPersistent(true);
|
||||
|
||||
SummaryProvider<DialogPreference> summaryProvider = preference -> PreferencesUtils.getDefaultActivity(ActivityTypePreference.this.getContext());
|
||||
SummaryProvider<DialogPreference> summaryProvider = preference -> PreferencesUtils.getDefaultActivity(PreferencesUtils.getSharedPreferences(context), ActivityTypePreference.this.getContext());
|
||||
setSummaryProvider(summaryProvider);
|
||||
}
|
||||
|
||||
@@ -60,6 +61,8 @@ public class ActivityTypePreference extends DialogPreference {
|
||||
|
||||
public static class ActivityPreferenceDialog extends PreferenceDialogFragmentCompat {
|
||||
|
||||
private SharedPreferences sharedPreferences;
|
||||
|
||||
private AutoCompleteTextView textView;
|
||||
private ImageView iconView;
|
||||
|
||||
@@ -77,9 +80,11 @@ public class ActivityTypePreference extends DialogPreference {
|
||||
super.onBindDialogView(view);
|
||||
|
||||
final Context context = getActivity();
|
||||
sharedPreferences = PreferencesUtils.getSharedPreferences(context);
|
||||
|
||||
|
||||
textView = view.findViewById(R.id.activity_type_preference_text_view);
|
||||
String category = PreferencesUtils.getDefaultActivity(context);
|
||||
String category = PreferencesUtils.getDefaultActivity(sharedPreferences, context);
|
||||
textView.setText(category);
|
||||
ArrayAdapter<CharSequence> adapter = ArrayAdapter.createFromResource(context, R.array.activity_types, android.R.layout.simple_dropdown_item_1line);
|
||||
textView.setAdapter(adapter);
|
||||
@@ -100,8 +105,14 @@ public class ActivityTypePreference extends DialogPreference {
|
||||
updateIcon(TrackIconUtils.getIconValue(context, category));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onDestroy() {
|
||||
super.onDestroy();
|
||||
sharedPreferences = null;
|
||||
}
|
||||
|
||||
private void showIconSelectDialog() {
|
||||
String category = PreferencesUtils.getDefaultActivity(getActivity());
|
||||
String category = PreferencesUtils.getDefaultActivity(sharedPreferences, getActivity());
|
||||
ChooseActivityTypeDialogFragment.showDialog(getActivity().getSupportFragmentManager(), category);
|
||||
}
|
||||
|
||||
@@ -110,7 +121,7 @@ public class ActivityTypePreference extends DialogPreference {
|
||||
if (positiveResult) {
|
||||
String newDefaultActivity = textView.getText().toString();
|
||||
if (getPreference().callChangeListener(newDefaultActivity)) {
|
||||
PreferencesUtils.setDefaultActivity(getActivity(), newDefaultActivity);
|
||||
PreferencesUtils.setDefaultActivity(sharedPreferences, getActivity(), newDefaultActivity);
|
||||
HackUtils.invalidatePreference(getPreference());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -91,7 +91,9 @@ public class SettingsActivity extends AbstractActivity implements ChooseActivity
|
||||
|
||||
public static class PrefsFragment extends PreferenceFragmentCompat {
|
||||
|
||||
private final SharedPreferences.OnSharedPreferenceChangeListener sharedPreferenceChangeListener = (preferences, key) -> {
|
||||
private SharedPreferences sharedPreferences;
|
||||
|
||||
private final SharedPreferences.OnSharedPreferenceChangeListener sharedPreferenceChangeListener = (sharedPreferences, key) -> {
|
||||
if (PreferencesUtils.isKey(getActivity(), R.string.recording_track_id_key, key)) {
|
||||
getActivity().runOnUiThread(this::updateReset);
|
||||
}
|
||||
@@ -99,7 +101,7 @@ public class SettingsActivity extends AbstractActivity implements ChooseActivity
|
||||
getActivity().runOnUiThread(this::updateUnits);
|
||||
}
|
||||
if (PreferencesUtils.isKey(getActivity(), R.string.night_mode_key, key)) {
|
||||
getActivity().runOnUiThread(() -> ActivityUtils.applyNightMode(getContext()));
|
||||
getActivity().runOnUiThread(() -> ActivityUtils.applyNightMode(sharedPreferences, getContext()));
|
||||
}
|
||||
};
|
||||
|
||||
@@ -108,6 +110,8 @@ public class SettingsActivity extends AbstractActivity implements ChooseActivity
|
||||
|
||||
@Override
|
||||
public void onCreatePreferences(Bundle savedInstanceState, String rootKey) {
|
||||
sharedPreferences = PreferencesUtils.getSharedPreferences(getContext());
|
||||
|
||||
try {
|
||||
setPreferencesFromResource(R.xml.settings, rootKey);
|
||||
} catch (ClassCastException e) {
|
||||
@@ -123,7 +127,7 @@ public class SettingsActivity extends AbstractActivity implements ChooseActivity
|
||||
|
||||
Preference instantExportDirectoryPreference = findPreference(getString(R.string.settings_default_export_directory_key));
|
||||
instantExportDirectoryPreference.setSummaryProvider(preference -> {
|
||||
DocumentFile directory = PreferencesUtils.getDefaultExportDirectoryUri(getContext());
|
||||
DocumentFile directory = PreferencesUtils.getDefaultExportDirectoryUri(sharedPreferences, getContext());
|
||||
//Use same value for not set as Androidx ListPreference and EditTextPreference
|
||||
return directory != null ? directory.getName() : getString(R.string.not_set);
|
||||
});
|
||||
@@ -132,7 +136,10 @@ public class SettingsActivity extends AbstractActivity implements ChooseActivity
|
||||
@Override
|
||||
public void onResume() {
|
||||
super.onResume();
|
||||
PreferencesUtils.getSharedPreferences(getActivity()).registerOnSharedPreferenceChangeListener(sharedPreferenceChangeListener);
|
||||
|
||||
sharedPreferences.registerOnSharedPreferenceChangeListener(sharedPreferenceChangeListener);
|
||||
sharedPreferenceChangeListener.onSharedPreferenceChanged(sharedPreferences, null);
|
||||
|
||||
updateUnits();
|
||||
updateReset();
|
||||
updateBluetooth();
|
||||
@@ -142,13 +149,13 @@ public class SettingsActivity extends AbstractActivity implements ChooseActivity
|
||||
|
||||
private void updatePostWorkoutExport() {
|
||||
Preference instantExportEnabledPreference = findPreference(getString(R.string.post_workout_export_enabled_key));
|
||||
instantExportEnabledPreference.setEnabled(PreferencesUtils.isDefaultExportDirectoryUri(getContext()));
|
||||
instantExportEnabledPreference.setEnabled(PreferencesUtils.isDefaultExportDirectoryUri(sharedPreferences, getContext()));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onPause() {
|
||||
super.onPause();
|
||||
PreferencesUtils.getSharedPreferences(getActivity()).unregisterOnSharedPreferenceChangeListener(sharedPreferenceChangeListener);
|
||||
sharedPreferences.unregisterOnSharedPreferenceChangeListener(sharedPreferenceChangeListener);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -176,6 +183,12 @@ public class SettingsActivity extends AbstractActivity implements ChooseActivity
|
||||
super.onDisplayPreferenceDialog(preference);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onDestroy() {
|
||||
super.onDestroy();
|
||||
sharedPreferences = null;
|
||||
}
|
||||
|
||||
public void setDefaultActivity(String iconValue) {
|
||||
if (activityPreferenceDialog != null) {
|
||||
activityPreferenceDialog.updateUI(iconValue);
|
||||
@@ -183,7 +196,7 @@ public class SettingsActivity extends AbstractActivity implements ChooseActivity
|
||||
}
|
||||
|
||||
private void updateReset() {
|
||||
final boolean isRecording = PreferencesUtils.isRecording(getActivity());
|
||||
final boolean isRecording = PreferencesUtils.isRecording(sharedPreferences, getActivity());
|
||||
Preference resetPreference = findPreference(getString(R.string.settings_reset_key));
|
||||
resetPreference.setSummary(isRecording ? getString(R.string.settings_not_while_recording) : "");
|
||||
resetPreference.setEnabled(!isRecording);
|
||||
@@ -196,7 +209,7 @@ public class SettingsActivity extends AbstractActivity implements ChooseActivity
|
||||
}
|
||||
|
||||
private void updateUnits() {
|
||||
boolean metricUnits = PreferencesUtils.isMetricUnits(getActivity());
|
||||
boolean metricUnits = PreferencesUtils.isMetricUnits(sharedPreferences, getActivity());
|
||||
|
||||
ListPreference voiceFrequency = findPreference(getString(R.string.voice_frequency_key));
|
||||
voiceFrequency.setEntries(StringUtils.getFrequencyOptions(getActivity(), metricUnits));
|
||||
|
||||
@@ -103,10 +103,15 @@ public class TrackStatisticsUpdater {
|
||||
/**
|
||||
* Adds a trackPoint.
|
||||
*
|
||||
* @param trackPoint the trackPoint
|
||||
* @param minRecordingDistance the min recording distance
|
||||
* @param trackPoint the trackPoint
|
||||
* @param minGPSDistance the min recording distance
|
||||
*/
|
||||
public void addTrackPoint(TrackPoint trackPoint, int minRecordingDistance) {
|
||||
public void addTrackPoint(TrackPoint trackPoint, int minGPSDistance) {
|
||||
internalAddTrackPoint(trackPoint, minGPSDistance);
|
||||
Log.d(TAG, this.toString());
|
||||
}
|
||||
|
||||
private void internalAddTrackPoint(TrackPoint trackPoint, int minGPSDistance) {
|
||||
if (!trackInitialized) {
|
||||
trackStatistics.setStartTime(trackPoint.getTime());
|
||||
trackInitialized = true;
|
||||
@@ -120,19 +125,26 @@ public class TrackStatisticsUpdater {
|
||||
currentSegment.setStopTime(trackPoint.getTime());
|
||||
currentSegment.setTotalTime(Duration.between(currentSegment.getStartTime(), trackPoint.getTime()));
|
||||
|
||||
if (trackPoint.isSegmentStart() || trackPoint.isSegmentEnd()) {
|
||||
if (trackPoint.isSegmentEnd()) {
|
||||
if (lastTrackPoint != null && lastMovingTrackPoint != null && lastTrackPoint != lastMovingTrackPoint) {
|
||||
currentSegment.addTotalDistance(lastMovingTrackPoint.distanceTo(lastTrackPoint));
|
||||
}
|
||||
}
|
||||
trackStatistics.merge(currentSegment);
|
||||
currentSegment.reset(trackPoint.getTime());
|
||||
if (trackPoint.isSegmentStart()) {
|
||||
reset(trackPoint);
|
||||
return;
|
||||
}
|
||||
|
||||
lastTrackPoint = null;
|
||||
lastMovingTrackPoint = null;
|
||||
elevationBuffer_m.reset();
|
||||
speedBuffer_ms.reset();
|
||||
// Process sensor data
|
||||
if (trackPoint.hasElevationGain()) {
|
||||
currentSegment.addTotalElevationGain(trackPoint.getElevationGain());
|
||||
}
|
||||
|
||||
if (trackPoint.hasElevationLoss()) {
|
||||
currentSegment.addTotalElevationLoss(trackPoint.getElevationLoss());
|
||||
}
|
||||
|
||||
if (trackPoint.hasSensorDistance()) {
|
||||
currentSegment.addTotalDistance(trackPoint.getSensorDistance());
|
||||
}
|
||||
|
||||
if (trackPoint.isSegmentEnd()) {
|
||||
reset(trackPoint);
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -141,39 +153,30 @@ public class TrackStatisticsUpdater {
|
||||
updateAbsoluteElevation(trackPoint.getAltitude());
|
||||
}
|
||||
|
||||
//Get elevation gain
|
||||
if (trackPoint.hasElevationGain()) {
|
||||
currentSegment.addTotalElevationGain(trackPoint.getElevationGain());
|
||||
Log.d(TAG, "elevation gain: " + trackPoint.getElevationGain());
|
||||
}
|
||||
|
||||
//Get elevation loss
|
||||
if (trackPoint.hasElevationLoss()) {
|
||||
currentSegment.addTotalElevationLoss(trackPoint.getElevationLoss());
|
||||
Log.d(TAG, "elevation loss: " + trackPoint.getElevationLoss());
|
||||
}
|
||||
|
||||
if (lastTrackPoint == null || lastMovingTrackPoint == null) {
|
||||
lastTrackPoint = trackPoint;
|
||||
lastMovingTrackPoint = trackPoint;
|
||||
return;
|
||||
}
|
||||
|
||||
double movingDistance = lastMovingTrackPoint.distanceTo(trackPoint);
|
||||
if (movingDistance < minRecordingDistance && !trackPoint.isMoving()) {
|
||||
speedBuffer_ms.reset();
|
||||
lastTrackPoint = trackPoint;
|
||||
return;
|
||||
if (!trackPoint.hasSensorDistance()) {
|
||||
// GPS-based distance/speed
|
||||
float movingDistance = lastMovingTrackPoint.distanceToPrevious(trackPoint);
|
||||
if (movingDistance < minGPSDistance && !trackPoint.isMoving()) {
|
||||
speedBuffer_ms.reset();
|
||||
lastTrackPoint = trackPoint;
|
||||
return;
|
||||
}
|
||||
// Update total distance
|
||||
currentSegment.addTotalDistance(movingDistance);
|
||||
}
|
||||
|
||||
Duration movingTime = Duration.between(lastTrackPoint.getTime(), trackPoint.getTime());
|
||||
if (movingTime.isNegative()) {
|
||||
lastTrackPoint = trackPoint;
|
||||
return;
|
||||
}
|
||||
|
||||
// Update total distance
|
||||
currentSegment.addTotalDistance(movingDistance);
|
||||
|
||||
// Update moving time
|
||||
currentSegment.addMovingTime(movingTime);
|
||||
|
||||
@@ -186,6 +189,16 @@ public class TrackStatisticsUpdater {
|
||||
lastMovingTrackPoint = trackPoint;
|
||||
}
|
||||
|
||||
private void reset(TrackPoint trackPoint) {
|
||||
trackStatistics.merge(currentSegment);
|
||||
currentSegment.reset(trackPoint.getTime());
|
||||
|
||||
lastTrackPoint = null;
|
||||
lastMovingTrackPoint = null;
|
||||
elevationBuffer_m.reset();
|
||||
speedBuffer_ms.reset();
|
||||
}
|
||||
|
||||
public void addTrackPoint(TrackPointIterator iterator, int minRecordingDistance) {
|
||||
while (iterator.hasNext()) {
|
||||
TrackPoint location = iterator.next();
|
||||
@@ -224,20 +237,14 @@ public class TrackStatisticsUpdater {
|
||||
|
||||
/**
|
||||
* Updates an elevation reading.
|
||||
*
|
||||
* @param elevation the elevation
|
||||
* @return the difference
|
||||
*/
|
||||
@VisibleForTesting
|
||||
private double updateAbsoluteElevation(double elevation) {
|
||||
private void updateAbsoluteElevation(double elevation) {
|
||||
// Update elevation using the smoothed average
|
||||
double oldAverage = elevationBuffer_m.getAverage();
|
||||
elevationBuffer_m.setNext(elevation);
|
||||
double newAverage = elevationBuffer_m.getAverage();
|
||||
|
||||
currentSegment.updateElevationExtremities(newAverage);
|
||||
|
||||
return newAverage - oldAverage;
|
||||
}
|
||||
|
||||
private boolean isValidSpeed(@NonNull TrackPoint trackPoint, @NonNull TrackPoint lastTrackPoint) {
|
||||
@@ -267,4 +274,11 @@ public class TrackStatisticsUpdater {
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "TrackStatisticsUpdater{" +
|
||||
"trackStatistics=" + trackStatistics +
|
||||
'}';
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3,6 +3,7 @@ package de.dennisguse.opentracks.util;
|
||||
import android.app.Activity;
|
||||
import android.app.SearchManager;
|
||||
import android.content.Context;
|
||||
import android.content.SharedPreferences;
|
||||
import android.os.Build;
|
||||
import android.os.VibrationEffect;
|
||||
import android.os.Vibrator;
|
||||
@@ -150,7 +151,7 @@ public class ActivityUtils {
|
||||
toast.show();
|
||||
}
|
||||
|
||||
public static void applyNightMode(Context context) {
|
||||
AppCompatDelegate.setDefaultNightMode(PreferencesUtils.getDefaultNightMode(context));
|
||||
public static void applyNightMode(SharedPreferences sharedPreferences, Context context) {
|
||||
AppCompatDelegate.setDefaultNightMode(PreferencesUtils.getDefaultNightMode(sharedPreferences, context));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -15,9 +15,7 @@ public class AnnouncementUtils {
|
||||
private AnnouncementUtils() {
|
||||
}
|
||||
|
||||
public static String getAnnouncement(Context context, TrackStatistics trackStatistics, String category, @Nullable IntervalStatistics.Interval currentInterval) {
|
||||
boolean metricUnits = PreferencesUtils.isMetricUnits(context);
|
||||
boolean reportSpeed = PreferencesUtils.isReportSpeed(context, category);
|
||||
public static String getAnnouncement(Context context, TrackStatistics trackStatistics, boolean isMetricUnits, boolean isReportSpeed, @Nullable IntervalStatistics.Interval currentInterval) {
|
||||
double distance = trackStatistics.getTotalDistance() * UnitConversions.M_TO_KM;
|
||||
double distancePerTime = trackStatistics.getAverageMovingSpeed() * UnitConversions.MPS_TO_KMH;
|
||||
double currentDistancePerTime = currentInterval != null ? currentInterval.getSpeed_ms() * UnitConversions.MPS_TO_KMH : 0;
|
||||
@@ -26,7 +24,7 @@ public class AnnouncementUtils {
|
||||
return context.getString(R.string.voice_total_distance_zero);
|
||||
}
|
||||
|
||||
if (!metricUnits) {
|
||||
if (!isMetricUnits) {
|
||||
distance *= UnitConversions.KM_TO_MI;
|
||||
distancePerTime *= UnitConversions.KM_TO_MI;
|
||||
currentDistancePerTime *= UnitConversions.KM_TO_MI;
|
||||
@@ -35,15 +33,15 @@ public class AnnouncementUtils {
|
||||
String rate;
|
||||
String currentRate;
|
||||
String currentRateMsg;
|
||||
if (reportSpeed) {
|
||||
int speedId = metricUnits ? R.plurals.voiceSpeedKilometersPerHour : R.plurals.voiceSpeedMilesPerHour;
|
||||
if (isReportSpeed) {
|
||||
int speedId = isMetricUnits ? R.plurals.voiceSpeedKilometersPerHour : R.plurals.voiceSpeedMilesPerHour;
|
||||
rate = context.getResources().getQuantityString(speedId, getQuantityCount(distancePerTime), distancePerTime);
|
||||
|
||||
currentRate = context.getResources().getQuantityString(speedId, getQuantityCount(currentDistancePerTime), currentDistancePerTime);
|
||||
currentRateMsg = context.getString(R.string.voice_speed_lap, currentRate);
|
||||
} else {
|
||||
double timePerDistance = distancePerTime == 0 ? 0.0 : 1 / distancePerTime;
|
||||
int paceId = metricUnits ? R.string.voice_pace_per_kilometer : R.string.voice_pace_per_mile;
|
||||
int paceId = isMetricUnits ? R.string.voice_pace_per_kilometer : R.string.voice_pace_per_mile;
|
||||
Duration time = Duration.ofMillis((long) (timePerDistance * UnitConversions.HR_TO_MIN * UnitConversions.MIN_TO_S * UnitConversions.S_TO_MS));
|
||||
rate = context.getString(paceId, getAnnounceTime(context, time));
|
||||
|
||||
@@ -53,7 +51,7 @@ public class AnnouncementUtils {
|
||||
currentRateMsg = context.getString(R.string.voice_pace_lap, currentRate);
|
||||
}
|
||||
|
||||
int totalDistanceId = metricUnits ? R.plurals.voiceTotalDistanceKilometers : R.plurals.voiceTotalDistanceMiles;
|
||||
int totalDistanceId = isMetricUnits ? R.plurals.voiceTotalDistanceKilometers : R.plurals.voiceTotalDistanceMiles;
|
||||
String totalDistance = context.getResources().getQuantityString(totalDistanceId, getQuantityCount(distance), distance);
|
||||
|
||||
currentRateMsg = currentInterval == null ? "" : " " + currentRateMsg;
|
||||
@@ -65,8 +63,8 @@ public class AnnouncementUtils {
|
||||
private static String getAnnounceTime(Context context, Duration duration) {
|
||||
String result = "";
|
||||
|
||||
int hours = (int) (duration.getSeconds() / (60 * 60));
|
||||
int minutes = (int) (duration.getSeconds() / 60);
|
||||
int hours = (int) (duration.toHours());
|
||||
int minutes = (int) (duration.toMinutes() % 60);
|
||||
int seconds = (int) (duration.getSeconds() % 60);
|
||||
|
||||
if (hours != 0) {
|
||||
|
||||
@@ -126,12 +126,12 @@ public class BluetoothUtils {
|
||||
cadence = new SensorDataCycling.Cadence(address, sensorName, crankCount, crankTime);
|
||||
}
|
||||
|
||||
SensorDataCycling.Speed speed = null;
|
||||
SensorDataCycling.DistanceSpeed speed = null;
|
||||
if (hasWheel && valueLength - index >= 4) {
|
||||
int wheelCount = characteristic.getIntValue(BluetoothGattCharacteristic.FORMAT_UINT16, index);
|
||||
index += 2;
|
||||
int wheelTime = characteristic.getIntValue(BluetoothGattCharacteristic.FORMAT_UINT16, index); // 1/1024s
|
||||
speed = new SensorDataCycling.Speed(address, sensorName, wheelCount, wheelTime);
|
||||
speed = new SensorDataCycling.DistanceSpeed(address, sensorName, wheelCount, wheelTime);
|
||||
}
|
||||
|
||||
return new SensorDataCycling.CadenceAndSpeed(address, sensorName, cadence, speed);
|
||||
|
||||
@@ -2,6 +2,7 @@ package de.dennisguse.opentracks.util;
|
||||
|
||||
import android.content.ContentResolver;
|
||||
import android.content.Context;
|
||||
import android.content.SharedPreferences;
|
||||
import android.database.Cursor;
|
||||
import android.net.Uri;
|
||||
import android.provider.DocumentsContract;
|
||||
@@ -14,6 +15,8 @@ import java.io.IOException;
|
||||
import java.io.OutputStream;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.UUID;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
import de.dennisguse.opentracks.content.data.Track;
|
||||
import de.dennisguse.opentracks.io.file.TrackFileFormat;
|
||||
@@ -26,9 +29,10 @@ public class ExportUtils {
|
||||
private static final String TAG = ExportUtils.class.getSimpleName();
|
||||
|
||||
public static void postWorkoutExport(Context context, Track track, ExportServiceResultReceiver resultReceiver) {
|
||||
if (PreferencesUtils.shouldInstantExportAfterWorkout(context)) {
|
||||
TrackFileFormat trackFileFormat = PreferencesUtils.getExportTrackFileFormat(context);
|
||||
DocumentFile directory = PreferencesUtils.getDefaultExportDirectoryUri(context);
|
||||
SharedPreferences sharedPreferences = PreferencesUtils.getSharedPreferences(context);
|
||||
if (PreferencesUtils.shouldInstantExportAfterWorkout(sharedPreferences, context)) {
|
||||
TrackFileFormat trackFileFormat = PreferencesUtils.getExportTrackFileFormat(sharedPreferences, context);
|
||||
DocumentFile directory = PreferencesUtils.getDefaultExportDirectoryUri(sharedPreferences, context);
|
||||
|
||||
ExportService.enqueue(context, resultReceiver, track.getId(), trackFileFormat, directory.getUri());
|
||||
}
|
||||
@@ -37,7 +41,7 @@ public class ExportUtils {
|
||||
public static boolean exportTrack(Context context, TrackFileFormat trackFileFormat, DocumentFile directory, Track track) {
|
||||
TrackExporter trackExporter = trackFileFormat.createTrackExporter(context);
|
||||
|
||||
Uri exportDocumentFileUri = getExportDocumentFileUri(context, track.getId(), trackFileFormat, directory);
|
||||
Uri exportDocumentFileUri = getExportDocumentFileUri(context, track, trackFileFormat, directory);
|
||||
if (exportDocumentFileUri == null) {
|
||||
Log.e(TAG, "Couldn't create document file for export");
|
||||
return false;
|
||||
@@ -62,8 +66,10 @@ public class ExportUtils {
|
||||
}
|
||||
}
|
||||
|
||||
public static boolean isExportFileExists(Track.Id trackId, String trackFileFormatExtension, List<String> filesName) {
|
||||
return filesName.contains(getExportFileNameByTrackId(trackId, trackFileFormatExtension));
|
||||
public static boolean isExportFileExists(UUID uuuid, String trackFileFormatExtension, List<String> filesName) {
|
||||
String word = uuuid.toString().substring(0, 8) + ".*\\." + trackFileFormatExtension;
|
||||
Pattern pattern = Pattern.compile(word);
|
||||
return filesName.stream().anyMatch(w -> pattern.matcher(w).matches());
|
||||
}
|
||||
|
||||
public static List<String> getAllFiles(Context context, Uri directoryUri) {
|
||||
@@ -82,8 +88,8 @@ public class ExportUtils {
|
||||
return fileNames;
|
||||
}
|
||||
|
||||
private static Uri getExportDocumentFileUri(Context context, Track.Id trackId, TrackFileFormat trackFileFormat, DocumentFile directory) {
|
||||
String exportFileName = getExportFileNameByTrackId(trackId, trackFileFormat.getExtension());
|
||||
private static Uri getExportDocumentFileUri(Context context, Track track, TrackFileFormat trackFileFormat, DocumentFile directory) {
|
||||
String exportFileName = getExportFileNameForTrack(track, trackFileFormat.getExtension());
|
||||
Uri exportDocumentFileUri = findFile(context, directory.getUri(), exportFileName);
|
||||
if (exportDocumentFileUri == null) {
|
||||
final DocumentFile file = directory.createFile(trackFileFormat.getMimeType(), exportFileName);
|
||||
@@ -94,8 +100,8 @@ public class ExportUtils {
|
||||
return exportDocumentFileUri;
|
||||
}
|
||||
|
||||
private static String getExportFileNameByTrackId(Track.Id trackId, String trackFileFormatExtension) {
|
||||
return trackId.getId() + "." + trackFileFormatExtension;
|
||||
private static String getExportFileNameForTrack(Track track, String trackFileFormatExtension) {
|
||||
return track.getUuid().toString().substring(0, 8) + "_" + track.getName() + "." + trackFileFormatExtension;
|
||||
}
|
||||
|
||||
private static Uri findFile(Context context, Uri directoryUri, String exportFileName) {
|
||||
|
||||
@@ -3,6 +3,7 @@ package de.dennisguse.opentracks.util;
|
||||
import android.content.ClipData;
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.content.SharedPreferences;
|
||||
import android.net.Uri;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
@@ -73,11 +74,13 @@ public class IntentDashboardUtils {
|
||||
|
||||
intent.putParcelableArrayListExtra(ACTION_DASHBOARD_PAYLOAD, uris);
|
||||
|
||||
intent.putExtra(EXTRAS_SHOULD_KEEP_SCREEN_ON, PreferencesUtils.shouldKeepScreenOn(context));
|
||||
intent.putExtra(EXTRAS_SHOW_WHEN_LOCKED, PreferencesUtils.shouldShowStatsOnLockscreen(context));
|
||||
SharedPreferences sharedPreferences = PreferencesUtils.getSharedPreferences(context);
|
||||
|
||||
intent.putExtra(EXTRAS_SHOULD_KEEP_SCREEN_ON, PreferencesUtils.shouldKeepScreenOn(sharedPreferences, context));
|
||||
intent.putExtra(EXTRAS_SHOW_WHEN_LOCKED, PreferencesUtils.shouldShowStatsOnLockscreen(sharedPreferences, context));
|
||||
intent.putExtra(EXTRAS_OPENTRACKS_IS_RECORDING_THIS_TRACK, isRecording);
|
||||
if (isRecording) {
|
||||
intent.putExtra(EXTRAS_SHOW_FULLSCREEN, PreferencesUtils.shouldUseFullscreen(context));
|
||||
intent.putExtra(EXTRAS_SHOW_FULLSCREEN, PreferencesUtils.shouldUseFullscreen(sharedPreferences, context));
|
||||
}
|
||||
|
||||
intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
|
||||
|
||||
@@ -91,7 +91,7 @@ public class IntentUtils {
|
||||
continue;
|
||||
}
|
||||
|
||||
Pair<Uri, String> uriAndMime = ShareContentProvider.createURI(trackId, track.getName(), PreferencesUtils.getExportTrackFileFormat(context));
|
||||
Pair<Uri, String> uriAndMime = ShareContentProvider.createURI(trackId, track.getName(), PreferencesUtils.getExportTrackFileFormat(PreferencesUtils.getSharedPreferences(context), context));
|
||||
uris.add(uriAndMime.first);
|
||||
mime = uriAndMime.second;
|
||||
}
|
||||
|
||||
@@ -42,37 +42,32 @@ public class PreferencesUtils {
|
||||
private PreferencesUtils() {
|
||||
}
|
||||
|
||||
@Deprecated //Should only be used to get a sharedPreference for more than one interaction!
|
||||
public static SharedPreferences getSharedPreferences(Context context) {
|
||||
return PreferenceManager.getDefaultSharedPreferences(context);
|
||||
}
|
||||
|
||||
public static SharedPreferences register(Context context, SharedPreferences.OnSharedPreferenceChangeListener sharedPreferenceChangeListener) {
|
||||
SharedPreferences sharedPreferences = getSharedPreferences(context);
|
||||
sharedPreferences.registerOnSharedPreferenceChangeListener(sharedPreferenceChangeListener);
|
||||
return sharedPreferences;
|
||||
}
|
||||
|
||||
public static void unregister(Context context, SharedPreferences.OnSharedPreferenceChangeListener sharedPreferenceChangeListener) {
|
||||
getSharedPreferences(context).unregisterOnSharedPreferenceChangeListener(sharedPreferenceChangeListener);
|
||||
}
|
||||
|
||||
@Deprecated
|
||||
//NOTE: is at the moment still used to determine if a track is currently recorded; better ask the service directly.
|
||||
//NOTE: This was also used to recover from a service restart, but this data should not be exposed to the whole application.
|
||||
public static final long RECORDING_TRACK_ID_DEFAULT = -1L;
|
||||
|
||||
@Deprecated //Use the TrackRecordingService
|
||||
public static Track.Id getRecordingTrackId(Context context) {
|
||||
SharedPreferences sharedPreferences = getSharedPreferences(context);
|
||||
public static Track.Id getRecordingTrackId(Context context, SharedPreferences sharedPreferences) {
|
||||
return new Track.Id(sharedPreferences.getLong(getKey(context, R.string.recording_track_id_key), RECORDING_TRACK_ID_DEFAULT));
|
||||
}
|
||||
|
||||
public static String getDefaultActivity(Context context) {
|
||||
return getString(context, R.string.default_activity_key, context.getString(R.string.default_activity_default));
|
||||
@Deprecated //Use the TrackRecordingService
|
||||
public static Track.Id getRecordingTrackId(SharedPreferences sharedPreferences, Context context) {
|
||||
return getRecordingTrackId(context, sharedPreferences);
|
||||
}
|
||||
|
||||
public static void setDefaultActivity(Context context, String newDefaultActivity) {
|
||||
setString(context, R.string.default_activity_key, newDefaultActivity);
|
||||
public static String getDefaultActivity(SharedPreferences sharedPreferences, Context context) {
|
||||
return getString(sharedPreferences, context, R.string.default_activity_key, context.getString(R.string.default_activity_default));
|
||||
}
|
||||
|
||||
public static void setDefaultActivity(SharedPreferences sharedPreferences, Context context, String newDefaultActivity) {
|
||||
setString(sharedPreferences, context, R.string.default_activity_key, newDefaultActivity);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -96,48 +91,19 @@ public class PreferencesUtils {
|
||||
return key == null || key.equals(getKey(context, keyId));
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets a boolean preference value.
|
||||
*
|
||||
* @param context the context
|
||||
* @param keyId the key id
|
||||
* @param defaultValue the default value
|
||||
*/
|
||||
private static boolean getBoolean(Context context, int keyId, boolean defaultValue) {
|
||||
SharedPreferences sharedPreferences = getSharedPreferences(context);
|
||||
private static boolean getBoolean(SharedPreferences sharedPreferences, Context context, int keyId, boolean defaultValue) {
|
||||
return sharedPreferences.getBoolean(getKey(context, keyId), defaultValue);
|
||||
}
|
||||
|
||||
private static boolean getBoolean(Context context, SharedPreferences sharedPreferences, int keyId, boolean defaultValue) {
|
||||
return sharedPreferences.getBoolean(getKey(context, keyId), defaultValue);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets a boolean preference value.
|
||||
*
|
||||
* @param context the context
|
||||
* @param keyId the key id
|
||||
* @param value the value
|
||||
*/
|
||||
//TODO Don't use; this function is only to be used TrackRecordingService and will be removed.
|
||||
@VisibleForTesting
|
||||
public static void setBoolean(Context context, int keyId, boolean value) {
|
||||
SharedPreferences sharedPreferences = getSharedPreferences(context);
|
||||
public static void setBoolean(SharedPreferences sharedPreferences, Context context, int keyId, boolean value) {
|
||||
Editor editor = sharedPreferences.edit();
|
||||
editor.putBoolean(getKey(context, keyId), value);
|
||||
editor.apply();
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets an integer preference value.
|
||||
*
|
||||
* @param context the context
|
||||
* @param keyId the key id
|
||||
* @param defaultValue the default value
|
||||
*/
|
||||
private static int getInt(Context context, int keyId, int defaultValue) {
|
||||
SharedPreferences sharedPreferences = getSharedPreferences(context);
|
||||
|
||||
private static int getInt(SharedPreferences sharedPreferences, Context context, int keyId, int defaultValue) {
|
||||
try {
|
||||
return sharedPreferences.getInt(getKey(context, keyId), defaultValue);
|
||||
} catch (ClassCastException e) {
|
||||
@@ -153,82 +119,42 @@ public class PreferencesUtils {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets an integer preference value.
|
||||
*
|
||||
* @param context the context
|
||||
* @param keyId the key id
|
||||
* @param value the value
|
||||
*/
|
||||
@VisibleForTesting
|
||||
public static void setInt(Context context, int keyId, int value) {
|
||||
SharedPreferences sharedPreferences = getSharedPreferences(context);
|
||||
public static void setInt(SharedPreferences sharedPreferences, Context context, int keyId, int value) {
|
||||
Editor editor = sharedPreferences.edit();
|
||||
editor.putInt(getKey(context, keyId), value);
|
||||
editor.apply();
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets a long preference value.
|
||||
*
|
||||
* @param context the context
|
||||
* @param keyId the key id
|
||||
* @param value the value
|
||||
*/
|
||||
//TODO Don't use; this function is only to be used TrackRecordingService and will be removed.
|
||||
@Deprecated
|
||||
@VisibleForTesting
|
||||
public static void setLong(Context context, int keyId, long value) {
|
||||
SharedPreferences sharedPreferences = getSharedPreferences(context);
|
||||
public static void setLong(SharedPreferences sharedPreferences, Context context, int keyId, long value) {
|
||||
Editor editor = sharedPreferences.edit();
|
||||
editor.putLong(getKey(context, keyId), value);
|
||||
editor.apply();
|
||||
}
|
||||
|
||||
@Deprecated
|
||||
public static String getString(Context context, int keyId, String defaultValue) {
|
||||
SharedPreferences sharedPreferences = getSharedPreferences(context);
|
||||
public static String getString(SharedPreferences sharedPreferences, Context context, int keyId, String defaultValue) {
|
||||
return sharedPreferences.getString(getKey(context, keyId), defaultValue);
|
||||
}
|
||||
|
||||
public static String getString(Context context, SharedPreferences sharedPreferences, int keyId, String defaultValue) {
|
||||
return sharedPreferences.getString(getKey(context, keyId), defaultValue);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets a string preference value.
|
||||
*
|
||||
* @param context the context
|
||||
* @param keyId the key id
|
||||
* @param value the value
|
||||
*/
|
||||
@VisibleForTesting
|
||||
public static void setString(Context context, int keyId, String value) {
|
||||
SharedPreferences sharedPreferences = getSharedPreferences(context);
|
||||
public static void setString(SharedPreferences sharedPreferences, Context context, int keyId, String value) {
|
||||
Editor editor = sharedPreferences.edit();
|
||||
editor.putString(getKey(context, keyId), value);
|
||||
editor.apply();
|
||||
}
|
||||
|
||||
@Deprecated
|
||||
public static boolean isMetricUnits(Context context) {
|
||||
return isMetricUnits(getSharedPreferences(context), context);
|
||||
}
|
||||
|
||||
public static boolean isMetricUnits(SharedPreferences sharedPreferences, Context context) {
|
||||
final String STATS_UNIT = context.getString(R.string.stats_units_default);
|
||||
return STATS_UNIT.equals(getString(context, sharedPreferences, R.string.stats_units_key, STATS_UNIT));
|
||||
}
|
||||
|
||||
@Deprecated
|
||||
public static boolean isReportSpeed(Context context, String category) {
|
||||
return isReportSpeed(getSharedPreferences(context), context, category);
|
||||
return STATS_UNIT.equals(getString(sharedPreferences, context, R.string.stats_units_key, STATS_UNIT));
|
||||
}
|
||||
|
||||
public static boolean isReportSpeed(SharedPreferences sharedPreferences, Context context, String category) {
|
||||
final String STATS_RATE_DEFAULT = context.getString(R.string.stats_rate_default);
|
||||
String currentStatsRate = getString(context, sharedPreferences, R.string.stats_rate_key, STATS_RATE_DEFAULT);
|
||||
if (currentStatsRate.equals(getString(context, sharedPreferences, R.string.stats_rate_speed_or_pace_default, STATS_RATE_DEFAULT))) {
|
||||
String currentStatsRate = getString(sharedPreferences, context, R.string.stats_rate_key, STATS_RATE_DEFAULT);
|
||||
if (currentStatsRate.equals(getString(sharedPreferences, context, R.string.stats_rate_speed_or_pace_default, STATS_RATE_DEFAULT))) {
|
||||
return TrackIconUtils.isSpeedIcon(context, category);
|
||||
}
|
||||
|
||||
@@ -236,8 +162,8 @@ public class PreferencesUtils {
|
||||
}
|
||||
|
||||
@Deprecated //Use TrackRecordingService
|
||||
public static boolean isRecordingTrackPaused(Context context) {
|
||||
return getBoolean(context, R.string.recording_track_paused_key, isRecordingTrackPausedDefault(context));
|
||||
public static boolean isRecordingTrackPaused(SharedPreferences sharedPreferences, Context context) {
|
||||
return getBoolean(sharedPreferences, context, R.string.recording_track_paused_key, isRecordingTrackPausedDefault(context));
|
||||
}
|
||||
|
||||
@Deprecated //Use TrackRecordingService
|
||||
@@ -246,9 +172,9 @@ public class PreferencesUtils {
|
||||
}
|
||||
|
||||
@Deprecated //Use TrackRecordingService
|
||||
public static void defaultRecordingTrackPaused(Context context) {
|
||||
public static void defaultRecordingTrackPaused(SharedPreferences sharedPreferences, Context context) {
|
||||
final boolean RECORDING_TRACK_PAUSED = context.getResources().getBoolean(R.bool.recording_track_paused_default);
|
||||
setBoolean(context, R.string.recording_track_paused_key, RECORDING_TRACK_PAUSED);
|
||||
setBoolean(sharedPreferences, context, R.string.recording_track_paused_key, RECORDING_TRACK_PAUSED);
|
||||
}
|
||||
|
||||
private static String getBluetoothSensorAddressNone(Context context) {
|
||||
@@ -259,83 +185,74 @@ public class PreferencesUtils {
|
||||
return getBluetoothSensorAddressNone(context).equals(currentValue);
|
||||
}
|
||||
|
||||
public static String getBluetoothHeartRateSensorAddress(Context context) {
|
||||
return getString(context, R.string.settings_sensor_bluetooth_heart_rate_key, getBluetoothSensorAddressNone(context));
|
||||
public static String getBluetoothHeartRateSensorAddress(SharedPreferences sharedPreferences, Context context) {
|
||||
return getString(sharedPreferences, context, R.string.settings_sensor_bluetooth_heart_rate_key, getBluetoothSensorAddressNone(context));
|
||||
}
|
||||
|
||||
public static String getBluetoothCyclingCadenceSensorAddress(Context context) {
|
||||
return getString(context, R.string.settings_sensor_bluetooth_cycling_cadence_key, getBluetoothSensorAddressNone(context));
|
||||
public static String getBluetoothCyclingCadenceSensorAddress(SharedPreferences sharedPreferences, Context context) {
|
||||
return getString(sharedPreferences, context, R.string.settings_sensor_bluetooth_cycling_cadence_key, getBluetoothSensorAddressNone(context));
|
||||
}
|
||||
|
||||
public static String getBluetoothCyclingSpeedSensorAddress(Context context) {
|
||||
return getString(context, R.string.settings_sensor_bluetooth_cycling_speed_key, getBluetoothSensorAddressNone(context));
|
||||
public static String getBluetoothCyclingSpeedSensorAddress(SharedPreferences sharedPreferences, Context context) {
|
||||
return getString(sharedPreferences, context, R.string.settings_sensor_bluetooth_cycling_speed_key, getBluetoothSensorAddressNone(context));
|
||||
}
|
||||
|
||||
public static int getWheelCircumference(Context context) {
|
||||
public static int getWheelCircumference(SharedPreferences sharedPreferences, Context context) {
|
||||
final int DEFAULT = Integer.parseInt(context.getResources().getString(R.string.settings_sensor_bluetooth_cycling_speed_wheel_circumference_default));
|
||||
return getInt(context, R.string.settings_sensor_bluetooth_cycling_speed_wheel_circumference_key, DEFAULT);
|
||||
return getInt(sharedPreferences, context, R.string.settings_sensor_bluetooth_cycling_speed_wheel_circumference_key, DEFAULT);
|
||||
}
|
||||
|
||||
public static boolean isBluetoothCyclingPowerSensorAddressNone(Context context) {
|
||||
return isBluetoothSensorAddressNone(context, getBluetoothCyclingPowerSensorAddress(context));
|
||||
public static String getBluetoothCyclingPowerSensorAddress(SharedPreferences sharedPreferences, Context context) {
|
||||
return getString(sharedPreferences, context, R.string.settings_sensor_bluetooth_cycling_power_key, getBluetoothSensorAddressNone(context));
|
||||
}
|
||||
|
||||
public static String getBluetoothCyclingPowerSensorAddress(Context context) {
|
||||
return getString(context, R.string.settings_sensor_bluetooth_cycling_power_key, getBluetoothSensorAddressNone(context));
|
||||
}
|
||||
|
||||
|
||||
public static boolean shouldShowStatsOnLockscreen(Context context) {
|
||||
public static boolean shouldShowStatsOnLockscreen(SharedPreferences sharedPreferences, Context context) {
|
||||
final boolean STATS_SHOW_ON_LOCKSCREEN_DEFAULT = context.getResources().getBoolean(R.bool.stats_show_on_lockscreen_while_recording_default);
|
||||
return getBoolean(context, R.string.stats_show_on_lockscreen_while_recording_key, STATS_SHOW_ON_LOCKSCREEN_DEFAULT);
|
||||
return getBoolean(sharedPreferences, context, R.string.stats_show_on_lockscreen_while_recording_key, STATS_SHOW_ON_LOCKSCREEN_DEFAULT);
|
||||
}
|
||||
|
||||
public static boolean shouldKeepScreenOn(Context context) {
|
||||
public static boolean shouldKeepScreenOn(SharedPreferences sharedPreferences, Context context) {
|
||||
final boolean DEFAULT = context.getResources().getBoolean(R.bool.stats_keep_screen_on_while_recording_default);
|
||||
return getBoolean(context, R.string.stats_keep_screen_on_while_recording_key, DEFAULT);
|
||||
return getBoolean(sharedPreferences, context, R.string.stats_keep_screen_on_while_recording_key, DEFAULT);
|
||||
}
|
||||
|
||||
public static boolean shouldUseFullscreen(Context context) {
|
||||
public static boolean shouldUseFullscreen(SharedPreferences sharedPreferences, Context context) {
|
||||
final boolean DEFAULT = context.getResources().getBoolean(R.bool.stats_fullscreen_while_recording_default);
|
||||
return getBoolean(context, R.string.stats_fullscreen_while_recording_key, DEFAULT);
|
||||
}
|
||||
|
||||
@Deprecated
|
||||
public static boolean isShowStatsElevation(Context context) {
|
||||
return isShowStatsElevation(getSharedPreferences(context), context);
|
||||
return getBoolean(sharedPreferences, context, R.string.stats_fullscreen_while_recording_key, DEFAULT);
|
||||
}
|
||||
|
||||
public static boolean isShowStatsElevation(SharedPreferences sharedPreferences, Context context) {
|
||||
final boolean STATS_SHOW_ELEVATION = context.getResources().getBoolean(R.bool.stats_show_elevation_default);
|
||||
return getBoolean(context, sharedPreferences, R.string.stats_show_grade_elevation_key, STATS_SHOW_ELEVATION);
|
||||
return getBoolean(sharedPreferences, context, R.string.stats_show_grade_elevation_key, STATS_SHOW_ELEVATION);
|
||||
}
|
||||
|
||||
public static boolean isStatsShowCoordinate(SharedPreferences sharedPreferences, Context context) {
|
||||
final boolean STATS_SHOW_COORDINATE = context.getResources().getBoolean(R.bool.stats_show_coordinate_default);
|
||||
return getBoolean(context, R.string.stats_show_coordinate_key, STATS_SHOW_COORDINATE);
|
||||
return getBoolean(sharedPreferences, context, R.string.stats_show_coordinate_key, STATS_SHOW_COORDINATE);
|
||||
}
|
||||
|
||||
public static int getVoiceFrequency(Context context) {
|
||||
public static int getVoiceFrequency(SharedPreferences sharedPreferences, Context context) {
|
||||
final int VOICE_FREQUENCY_DEFAULT = Integer.parseInt(context.getResources().getString(R.string.voice_frequency_default));
|
||||
return getInt(context, R.string.voice_frequency_key, VOICE_FREQUENCY_DEFAULT);
|
||||
return getInt(sharedPreferences, context, R.string.voice_frequency_key, VOICE_FREQUENCY_DEFAULT);
|
||||
}
|
||||
|
||||
public static int getRecordingDistanceInterval(Context context) {
|
||||
return getInt(context, R.string.recording_distance_interval_key, getRecordingDistanceIntervalDefault(context));
|
||||
public static int getRecordingDistanceInterval(SharedPreferences sharedPreferences, Context context) {
|
||||
return getInt(sharedPreferences, context, R.string.recording_distance_interval_key, getRecordingDistanceIntervalDefault(context));
|
||||
}
|
||||
|
||||
public static int getRecordingDistanceIntervalDefault(Context context) {
|
||||
return Integer.parseInt(context.getResources().getString(R.string.recording_distance_interval_default));
|
||||
}
|
||||
|
||||
public static int getMaxRecordingDistance(Context context) {
|
||||
public static int getMaxRecordingDistance(SharedPreferences sharedPreferences, Context context) {
|
||||
final int MAX_RECORDING_DISTANCE = Integer.parseInt(context.getResources().getString(R.string.max_recording_distance_default));
|
||||
return getInt(context, R.string.max_recording_distance_key, MAX_RECORDING_DISTANCE);
|
||||
return getInt(sharedPreferences, context, R.string.max_recording_distance_key, MAX_RECORDING_DISTANCE);
|
||||
}
|
||||
|
||||
public static int getMinRecordingInterval(Context context) {
|
||||
//TODO Duration
|
||||
public static int getMinRecordingInterval(SharedPreferences sharedPreferences, Context context) {
|
||||
final int MIN_RECORDING_INTERVAL = Integer.parseInt(context.getResources().getString(R.string.min_recording_interval_default));
|
||||
return getInt(context, R.string.min_recording_interval_key, MIN_RECORDING_INTERVAL);
|
||||
return getInt(sharedPreferences, context, R.string.min_recording_interval_key, MIN_RECORDING_INTERVAL);
|
||||
}
|
||||
|
||||
public static int getMinRecordingIntervalAdaptAccuracy(Context context) {
|
||||
@@ -350,19 +267,19 @@ public class PreferencesUtils {
|
||||
return Integer.parseInt(context.getResources().getString(R.string.min_recording_interval_default));
|
||||
}
|
||||
|
||||
public static int getRecordingGPSAccuracy(Context context) {
|
||||
public static int getRecordingGPSAccuracy(SharedPreferences sharedPreferences, Context context) {
|
||||
final int RECORDING_GPS_ACCURACY = Integer.parseInt(context.getResources().getString(R.string.recording_gps_accuracy_default));
|
||||
return getInt(context, R.string.recording_gps_accuracy_key, RECORDING_GPS_ACCURACY);
|
||||
return getInt(sharedPreferences, context, R.string.recording_gps_accuracy_key, RECORDING_GPS_ACCURACY);
|
||||
}
|
||||
|
||||
public static boolean shouldInstantExportAfterWorkout(Context context) {
|
||||
public static boolean shouldInstantExportAfterWorkout(SharedPreferences sharedPreferences, Context context) {
|
||||
final boolean INSTANT_POST_WORKOUT_EXPORT_DEFAULT = context.getResources().getBoolean(R.bool.post_workout_export_enabled_default);
|
||||
return getBoolean(context, R.string.post_workout_export_enabled_key, INSTANT_POST_WORKOUT_EXPORT_DEFAULT);
|
||||
return getBoolean(sharedPreferences, context, R.string.post_workout_export_enabled_key, INSTANT_POST_WORKOUT_EXPORT_DEFAULT);
|
||||
}
|
||||
|
||||
public static TrackFileFormat getExportTrackFileFormat(Context context) {
|
||||
final String TRACKFILEFORMAT_NAME_DEFAULT = getString(context, R.string.export_trackfileformat_default, null);
|
||||
String trackFileFormatName = getString(context, R.string.export_trackfileformat_key, TRACKFILEFORMAT_NAME_DEFAULT);
|
||||
public static TrackFileFormat getExportTrackFileFormat(SharedPreferences sharedPreferences, Context context) {
|
||||
final String TRACKFILEFORMAT_NAME_DEFAULT = getString(sharedPreferences, context, R.string.export_trackfileformat_default, null);
|
||||
String trackFileFormatName = getString(sharedPreferences, context, R.string.export_trackfileformat_key, TRACKFILEFORMAT_NAME_DEFAULT);
|
||||
try {
|
||||
return TrackFileFormat.valueOf(trackFileFormatName);
|
||||
} catch (Exception e) {
|
||||
@@ -370,24 +287,24 @@ public class PreferencesUtils {
|
||||
}
|
||||
}
|
||||
|
||||
public static boolean getPreventReimportTracks(Context context) {
|
||||
final boolean defaultValue = getBoolean(context, R.bool.import_prevent_reimport_default, false);
|
||||
return getBoolean(context, R.string.import_prevent_reimport_key, defaultValue);
|
||||
public static boolean getPreventReimportTracks(SharedPreferences sharedPreferences, Context context) {
|
||||
final boolean defaultValue = getBoolean(sharedPreferences, context, R.bool.import_prevent_reimport_default, false);
|
||||
return getBoolean(sharedPreferences, context, R.string.import_prevent_reimport_key, defaultValue);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return {@link androidx.appcompat.app.AppCompatDelegate}.MODE_*
|
||||
*/
|
||||
public static int getDefaultNightMode(Context context) {
|
||||
public static int getDefaultNightMode(SharedPreferences sharedPreferences, Context context) {
|
||||
final String defaultValue = getKey(context, R.string.night_mode_default);
|
||||
final String value = getString(context, R.string.night_mode_key, defaultValue);
|
||||
final String value = getString(sharedPreferences, context, R.string.night_mode_key, defaultValue);
|
||||
|
||||
return Integer.parseInt(value);
|
||||
}
|
||||
|
||||
@Deprecated //Use TrackRecordingService
|
||||
public static boolean isRecording(Context context) {
|
||||
return isRecording(getRecordingTrackId(context));
|
||||
public static boolean isRecording(SharedPreferences sharedPreferences, Context context) {
|
||||
return isRecording(getRecordingTrackId(context, sharedPreferences));
|
||||
}
|
||||
|
||||
@Deprecated
|
||||
@@ -399,16 +316,19 @@ public class PreferencesUtils {
|
||||
return recordingTrackId.getId() != RECORDING_TRACK_ID_DEFAULT;
|
||||
}
|
||||
|
||||
public static void resetPreferences(Context context, boolean readAgain) {
|
||||
public static SharedPreferences resetPreferences(Context context, boolean readAgain) {
|
||||
SharedPreferences sharedPreferences = getSharedPreferences(context);
|
||||
if (readAgain) {
|
||||
// We want to really clear settings now.
|
||||
getSharedPreferences(context).edit().clear().commit();
|
||||
sharedPreferences.edit().clear().commit();
|
||||
}
|
||||
PreferenceManager.setDefaultValues(context, R.xml.settings, readAgain);
|
||||
|
||||
return sharedPreferences;
|
||||
}
|
||||
|
||||
public static DocumentFile getDefaultExportDirectoryUri(Context context) {
|
||||
String singleExportDirectorySettingsKey = getString(context, R.string.settings_default_export_directory_key, null);
|
||||
public static DocumentFile getDefaultExportDirectoryUri(SharedPreferences sharedPreferences, Context context) {
|
||||
String singleExportDirectorySettingsKey = getString(sharedPreferences, context, R.string.settings_default_export_directory_key, null);
|
||||
if (singleExportDirectorySettingsKey == null) {
|
||||
return null;
|
||||
}
|
||||
@@ -420,12 +340,12 @@ public class PreferencesUtils {
|
||||
return null;
|
||||
}
|
||||
|
||||
public static void setDefaultExportDirectoryUri(Context context, Uri directoryUri) {
|
||||
public static void setDefaultExportDirectoryUri(SharedPreferences sharedPreferences, Context context, Uri directoryUri) {
|
||||
String value = directoryUri != null ? directoryUri.toString() : null;
|
||||
setString(context, R.string.settings_default_export_directory_key, value);
|
||||
setString(sharedPreferences, context, R.string.settings_default_export_directory_key, value);
|
||||
}
|
||||
|
||||
public static boolean isDefaultExportDirectoryUri(Context context) {
|
||||
return getDefaultExportDirectoryUri(context) != null;
|
||||
public static boolean isDefaultExportDirectoryUri(SharedPreferences sharedPreferences, Context context) {
|
||||
return getDefaultExportDirectoryUri(sharedPreferences, context) != null;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -41,8 +41,9 @@ public class TrackNameUtils {
|
||||
private TrackNameUtils() {
|
||||
}
|
||||
|
||||
//TODO Should not access sharedPreferences; trackName should be an ENUM.
|
||||
public static String getTrackName(Context context, Track.Id trackId, Instant startTime) {
|
||||
String trackName = PreferencesUtils.getString(context, R.string.track_name_key, context.getString(R.string.track_name_default));
|
||||
String trackName = PreferencesUtils.getString(PreferencesUtils.getSharedPreferences(context), context, R.string.track_name_key, context.getString(R.string.track_name_default));
|
||||
|
||||
if (trackName.equals(context.getString(R.string.settings_recording_track_name_date_local_value))) {
|
||||
return StringUtils.formatDateTime(context, startTime);
|
||||
|
||||
@@ -33,7 +33,7 @@ public class IntervalStatistics {
|
||||
TrackPoint trackPoint = trackPoints.get(i);
|
||||
|
||||
if (trackPoint.hasLocation() && prevTrackPoint.hasLocation()) {
|
||||
interval.distance_m += prevTrackPoint.distanceTo(trackPoint);
|
||||
interval.distance_m += prevTrackPoint.distanceToPrevious(trackPoint);
|
||||
interval.time = interval.time.plus(Duration.between(prevTrackPoint.getTime(), trackPoint.getTime()));
|
||||
interval.gain_m += trackPoint.hasElevationGain() ? trackPoint.getElevationGain() : 0;
|
||||
interval.loss_m += trackPoint.hasElevationLoss() ? trackPoint.getElevationLoss() : 0;
|
||||
|
||||
@@ -19,9 +19,9 @@ public class SensorDataModel {
|
||||
this.sensorValue = StringUtils.formatDecimal(sensorValue, 0);
|
||||
}
|
||||
|
||||
public SensorDataModel(SensorData sensorData) {
|
||||
public SensorDataModel(SensorData<Float> sensorData) {
|
||||
this.sensorName = sensorData.getSensorNameOrAddress();
|
||||
this.sensorValue = sensorData.hasValue() && sensorData.isRecent() ? StringUtils.formatDecimal((float) sensorData.getValue(), 0) : null;
|
||||
this.sensorValue = sensorData.hasValue() && sensorData.isRecent() ? StringUtils.formatDecimal(sensorData.getValue(), 0) : null;
|
||||
if (sensorData instanceof SensorDataHeartRate) {
|
||||
this.labelId = R.string.sensor_state_heart_rate;
|
||||
this.unitId = R.string.sensor_unit_beats_per_minute;
|
||||
|
||||
@@ -25,8 +25,9 @@
|
||||
android:id="@+id/stats_sensor_value"
|
||||
style="@style/StatsLargeValue"
|
||||
android:value="@string/value_unknown"
|
||||
app:layout_constraintHorizontal_chainStyle="packed"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintEnd_toStartOf="@id/stats_sensor_unit"
|
||||
app:layout_constraintTop_toBottomOf="@id/stats_sensor_sensor_value"
|
||||
tools:text="90" />
|
||||
|
||||
@@ -34,8 +35,8 @@
|
||||
android:id="@+id/stats_sensor_unit"
|
||||
style="@style/StatsUnit"
|
||||
tools:text="@string/sensor_unit_beats_per_minute"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintStart_toEndOf="@id/stats_sensor_value"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintTop_toBottomOf="@id/stats_sensor_value" />
|
||||
app:layout_constraintBottom_toBottomOf="@id/stats_sensor_value" />
|
||||
|
||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
||||
@@ -30,91 +30,124 @@
|
||||
android:orientation="vertical"
|
||||
app:layout_constraintGuide_end="8dp" />
|
||||
|
||||
<!-- Activity information: icon, name, description and start datetime -->
|
||||
<ImageView
|
||||
android:id="@+id/stats_activity_type_icon"
|
||||
style="@style/IconStats"
|
||||
app:layout_constraintStart_toEndOf="@id/guideline"
|
||||
app:layout_constraintTop_toTopOf="@id/stats_name_value"
|
||||
tools:src="@drawable/ic_activity_bike_24dp" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/stats_name_value"
|
||||
style="@style/TextLargeStats"
|
||||
android:layout_width="0dp"
|
||||
app:layout_constrainedWidth="true"
|
||||
app:layout_constraintEnd_toStartOf="@id/guideline3"
|
||||
app:layout_constraintStart_toEndOf="@id/stats_activity_type_icon"
|
||||
app:layout_constraintTop_toTopOf="parent"
|
||||
tools:text="Name of the activity" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/stats_description_value"
|
||||
style="@style/TextMediumStats"
|
||||
android:layout_width="0dp"
|
||||
app:layout_constrainedWidth="true"
|
||||
app:layout_constraintEnd_toStartOf="@id/guideline3"
|
||||
app:layout_constraintStart_toStartOf="@id/stats_name_value"
|
||||
app:layout_constraintTop_toBottomOf="@id/stats_name_value"
|
||||
tools:text="Here will be a description that could be really long and it would show completely without problems." />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/stats_start_datetime_value"
|
||||
style="@style/TextSmallStats"
|
||||
android:layout_width="0dp"
|
||||
app:layout_constrainedWidth="true"
|
||||
app:layout_constraintEnd_toStartOf="@id/guideline3"
|
||||
app:layout_constraintStart_toStartOf="@id/guideline"
|
||||
app:layout_constraintTop_toBottomOf="@id/stats_description_barrier"
|
||||
tools:text="Saturday 6th, 16:44" />
|
||||
|
||||
<!-- Barrier -->
|
||||
<androidx.constraintlayout.widget.Barrier
|
||||
android:id="@+id/stats_description_barrier"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
app:barrierDirection="bottom"
|
||||
app:constraint_referenced_ids="stats_activity_type_icon,stats_name_value,stats_description_value" />
|
||||
|
||||
<!-- Horizontal line -->
|
||||
<View
|
||||
android:id="@+id/stats_information_horizontal_line"
|
||||
style="@style/StatsHorizontalLine"
|
||||
android:layout_marginBottom="8dp"
|
||||
app:layout_constraintStart_toEndOf="@id/guideline"
|
||||
app:layout_constraintEnd_toStartOf="@id/guideline3"
|
||||
app:layout_constraintTop_toBottomOf="@id/stats_start_datetime_value" />
|
||||
|
||||
<!-- Distance -->
|
||||
<TextView
|
||||
android:id="@+id/stats_distance_label"
|
||||
style="@style/StatsLargeLabel"
|
||||
android:text="@string/stats_distance"
|
||||
app:layout_constrainedWidth="true"
|
||||
app:layout_constraintStart_toEndOf="@+id/guideline"
|
||||
app:layout_constraintEnd_toStartOf="@+id/guideline2"
|
||||
app:layout_constraintTop_toTopOf="parent" />
|
||||
app:layout_constraintStart_toEndOf="@id/guideline"
|
||||
app:layout_constraintEnd_toStartOf="@id/guideline2"
|
||||
app:layout_constraintTop_toBottomOf="@id/stats_information_horizontal_line" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/stats_distance_value"
|
||||
style="@style/StatsLargeValue"
|
||||
android:value="@string/value_unknown"
|
||||
app:layout_constraintHorizontal_chainStyle="packed"
|
||||
app:layout_constraintStart_toEndOf="@+id/guideline"
|
||||
app:layout_constraintEnd_toStartOf="@+id/stats_distance_unit"
|
||||
app:layout_constraintTop_toBottomOf="@+id/stats_distance_label"
|
||||
app:layout_constraintStart_toEndOf="@id/guideline"
|
||||
app:layout_constraintEnd_toStartOf="@id/stats_distance_unit"
|
||||
app:layout_constraintTop_toBottomOf="@id/stats_distance_label"
|
||||
tools:text="100" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/stats_distance_unit"
|
||||
style="@style/StatsUnit"
|
||||
app:layout_constraintBottom_toBottomOf="@+id/stats_distance_value"
|
||||
app:layout_constraintStart_toEndOf="@+id/stats_distance_value"
|
||||
app:layout_constraintEnd_toStartOf="@+id/guideline2"
|
||||
app:layout_constraintBottom_toBottomOf="@id/stats_distance_value"
|
||||
app:layout_constraintStart_toEndOf="@id/stats_distance_value"
|
||||
app:layout_constraintEnd_toStartOf="@id/guideline2"
|
||||
tools:text="km" />
|
||||
|
||||
<!-- Activity Type -->
|
||||
<TextView
|
||||
android:id="@+id/stats_activity_type_label"
|
||||
style="@style/StatsLargeLabel"
|
||||
android:text="@string/track_edit_activity_type_hint"
|
||||
app:layout_constrainedWidth="true"
|
||||
app:layout_constraintStart_toEndOf="@+id/guideline2"
|
||||
app:layout_constraintEnd_toStartOf="@+id/guideline3"
|
||||
app:layout_constraintTop_toTopOf="parent" />
|
||||
|
||||
<ImageView
|
||||
android:id="@+id/stats_activity_type_icon"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
app:layout_constraintStart_toEndOf="@+id/guideline2"
|
||||
app:layout_constraintEnd_toStartOf="@+id/guideline3"
|
||||
app:layout_constraintBottom_toBottomOf="@+id/stats_distance_value"
|
||||
app:layout_constraintTop_toTopOf="@+id/stats_distance_value"
|
||||
app:tint="?android:textColorPrimary" />
|
||||
|
||||
<!-- Total time -->
|
||||
<TextView
|
||||
android:id="@+id/stats_total_time_label"
|
||||
style="@style/StatsLargeLabel"
|
||||
android:text="@string/stats_total_time"
|
||||
app:layout_constrainedWidth="true"
|
||||
app:layout_constraintStart_toEndOf="@+id/guideline"
|
||||
app:layout_constraintEnd_toStartOf="@+id/guideline2"
|
||||
app:layout_constraintTop_toBottomOf="@+id/stats_distance_value" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/stats_total_time_value"
|
||||
style="@style/StatsLargeValue"
|
||||
app:layout_constraintStart_toEndOf="@+id/guideline"
|
||||
app:layout_constraintEnd_toStartOf="@+id/guideline2"
|
||||
app:layout_constraintTop_toBottomOf="@+id/stats_total_time_label"
|
||||
tools:text="00:00:00" />
|
||||
|
||||
<!-- Moving time -->
|
||||
<TextView
|
||||
android:id="@+id/stats_moving_time_label"
|
||||
style="@style/StatsLargeLabel"
|
||||
android:text="@string/stats_moving_time"
|
||||
app:layout_constrainedWidth="true"
|
||||
app:layout_constraintBottom_toBottomOf="@+id/stats_total_time_label"
|
||||
app:layout_constraintEnd_toStartOf="@+id/guideline3"
|
||||
app:layout_constraintStart_toEndOf="@+id/guideline2"
|
||||
app:layout_constraintTop_toTopOf="@+id/stats_total_time_label"
|
||||
app:layout_constraintVertical_bias="1.0" />
|
||||
app:layout_constraintStart_toEndOf="@id/guideline2"
|
||||
app:layout_constraintEnd_toStartOf="@id/guideline3"
|
||||
app:layout_constraintTop_toBottomOf="@id/stats_information_horizontal_line" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/stats_moving_time_value"
|
||||
style="@style/StatsLargeValue"
|
||||
android:value="@string/value_unknown"
|
||||
app:layout_constraintStart_toEndOf="@+id/guideline2"
|
||||
app:layout_constraintEnd_toStartOf="@+id/guideline3"
|
||||
app:layout_constraintTop_toBottomOf="@+id/stats_moving_time_label"
|
||||
app:layout_constraintStart_toEndOf="@id/guideline2"
|
||||
app:layout_constraintEnd_toStartOf="@id/guideline3"
|
||||
app:layout_constraintTop_toBottomOf="@id/stats_moving_time_label"
|
||||
tools:text="00:00:00" />
|
||||
|
||||
<!-- Total time -->
|
||||
<TextView
|
||||
android:id="@+id/stats_total_time_label"
|
||||
style="@style/StatsSmallLabel"
|
||||
android:text="@string/stats_total_time"
|
||||
app:layout_constrainedWidth="true"
|
||||
app:layout_constraintStart_toEndOf="@id/guideline2"
|
||||
app:layout_constraintEnd_toStartOf="@id/guideline3"
|
||||
app:layout_constraintTop_toBottomOf="@id/stats_moving_time_value" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/stats_total_time_value"
|
||||
style="@style/StatsSmallValue"
|
||||
app:layout_constraintStart_toEndOf="@id/guideline2"
|
||||
app:layout_constraintEnd_toStartOf="@id/guideline3"
|
||||
app:layout_constraintTop_toBottomOf="@id/stats_total_time_label"
|
||||
tools:text="00:00:00" />
|
||||
|
||||
<!-- Barrier -->
|
||||
@@ -130,18 +163,74 @@
|
||||
android:id="@+id/stats_speed_horizontal_line"
|
||||
style="@style/StatsHorizontalLine"
|
||||
android:layout_marginBottom="8dp"
|
||||
app:layout_constraintStart_toEndOf="@+id/guideline"
|
||||
app:layout_constraintEnd_toStartOf="@+id/guideline3"
|
||||
app:layout_constraintTop_toBottomOf="@+id/stats_time_barrier" />
|
||||
app:layout_constraintStart_toEndOf="@id/guideline"
|
||||
app:layout_constraintEnd_toStartOf="@id/guideline3"
|
||||
app:layout_constraintTop_toBottomOf="@id/stats_time_barrier" />
|
||||
|
||||
<!-- Speed/Pace max -->
|
||||
<TextView
|
||||
android:id="@+id/stats_max_speed_label"
|
||||
style="@style/StatsLargeLabel"
|
||||
app:layout_constrainedWidth="true"
|
||||
app:layout_constraintStart_toEndOf="@id/guideline"
|
||||
app:layout_constraintEnd_toStartOf="@id/guideline2"
|
||||
app:layout_constraintTop_toBottomOf="@id/stats_speed_horizontal_line"
|
||||
tools:text="Max. Speed" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/stats_max_speed_value"
|
||||
style="@style/StatsLargeValue"
|
||||
android:value="@string/value_unknown"
|
||||
app:layout_constraintHorizontal_chainStyle="packed"
|
||||
app:layout_constraintStart_toEndOf="@id/guideline"
|
||||
app:layout_constraintEnd_toStartOf="@id/stats_max_speed_unit"
|
||||
app:layout_constraintTop_toBottomOf="@id/stats_max_speed_label"
|
||||
tools:text="30.00" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/stats_max_speed_unit"
|
||||
style="@style/StatsUnit"
|
||||
app:layout_constraintBottom_toBottomOf="@id/stats_max_speed_value"
|
||||
app:layout_constraintStart_toEndOf="@id/stats_max_speed_value"
|
||||
app:layout_constraintEnd_toStartOf="@id/guideline2"
|
||||
tools:text="km/h" />
|
||||
|
||||
<!-- Avg. moving speed/pace -->
|
||||
<TextView
|
||||
android:id="@+id/stats_moving_speed_label"
|
||||
style="@style/StatsLargeLabel"
|
||||
app:layout_constrainedWidth="true"
|
||||
app:layout_constraintStart_toEndOf="@id/guideline2"
|
||||
app:layout_constraintEnd_toStartOf="@id/guideline3"
|
||||
app:layout_constraintTop_toBottomOf="@id/stats_speed_horizontal_line"
|
||||
tools:text="Avg. Moving Speed" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/stats_moving_speed_value"
|
||||
style="@style/StatsLargeValue"
|
||||
android:value="@string/value_unknown"
|
||||
app:layout_constraintHorizontal_chainStyle="packed"
|
||||
app:layout_constraintStart_toEndOf="@id/guideline2"
|
||||
app:layout_constraintEnd_toStartOf="@id/stats_moving_speed_unit"
|
||||
app:layout_constraintTop_toBottomOf="@id/stats_moving_speed_label"
|
||||
tools:text="30.00" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/stats_moving_speed_unit"
|
||||
style="@style/StatsUnit"
|
||||
app:layout_constraintBottom_toBottomOf="@id/stats_moving_speed_value"
|
||||
app:layout_constraintStart_toEndOf="@id/stats_moving_speed_value"
|
||||
app:layout_constraintEnd_toStartOf="@id/guideline3"
|
||||
tools:text="km/h" />
|
||||
|
||||
<!-- Average speed/pace -->
|
||||
<TextView
|
||||
android:id="@+id/stats_average_speed_label"
|
||||
style="@style/StatsSmallLabel"
|
||||
app:layout_constrainedWidth="true"
|
||||
app:layout_constraintStart_toEndOf="@+id/guideline2"
|
||||
app:layout_constraintEnd_toStartOf="@+id/guideline3"
|
||||
app:layout_constraintTop_toBottomOf="@id/stats_speed_horizontal_line"
|
||||
app:layout_constraintStart_toEndOf="@id/guideline2"
|
||||
app:layout_constraintEnd_toStartOf="@id/guideline3"
|
||||
app:layout_constraintTop_toBottomOf="@id/stats_moving_speed_value"
|
||||
tools:text="Avg. Speed" />
|
||||
|
||||
<TextView
|
||||
@@ -149,73 +238,17 @@
|
||||
style="@style/StatsSmallValue"
|
||||
android:value="@string/value_unknown"
|
||||
app:layout_constraintHorizontal_chainStyle="packed"
|
||||
app:layout_constraintStart_toEndOf="@+id/guideline2"
|
||||
app:layout_constraintEnd_toStartOf="@+id/stats_average_speed_unit"
|
||||
app:layout_constraintTop_toBottomOf="@+id/stats_average_speed_label"
|
||||
app:layout_constraintStart_toEndOf="@id/guideline2"
|
||||
app:layout_constraintEnd_toStartOf="@id/stats_average_speed_unit"
|
||||
app:layout_constraintTop_toBottomOf="@id/stats_average_speed_label"
|
||||
tools:text="30.00" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/stats_average_speed_unit"
|
||||
style="@style/StatsUnit"
|
||||
app:layout_constraintBottom_toBottomOf="@+id/stats_average_speed_value"
|
||||
app:layout_constraintStart_toEndOf="@+id/stats_average_speed_value"
|
||||
app:layout_constraintEnd_toStartOf="@+id/guideline3"
|
||||
tools:text="km/h" />
|
||||
|
||||
<!-- Speed max -->
|
||||
<TextView
|
||||
android:id="@+id/stats_max_speed_label"
|
||||
style="@style/StatsSmallLabel"
|
||||
app:layout_constrainedWidth="true"
|
||||
app:layout_constraintStart_toEndOf="@+id/guideline"
|
||||
app:layout_constraintEnd_toStartOf="@+id/guideline2"
|
||||
app:layout_constraintTop_toBottomOf="@id/stats_average_speed_value"
|
||||
tools:text="Max. Speed" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/stats_max_speed_value"
|
||||
style="@style/StatsSmallValue"
|
||||
android:value="@string/value_unknown"
|
||||
app:layout_constraintHorizontal_chainStyle="packed"
|
||||
app:layout_constraintStart_toEndOf="@+id/guideline"
|
||||
app:layout_constraintEnd_toStartOf="@+id/stats_max_speed_unit"
|
||||
app:layout_constraintTop_toBottomOf="@+id/stats_max_speed_label"
|
||||
tools:text="30.00" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/stats_max_speed_unit"
|
||||
style="@style/StatsUnit"
|
||||
app:layout_constraintBottom_toBottomOf="@+id/stats_max_speed_value"
|
||||
app:layout_constraintStart_toEndOf="@+id/stats_max_speed_value"
|
||||
app:layout_constraintEnd_toStartOf="@+id/guideline2"
|
||||
tools:text="km/h" />
|
||||
|
||||
<!-- Moving speed max -->
|
||||
<TextView
|
||||
android:id="@+id/stats_moving_speed_label"
|
||||
style="@style/StatsSmallLabel"
|
||||
app:layout_constrainedWidth="true"
|
||||
app:layout_constraintStart_toEndOf="@+id/guideline2"
|
||||
app:layout_constraintEnd_toStartOf="@+id/guideline3"
|
||||
app:layout_constraintTop_toBottomOf="@id/stats_average_speed_value"
|
||||
tools:text="Max. Moving Speed" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/stats_moving_speed_value"
|
||||
style="@style/StatsSmallValue"
|
||||
android:value="@string/value_unknown"
|
||||
app:layout_constraintHorizontal_chainStyle="packed"
|
||||
app:layout_constraintStart_toEndOf="@+id/guideline2"
|
||||
app:layout_constraintEnd_toStartOf="@+id/stats_moving_speed_unit"
|
||||
app:layout_constraintTop_toBottomOf="@+id/stats_moving_speed_label"
|
||||
tools:text="30.00" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/stats_moving_speed_unit"
|
||||
style="@style/StatsUnit"
|
||||
app:layout_constraintBottom_toBottomOf="@+id/stats_moving_speed_value"
|
||||
app:layout_constraintStart_toEndOf="@+id/stats_moving_speed_value"
|
||||
app:layout_constraintEnd_toStartOf="@+id/guideline3"
|
||||
app:layout_constraintBottom_toBottomOf="@id/stats_average_speed_value"
|
||||
app:layout_constraintStart_toEndOf="@id/stats_average_speed_value"
|
||||
app:layout_constraintEnd_toStartOf="@id/guideline3"
|
||||
tools:text="km/h" />
|
||||
|
||||
<!-- Barrier -->
|
||||
@@ -224,16 +257,16 @@
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
app:barrierDirection="bottom"
|
||||
app:constraint_referenced_ids="stats_moving_speed_value,stats_max_speed_value" />
|
||||
app:constraint_referenced_ids="stats_max_speed_value,stats_moving_speed_value,stats_average_speed_value" />
|
||||
|
||||
<!-- Horizontal line -->
|
||||
<View
|
||||
android:id="@+id/stats_elevation_horizontal_line"
|
||||
style="@style/StatsHorizontalLine"
|
||||
android:layout_marginBottom="8dp"
|
||||
app:layout_constraintStart_toEndOf="@+id/guideline"
|
||||
app:layout_constraintEnd_toStartOf="@+id/guideline3"
|
||||
app:layout_constraintTop_toBottomOf="@+id/stats_speed_barrier" />
|
||||
app:layout_constraintStart_toEndOf="@id/guideline"
|
||||
app:layout_constraintEnd_toStartOf="@id/guideline3"
|
||||
app:layout_constraintTop_toBottomOf="@id/stats_speed_barrier" />
|
||||
|
||||
<!-- Elevation gain and loss -->
|
||||
<androidx.constraintlayout.widget.Group
|
||||
@@ -244,56 +277,56 @@
|
||||
|
||||
<TextView
|
||||
android:id="@+id/stats_elevation_gain_label"
|
||||
style="@style/StatsSmallLabel"
|
||||
style="@style/StatsLargeLabel"
|
||||
android:text="@string/stats_gain"
|
||||
app:layout_constrainedWidth="true"
|
||||
app:layout_constraintStart_toEndOf="@+id/guideline"
|
||||
app:layout_constraintEnd_toStartOf="@+id/guideline2"
|
||||
app:layout_constraintStart_toEndOf="@id/guideline"
|
||||
app:layout_constraintEnd_toStartOf="@id/guideline2"
|
||||
app:layout_constraintTop_toBottomOf="@id/stats_elevation_horizontal_line" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/stats_elevation_gain_value"
|
||||
style="@style/StatsSmallValue"
|
||||
style="@style/StatsLargeValue"
|
||||
android:value="@string/value_unknown"
|
||||
app:layout_constraintHorizontal_chainStyle="packed"
|
||||
app:layout_constraintStart_toEndOf="@+id/guideline"
|
||||
app:layout_constraintEnd_toStartOf="@+id/stats_elevation_gain_unit"
|
||||
app:layout_constraintTop_toBottomOf="@+id/stats_elevation_gain_label"
|
||||
app:layout_constraintStart_toEndOf="@id/guideline"
|
||||
app:layout_constraintEnd_toStartOf="@id/stats_elevation_gain_unit"
|
||||
app:layout_constraintTop_toBottomOf="@id/stats_elevation_gain_label"
|
||||
tools:text="100" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/stats_elevation_gain_unit"
|
||||
style="@style/StatsUnit"
|
||||
app:layout_constraintBottom_toBottomOf="@+id/stats_elevation_gain_value"
|
||||
app:layout_constraintStart_toEndOf="@+id/stats_elevation_gain_value"
|
||||
app:layout_constraintEnd_toStartOf="@+id/guideline2"
|
||||
app:layout_constraintBottom_toBottomOf="@id/stats_elevation_gain_value"
|
||||
app:layout_constraintStart_toEndOf="@id/stats_elevation_gain_value"
|
||||
app:layout_constraintEnd_toStartOf="@id/guideline2"
|
||||
tools:text="m" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/stats_elevation_loss_label"
|
||||
style="@style/StatsSmallLabel"
|
||||
style="@style/StatsLargeLabel"
|
||||
android:text="@string/stats_loss"
|
||||
app:layout_constrainedWidth="true"
|
||||
app:layout_constraintStart_toEndOf="@+id/guideline2"
|
||||
app:layout_constraintEnd_toStartOf="@+id/guideline3"
|
||||
app:layout_constraintStart_toEndOf="@id/guideline2"
|
||||
app:layout_constraintEnd_toStartOf="@id/guideline3"
|
||||
app:layout_constraintTop_toBottomOf="@id/stats_elevation_horizontal_line" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/stats_elevation_loss_value"
|
||||
style="@style/StatsSmallValue"
|
||||
style="@style/StatsLargeValue"
|
||||
android:value="@string/value_unknown"
|
||||
app:layout_constraintHorizontal_chainStyle="packed"
|
||||
app:layout_constraintStart_toEndOf="@+id/guideline2"
|
||||
app:layout_constraintEnd_toStartOf="@+id/stats_elevation_loss_unit"
|
||||
app:layout_constraintTop_toBottomOf="@+id/stats_elevation_loss_label"
|
||||
app:layout_constraintStart_toEndOf="@id/guideline2"
|
||||
app:layout_constraintEnd_toStartOf="@id/stats_elevation_loss_unit"
|
||||
app:layout_constraintTop_toBottomOf="@id/stats_elevation_loss_label"
|
||||
tools:text="100" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/stats_elevation_loss_unit"
|
||||
style="@style/StatsUnit"
|
||||
app:layout_constraintBottom_toBottomOf="@+id/stats_elevation_loss_value"
|
||||
app:layout_constraintStart_toEndOf="@+id/stats_elevation_loss_value"
|
||||
app:layout_constraintEnd_toStartOf="@+id/guideline3"
|
||||
app:layout_constraintBottom_toBottomOf="@id/stats_elevation_loss_value"
|
||||
app:layout_constraintStart_toEndOf="@id/stats_elevation_loss_value"
|
||||
app:layout_constraintEnd_toStartOf="@id/guideline3"
|
||||
tools:text="m" />
|
||||
|
||||
<!-- Barrier -->
|
||||
@@ -309,9 +342,9 @@
|
||||
android:id="@+id/stats_end_horizontal_line"
|
||||
style="@style/StatsHorizontalLine"
|
||||
android:layout_marginBottom="8dp"
|
||||
app:layout_constraintStart_toEndOf="@+id/guideline"
|
||||
app:layout_constraintEnd_toStartOf="@+id/guideline3"
|
||||
app:layout_constraintTop_toBottomOf="@+id/stats_elevation_barrier" />
|
||||
app:layout_constraintStart_toEndOf="@id/guideline"
|
||||
app:layout_constraintEnd_toStartOf="@id/guideline3"
|
||||
app:layout_constraintTop_toBottomOf="@id/stats_elevation_barrier" />
|
||||
|
||||
<!-- Sensors data -->
|
||||
<androidx.recyclerview.widget.RecyclerView
|
||||
|
||||
@@ -16,9 +16,9 @@ limitations under the License.
|
||||
<menu xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto">
|
||||
<item
|
||||
android:id="@+id/track_list_search"
|
||||
android:title="@string/menu_search"
|
||||
app:actionViewClass="androidx.appcompat.widget.SearchView"
|
||||
android:id="@+id/track_list_aggregated_stats"
|
||||
android:icon="@drawable/ic_statistics_24dp"
|
||||
android:title="@string/menu_aggregated_statistics"
|
||||
app:showAsAction="ifRoom" />
|
||||
<!-- Icon and title is overwritten by TrackListActivity -->
|
||||
<!-- Hide icon as workaround for https://github.com/OpenTracksApp/OpenTracks/issues/200 -->
|
||||
@@ -29,9 +29,9 @@ limitations under the License.
|
||||
android:visible="false"
|
||||
app:showAsAction="ifRoom" />
|
||||
<item
|
||||
android:id="@+id/track_list_aggregated_stats"
|
||||
android:icon="@drawable/ic_statistics_24dp"
|
||||
android:title="@string/menu_aggregated_statistics"
|
||||
android:id="@+id/track_list_search"
|
||||
android:title="@string/menu_search"
|
||||
app:actionViewClass="androidx.appcompat.widget.SearchView"
|
||||
app:showAsAction="ifRoom" />
|
||||
<item
|
||||
android:id="@+id/track_list_markers"
|
||||
|
||||
@@ -32,45 +32,37 @@ limitations under the License.
|
||||
* vis spor på et kort (se nedenfor)</string>
|
||||
<string name="help_track_map_title">Hvordan viser man et spor på et kort?</string>
|
||||
<string name="help_track_map_content">" Brug det lille kortikon.
|
||||
\n a) (live opdateringer) Installer <a href=\"https://github.com/OpenTracksApp/OSMDashboard\"> OSMDashboard </a>. Det giver online eller offline kort via <a href=\"https://www.openstreetmap.org\"> OpenStreetMap </a>.
|
||||
\n a) (live opdateringer) Installer <a href=\"https://github.com/OpenTracksApp/OSMDashboard\"> OSMDashboard</a>. Det giver online eller offline kort via <a href=\"https://www.openstreetmap.org\">OpenStreetMap</a>.
|
||||
\n Faktisk er OSMDashboard en separat app, der kun kan få adgang (midlertidigt) til dataene for spor, der skal vises.
|
||||
\n b) Via en normal kortapplikation (f.eks. <a href=\"http://osmand.net/\"> OsmAnd </a> og <a href=\"https://maps.me/\"> MAPS.ME < / a>). Disse apps skal understøtte KMZ-filformatet (kun placeringer og tidsstempler). Faktisk skal en app understøtte <i> ACTION_VIEW </i> med MIME <i> -applikation / vnd.google-earth.kmz </i>. "</string>
|
||||
\n b) Via en normal kortapplikation (f.e., <a href=\"http://osmand.net/\">OsmAnd</a> og <a href=\"https://maps.me/\">MAPS.ME< / a>). Disse apps skal understøtte KMZ fil formatet (kun placeringer og tidsstempler). Faktisk, skal en app understøtte <i>ACTION_VIEW</i> med MIME <i>applikation/vnd.google-earth.kmz </i>. "</string>
|
||||
<string name="help_storage_pictures_title">Hvordan gemmer OpenTracks billeder, der blev taget som markører?</string>
|
||||
<string name="help_storage_pictures_content">Disse gemmes i appens interne lager (dvs. kun tilgængelig inden for OpenTracks).</string>
|
||||
<string name="help_track_export_title">Hvordan kan jeg eksportere / dele indspillede numre?</string>
|
||||
<string name="help_track_export_content">
|
||||
Spor kan eksporteres fra indstillingerne som KMZ, KML eller GPX.
|
||||
KMZ / KML alle data; GPX kun placeringer.
|
||||
Spor, der deles via <i>delingsikonet</i> deles som KMZ.</string>
|
||||
<string name="help_track_export_content"> Spor kan eksporteres fra indstillingerne som KMZ, KML eller GPX. KMZ / KML alle data; GPX kun placeringer. Spor, der deles via <i>delingsikonet</i> deles som KMZ. </string>
|
||||
<string name="help_data_google_play_services_title">Kræver/bruger OpenTracks Google Play Service?</string>
|
||||
<string name="help_recording_title">Hvordan registrerer OpenTracks data?</string>
|
||||
<string name="help_recording_content">
|
||||
OpenTracks bruger enhedens GPS-sensor til at registrere placeringen kontinuerligt.
|
||||
Hvis GPS-enheden rapporterer unøjagtige data (f.eks. Placering, hastighed, højde), kan OpenTracks ikke gøre noget ved det.</string>
|
||||
<string name="help_data_storage_title">Hvordan gemmer OpenTracks sensordata?</string>
|
||||
<string name="help_data_storage_content">
|
||||
Sensordata gemmes pr. Optaget placering.
|
||||
Med hensyn til en pulssensor betyder det, at der kun er <i> en </i> måling af hjertefrekvensen lagret pr. Placering.
|
||||
Hvis du står stille eller er indendørs, registreres ingen sensordata (men de vil blive vist).</string>
|
||||
<string name="help_data_storage_content"> Sensordata gemmes pr. Optaget placering. Med hensyn til en pulssensor betyder det, at der kun er <i> en </i> måling af hjertefrekvensen lagret pr. Placering. Hvis du står stille eller er indendørs, registreres ingen sensordata (men de vil blive vist). </string>
|
||||
<string name="help_gps_troubleshooting_title">OpenTracks optager ikke placeringer?</string>
|
||||
<string name="help_gps_troubleshooting_content">
|
||||
Hvad du kan gøre:\n
|
||||
a) Kontroller, om GPS er aktiveret,\n
|
||||
b) forbedre GPS-modtagelse (f.eks. gå udendørs), \n
|
||||
c) Kontroller, om du har appen <a href="https://f-droid.org/en/packages/com.android.gpstest.osmdroid/"> GPSTest </a> og \n
|
||||
d) deaktiver strømbesparelse. \n
|
||||
<string name="help_gps_troubleshooting_content"> Hvad du kan gøre:
|
||||
\n a) Kontroller, om GPS er aktiveret,
|
||||
\n b) forbedre GPS-modtagelse (f.eks. gå udendørs),
|
||||
\n c) Kontroller, om du har appen <a href="https://f-droid.org/en/packages/com.android.gpstest.osmdroid/"> GPSTest </a> og
|
||||
\n d) deaktiver strømbesparelse.
|
||||
\n
|
||||
Hvis alt ikke hjælper, skal du rapportere en fejl.</string>
|
||||
\n Hvis alt ikke hjælper, skal du rapportere en fejl. </string>
|
||||
<string name="help_support_title">Hvordan kan jeg støtte OpenTracks?</string>
|
||||
<string name="help_support_content">
|
||||
Du kan: \n
|
||||
a) sprede ordet om OpenTracks, \n
|
||||
b) <a href="https://github.com/OpenTracksApp/OpenTracks/issues"> foreslå funktioner </a>, \n
|
||||
c) <a href="https://github.com/OpenTracksApp/OpenTracks/issues"> rapportfejl </a>, \n
|
||||
d) hjælp til softwareudvikling, \n
|
||||
e) køb i <a href="https://play.google.com/store/apps/details?id=de.dennisguse.opentracks.playstore"> Google Play Store </a>, \n
|
||||
f) donere, og \n
|
||||
g) gør hvad du synes hjælper OpenTracks.</string>
|
||||
<string name="help_support_content"> Du kan:
|
||||
\n a) sprede ordet om OpenTracks,
|
||||
\n b) <a href="https://github.com/OpenTracksApp/OpenTracks/issues"> foreslå funktioner </a>,
|
||||
\n c) <a href="https://github.com/OpenTracksApp/OpenTracks/issues"> rapportfejl </a>,
|
||||
\n d) hjælp til softwareudvikling,
|
||||
\n e) køb i <a href="https://play.google.com/store/apps/details?id=de.dennisguse.opentracks.playstore"> Google Play Store </a>,
|
||||
\n f) donere, og
|
||||
\n g) gør hvad du synes hjælper OpenTracks. </string>
|
||||
<string name="open_track_using_dashboard">Åben med OpenTracks-Dashboard</string>
|
||||
<string name="open_track_as_KMZ">Åben som KMZ</string>
|
||||
<string name="open_track_as_GPX">Åben som GPX</string>
|
||||
@@ -135,9 +127,9 @@ g) gør hvad du synes hjælper OpenTracks.</string>
|
||||
<string name="activity_type_trail_hiking">vandretur</string>
|
||||
<string name="activity_type_trail_running">løb på sti</string>
|
||||
<string name="activity_type_train">tog</string>
|
||||
<string name="activity_type_ultimate_frisbee">ultimate frisbee</string>
|
||||
<string name="activity_type_ultimate_frisbee">ultimativ frisbee</string>
|
||||
<string name="activity_type_unknown">ukendt</string>
|
||||
<string name="activity_type_wakeboarding">wakeboarding</string>
|
||||
<string name="activity_type_wakeboarding">vågneboarding</string>
|
||||
<string name="activity_type_walking">til fods</string>
|
||||
<string name="activity_type_water_skiing">vandski</string>
|
||||
<string name="activity_type_wind_surfing">windsurfing</string>
|
||||
@@ -178,7 +170,7 @@ g) gør hvad du synes hjælper OpenTracks.</string>
|
||||
<string name="description_total_distance">Samlet afstand: %1$.2f km (%2$.1f mil)</string>
|
||||
<string name="description_total_time">Samlet tid: %1$s</string>
|
||||
<string name="description_track_point">Punkt</string>
|
||||
<string name="description_track_segment">Segment</string>
|
||||
<string name="description_track_segment">segment</string>
|
||||
<!-- Export -->
|
||||
<string name="export_all_option">Alle som %1$s</string>
|
||||
<string name="export_all_title">Eksportér til eksternt lager</string>
|
||||
@@ -347,7 +339,7 @@ g) gør hvad du synes hjælper OpenTracks.</string>
|
||||
<string name="settings_reset_done">Alle indstillinger er blevet gendannet til standardværdierne</string>
|
||||
<string name="settings_error_initial_values_restored">Beklager, men der opstod en fejl. Alle indstillinger er vendt tilbage til standardværdierne.</string>
|
||||
<!-- Settings Sensor -->
|
||||
<string name="settings_sensor">Sensor</string>
|
||||
<string name="settings_sensor">sensor</string>
|
||||
<string name="settings_sensor_bluetooth">Bluetooth</string>
|
||||
<string name="settings_sensor_bluetooth_sensor">Bluetooth-sensor</string>
|
||||
<string name="settings_sensor_connecting">Tilsluttet %1$s</string>
|
||||
@@ -360,8 +352,9 @@ g) gør hvad du synes hjælper OpenTracks.</string>
|
||||
<string name="settings_stats_units_title">Foretrukne enheder</string>
|
||||
<!-- Share Track -->
|
||||
<string name="share_track_share_file">Del rutefil</string>
|
||||
<string name="share_track_share_file_body">Du kunne måske være interesseret i denne rute.\n\nDu kan bruge Google Earth eller anden
|
||||
kortlægningssoftware til at se ruten.\n\n%1$s</string>
|
||||
<string name="share_track_share_file_body">Jeg tror, du måske er interesseret i dette nummer.
|
||||
\n
|
||||
\n%1$s</string>
|
||||
<string name="share_track_subject">Jeg vil gerne dele en rute med dig</string>
|
||||
<!-- Stats -->
|
||||
<string name="stats_average_moving_pace">Gns. bevægelsestempo</string>
|
||||
@@ -440,9 +433,9 @@ g) gør hvad du synes hjælper OpenTracks.</string>
|
||||
<string name="value_integer_meter_recommended">%1$d m (anbefalet)</string>
|
||||
<string name="value_integer_mile">%1$d mil</string>
|
||||
<string name="value_integer_mile_hour">%1$d mil/t</string>
|
||||
<string name="value_integer_minute">%1$d min.</string>
|
||||
<string name="value_integer_minute">%1$d min</string>
|
||||
<string name="value_integer_percent">%1$d %%</string>
|
||||
<string name="value_integer_second">%1$d sek.</string>
|
||||
<string name="value_integer_second">%1$d sek</string>
|
||||
<string name="value_none">Ingen</string>
|
||||
<string name="value_off">Fra</string>
|
||||
<string name="value_smallest">Mindste</string>
|
||||
@@ -533,13 +526,13 @@ g) gør hvad du synes hjælper OpenTracks.</string>
|
||||
<string name="waypoint_type_pothole">hul i vejen</string>
|
||||
<string name="waypoint_type_railway_crossing">jernbaneoverskæring</string>
|
||||
<string name="waypoint_type_restaurant">restaurant</string>
|
||||
<string name="waypoint_type_shopping">shopping</string>
|
||||
<string name="waypoint_type_shopping">handle ind</string>
|
||||
<string name="waypoint_type_sight">udsigt</string>
|
||||
<string name="waypoint_type_store">butik</string>
|
||||
<string name="waypoint_type_swimming_pool">swimmingpool</string>
|
||||
<string name="waypoint_type_telephone">telefon</string>
|
||||
<string name="waypoint_type_theater">teater</string>
|
||||
<string name="waypoint_type_toilet">toilet</string>
|
||||
<string name="waypoint_type_toilet">toilette</string>
|
||||
<string name="waypoint_type_tourist_information">turistinformation</string>
|
||||
<string name="waypoint_type_tree">træ</string>
|
||||
<string name="waypoint_type_tunnel">tunnel</string>
|
||||
@@ -549,4 +542,72 @@ g) gør hvad du synes hjælper OpenTracks.</string>
|
||||
<!-- Share image -->
|
||||
<string name="share_image_subject">Jeg vil gerne dele et billede med dig</string>
|
||||
<string name="share_image_body">Jeg tror, du måske er interesseret i dette billede.</string>
|
||||
<string name="open_track_as_trackfileformat">Åbn som %1$s</string>
|
||||
<string name="activity_type_escooter">elektrisk scooter</string>
|
||||
<string name="generic_click_twice_cancel">Klik tilbage igen for at annullere</string>
|
||||
<string name="value_float_kilometer_hour">%1$.2f km/t</string>
|
||||
<string name="import_unable_to_import_file">Kunne ikke importere filen:%1$s</string>
|
||||
<string name="value_pace_mile">%1$d:%2$02d min/mi</string>
|
||||
<string name="value_pace_kilometer">%1$d:%2$02d min/km</string>
|
||||
<string name="import_prevent_reimport">Du har indstillet forhindre genimport af spor</string>
|
||||
<string name="settings_default_export_uri_title">Spor eksportmappe</string>
|
||||
<string name="instant_export_enabled_summary">Eksporter spor til lagring, når optagelsen er færdig</string>
|
||||
<string name="export_progress_summary_new_msg">Nye spor eksporteret</string>
|
||||
<string name="instant_export_enabled_title">Øjeblikkelig eksport efter træning</string>
|
||||
<string name="toast_message_instant_track_was_exported">Sidste spor eksporteret</string>
|
||||
<string name="generic_separator_done_total">/</string>
|
||||
<string name="generic_skip">Springe</string>
|
||||
<string name="description_elevation_loss">Højdetab: %1$d m (%2$d ft)</string>
|
||||
<string name="export_error_post_workout">Eksport efter træning mislykkedes. Kontroller eksportmappen.</string>
|
||||
<string name="export_track_already_exists_msg">Sporet %1$s findes allerede i destinationsmappen.</string>
|
||||
<string name="export_all_do_it_for_all">gør det for alle konflikter</string>
|
||||
<string name="export_track_errors">Spor ikke eksporteret</string>
|
||||
<string name="gps_disabled_msg">GPS deaktiveret</string>
|
||||
<string name="gps_fixed_and_ready">GPS fast og klar</string>
|
||||
<string name="import_no_kml_file_found">Ingen doc.kml-fil fundet i KMZ</string>
|
||||
<string name="sensor_state_cadence_avg">Gennemsnitlig kadence</string>
|
||||
<string name="sensor_state_cadence_max">Max kadence</string>
|
||||
<string name="sensor_state_heart_rate_avg">Gns. Puls</string>
|
||||
<string name="sensor_state_heart_rate_max">Maks. Puls</string>
|
||||
<string name="sensor_state_power_avg">Gennemsnitlig strøm</string>
|
||||
<string name="settings_default_trackfileformat">Eksport/del filformat</string>
|
||||
<string name="settings_recording_fullscreen_on_while_recording_title">Fuld skærm</string>
|
||||
<string name="settings_recording_fullscreen_on_while_recording_summary">Under optagelse, vises på fuld skærm.</string>
|
||||
<string name="settings_night_mode_title">UI Tema</string>
|
||||
<string name="settings_night_mode_option_system">Ordning</string>
|
||||
<string name="settings_night_mode_option_day">Dag</string>
|
||||
<string name="settings_night_mode_option_night">Nat</string>
|
||||
<string name="settings_sensor_wheel_circumference">Hjulets omkreds (mm)</string>
|
||||
<string name="stats_split_interval">Split, divide</string>
|
||||
<string name="stats_intervals">divides</string>
|
||||
<string name="interval_list_empty_message">Der er ikke noget interval endnu</string>
|
||||
<string name="aggregated_stats_empty_message">Optag dit første spor for at se aggregerede statistikker</string>
|
||||
<string name="track_detail_intervals_tab">Intervaller</string>
|
||||
<string name="value_float_mile_hour">%1$.2f mph</string>
|
||||
<string name="voice_pace_lap">Omgangstid på %1$s</string>
|
||||
<string name="voice_speed_lap">Omgangshastighed på %1$s</string>
|
||||
<string name="hold_to_pause">Hold nede for at holde pause</string>
|
||||
<string name="hold_to_stop">Hold for at stoppe</string>
|
||||
<string name="settings_prevent_reimport_tracks_title">Forhindre genimport af spor</string>
|
||||
<plurals name="generic_completed_with_errors">
|
||||
<item quantity="one">Afsluttet med en fejl</item>
|
||||
<item quantity="other">Afsluttet med %1$d fejl</item>
|
||||
</plurals>
|
||||
<string name="stats_loss">Tab</string>
|
||||
<string name="export_progress_summary_overwrite_msg">Spor overskrevet</string>
|
||||
<string name="export_progress_summary_skip_msg">Spor sprunget over</string>
|
||||
<string name="export_progress_summary_errors_msg">Fejl</string>
|
||||
<string name="generic_open_track">Åbn spor</string>
|
||||
<string name="generic_completed">Afsluttet</string>
|
||||
<string name="import_thread_interrupted">Tråden afbrudt</string>
|
||||
<string name="import_unsupported_format">Ikke-understøttet filformat</string>
|
||||
<string name="import_parser_error">Parserfejl:%1$s</string>
|
||||
<string name="import_error_list_dialog_title">Filer ikke importeret</string>
|
||||
<string name="import_progress_summary_ok_msg">Importerede filer</string>
|
||||
<string name="import_progress_summary_exists_msg">Eksisterer allerede</string>
|
||||
<string name="import_progress_summary_errors_msg">Fejl</string>
|
||||
<string name="import_file_imported">Filen%1$s er importeret</string>
|
||||
<string name="import_error_info">%1$s: %2$s</string>
|
||||
<string name="generic_show_errors">Vis fejl</string>
|
||||
<string name="generic_overwrite">Overskriv</string>
|
||||
</resources>
|
||||
@@ -1,4 +1,5 @@
|
||||
<?xml version="1.0" encoding="utf-8"?><!--
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!--
|
||||
Copyright 2010 Google Inc.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
@@ -12,8 +13,7 @@ distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
-->
|
||||
<resources>
|
||||
--><resources>
|
||||
<string name="about_preference_title">Über OpenTracks</string>
|
||||
<string name="about_description"><i>OpenTracks</i> ist eine Sport-Tracking-Anwendung, die Ihre Privatsphäre vollständig respektiert.</string>
|
||||
<string name="about_url">Projekt-Website: <a href="%1$s">%1$s</a></string>
|
||||
@@ -30,7 +30,6 @@ limitations under the License.
|
||||
* Markierungen erstellen (inkl. Bilder)\n
|
||||
* arbeitet vollständig offline\n
|
||||
* Tracks auf einer Karte anzeigen (siehe unten)</string>
|
||||
|
||||
<string name="help_track_map_title">Wie wird ein Track auf einer Karte angezeigt?</string>
|
||||
<string name="help_track_map_content">
|
||||
Verwende das kleine Kartensymbol.\n
|
||||
@@ -44,39 +43,28 @@ b)
|
||||
Über eine normale Kartenanwendung (z.B. <a href="http://osmand.net/">OsmAnd</a> und <a href="https://maps.me/">MAPS.ME</a>).
|
||||
Diese Anwendungen müssen das KMZ-Dateiformat unterstützen (nur Orte und Zeitstempel).
|
||||
In der Tat muss eine App <i>ACTION_VIEW</i> mit MIME <i>application/vnd.google-earth.kmz</i> unterstützen.</string>
|
||||
|
||||
<string name="help_storage_pictures_title">Wie speichert OpenTracks Bilder, die als Markierungen aufgenommen wurden?</string>
|
||||
<string name="help_storage_pictures_content">Diese werden App-intern gespeichert (d.h. nur innerhalb von OpenTracks zugänglich).</string>
|
||||
|
||||
<string name="help_track_export_title">Wie kann ich aufgezeichnete Tracks exportieren/teilen?</string>
|
||||
<string name="help_track_export_content">
|
||||
Tracks können aus den Einstellungen als KMZ, KML oder GPX exportiert werden.
|
||||
KMZ/KML alle Daten; GPX nur Standorte.
|
||||
Tracks, die über das Symbol <i>Teilen Icon</i> freigegeben werden, werden als KMZ freigegeben.</string>
|
||||
|
||||
<string name="help_track_export_content"> Tracks können aus den Einstellungen als KMZ, KML oder GPX exportiert werden. KMZ/KML alle Daten; GPX nur Standorte. Tracks, die über das Symbol <i>Teilen Icon</i> freigegeben werden, werden als KMZ freigegeben. </string>
|
||||
<string name="help_data_google_play_services_title">Benötigt/benutzt OpenTracks den Google Play Service?</string>
|
||||
|
||||
<string name="help_recording_title">Wie zeichnet OpenTracks Daten auf?</string>
|
||||
<string name="help_recording_content">
|
||||
OpenTracks verwendet den GPS-Sensor des Geräts, um den Standort kontinuierlich aufzuzeichnen.
|
||||
Meldet das GPS-Gerät ungenaue Daten (z.B. Standort, Geschwindigkeit, Höhe), kann OpenTracks nichts dagegen unternehmen.</string>
|
||||
|
||||
<string name="help_data_storage_title">Wie speichert OpenTracks Sensordaten?</string>
|
||||
<string name="help_data_storage_content">
|
||||
Sensordaten werden pro aufgezeichneter Stelle gespeichert.
|
||||
Bezogen auf einen Herzfrequenzsensor bedeutet dies, dass nur <i>eine</i>Herzfrequenzmessung pro Ort gespeichert wird.
|
||||
Wenn Du still stehst oder du dich in geschlossenen Räumen aufhälst, werden keine Sensordaten aufgezeichnet (aber angezeigt).</string>
|
||||
|
||||
<string name="help_gps_troubleshooting_title">OpenTracks zeichnet keine Standorte auf?</string>
|
||||
<string name="help_gps_troubleshooting_content">
|
||||
Was Du tun kannst:\n
|
||||
a) prüfen, ob GPS aktiviert ist,\n
|
||||
b) den GPS-Empfang verbessern (z.B. ins Freie gehen),\n
|
||||
c) prüfen, ob mit <a href="https://f-droid.org/en/packages/com.android.gpstest.osmdroid/">GPSTest</a> app, und\n
|
||||
d) Energiesparen deaktivieren.\n
|
||||
<string name="help_gps_troubleshooting_content"> Was Du tun kannst:
|
||||
\n a) prüfen, ob GPS aktiviert ist,
|
||||
\n b) den GPS-Empfang verbessern (z.B. ins Freie gehen),
|
||||
\n c) mit der <a href="https://f-droid.org/en/packages/com.android.gpstest.osmdroid/">GPSTest</a>-App das GPS testen, und
|
||||
\n d) Energiesparen deaktivieren.
|
||||
\n
|
||||
Wenn alles nicht hilft, melde bitte einen Fehler.</string>
|
||||
|
||||
\n Wenn das alles nicht hilft, melde bitte einen Fehler. </string>
|
||||
<string name="help_support_title">Wie kann ich OpenTracks unterstützen?</string>
|
||||
<string name="help_support_content">
|
||||
Du kannst:\n
|
||||
@@ -87,12 +75,10 @@ d) Hilfe bei der Software-Entwicklung,\n
|
||||
e) kaufen in <a href="https://play.google.com/store/apps/details?id=de.dennisguse.opentracks.playstore">Google Play Store</a>,\n
|
||||
f) spenden, und\n
|
||||
g) das tun, was Deiner Meinung nach OpenTracks hilft.</string>
|
||||
|
||||
<string name="open_track_using_dashboard">Öffne mithilfe des OpenTracks-Dashboard</string>
|
||||
<string name="open_track_as_KMZ">Als KMZ öffnen</string>
|
||||
<string name="open_track_as_GPX">Als GPX öffnen</string>
|
||||
<string name="open_track_as_trackfileformat">Als %1$s öffnen</string>
|
||||
|
||||
<!-- Activity Type -->
|
||||
<string name="activity_type_airplane">Flugzeug</string>
|
||||
<string name="activity_type_atv">Quad</string>
|
||||
@@ -370,21 +356,20 @@ g) das tun, was Deiner Meinung nach OpenTracks hilft.</string>
|
||||
<string name="settings_sensor_bluetooth_sensor">Bluetooth-Sensor</string>
|
||||
<string name="settings_sensor_connecting">Verbunden mit %1$s</string>
|
||||
<string name="settings_sensor_disconnected">Verbindung getrennt von %1$s</string>
|
||||
|
||||
<!-- Settings Stats -->
|
||||
<string name="settings_stats_coordinate">Breiten-/Längengrad</string>
|
||||
<string name="settings_stats_rate_title">Bevorzugtes Maß</string>
|
||||
<string name="settings_stats_units_imperial">Englisch (mi, ft)</string>
|
||||
<string name="settings_stats_units_metric">Metrisch (km, m)</string>
|
||||
<string name="settings_stats_units_title">Bevorzugte Einheiten</string>
|
||||
|
||||
<!-- Share Track -->
|
||||
<string name="share_track_share_file">Track-Datei teilen</string>
|
||||
<string name="share_track_share_file_body">Dieser Track könnte dich interessieren.\n\nDu kannst ihn dir mit Google Earth oder einer anderen Kartierungssoftware ansehen.\n\n%1$s</string>
|
||||
<string name="share_track_subject">Ich möchte einen Track mit dir teilen</string>
|
||||
<!-- Stats -->
|
||||
<string name="stats_average_moving_pace">Durchschnitt\n(Bewegung)</string>
|
||||
<string name="stats_average_moving_speed">Durchschnitt\n(Bewegung)</string>
|
||||
<string name="stats_average_moving_speed">Durchschnittsgeshwindigkeit
|
||||
\n(Bewegung)</string>
|
||||
<string name="stats_average_pace">Durchschnitt</string>
|
||||
<string name="stats_average_speed">Durchschnitt</string>
|
||||
<string name="stats_elevation">Höhe</string>
|
||||
@@ -394,7 +379,7 @@ g) das tun, was Deiner Meinung nach OpenTracks hilft.</string>
|
||||
<string name="stats_latitude">Breitengrad</string>
|
||||
<string name="stats_longitude">Längengrad</string>
|
||||
<string name="stats_max">Max.</string>
|
||||
<string name="stats_max_speed">Max.</string>
|
||||
<string name="stats_max_speed">Max. Geschwindigkeit</string>
|
||||
<string name="stats_min">Min.</string>
|
||||
<string name="stats_moving_time">Bewegungszeit</string>
|
||||
<string name="stats_pace">Tempo</string>
|
||||
@@ -570,5 +555,18 @@ g) das tun, was Deiner Meinung nach OpenTracks hilft.</string>
|
||||
<!-- Share image -->
|
||||
<string name="share_image_subject">Ich möchte gerne ein Bild mit Dir teilen</string>
|
||||
<string name="share_image_body">Ich denke, dieses Bild könnte Dich interessieren.</string>
|
||||
|
||||
</resources>
|
||||
<string name="settings_night_mode_title">UI Thema</string>
|
||||
<string name="settings_night_mode_option_system">System</string>
|
||||
<string name="settings_night_mode_option_day">Tag</string>
|
||||
<string name="settings_night_mode_option_night">Nacht</string>
|
||||
<string name="settings_sensor_wheel_circumference">Radumfang (mm)</string>
|
||||
<string name="stats_loss">Abstieg</string>
|
||||
<string name="hold_to_stop">Halten zum Anhalten</string>
|
||||
<string name="import_progress_summary_errors_msg">Fehler</string>
|
||||
<string name="import_progress_summary_exists_msg">Schon vorhanden</string>
|
||||
<string name="export_track_errors">Tracks werden nicht exportiert</string>
|
||||
<string name="settings_default_export_uri_title">Track Export Verzeichnis</string>
|
||||
<string name="import_progress_summary_ok_msg">Dateien importiert</string>
|
||||
<string name="export_all_do_it_for_all">für alle Konflikte anwenden</string>
|
||||
<string name="export_progress_summary_new_msg">Neue Tracks exportiert</string>
|
||||
</resources>
|
||||
@@ -23,62 +23,45 @@ limitations under the License.
|
||||
<string name="about_libraries">Bibliotecas</string>
|
||||
<!-- Help -->
|
||||
<string name="help_feature_title">OpenTracks tiene las siguientes características:</string>
|
||||
<string name="help_feature_content">
|
||||
* crear un seguimiento GPS *
|
||||
* usa un sensor de ritmo cardíaco (sólo Bluetooth LE) *
|
||||
* anuncios de voz (por tiempo o distancia)*
|
||||
* crear marcadores (incluyendo imágenes) *
|
||||
* funciona completamente fuera de línea *
|
||||
* mostrar las pistas en un mapa (ver abajo)</string>
|
||||
<string name="help_feature_content">* crear pistas de GPS
|
||||
\n* utilizar un sensor de frecuencia cardíaca (solo Bluetooth LE)
|
||||
\n* Anuncios de voz (por tiempo o distancia)
|
||||
\n* crear marcadores (incluidas imágenes)
|
||||
\n* funciona completamente sin conexión
|
||||
\n* mostrar pistas en un mapa (ver más abajo)</string>
|
||||
<string name="help_track_map_title">¿Cómo mostrar una pista en un mapa?</string>
|
||||
<string name="help_track_map_content">
|
||||
Usa el icono del mapa pequeño.
|
||||
a)
|
||||
(actualizaciones en vivo) Instalar <a href="https://github.com/OpenTracksApp/OSMDashboard">OSMDashboard</a>.
|
||||
Proporciona mapas en línea o fuera de línea a través de <a href="https://www.openstreetmap.org">OpenStreetMap</a>.\n
|
||||
En realidad, OSMDashboard es una aplicación independiente que sólo puede acceder (temporalmente) a los datos de las pist(as) que se van a mostrar.
|
||||
|
||||
\n
|
||||
b)
|
||||
A través de una aplicación de mapas normal (por ejemplo, <a href="http://osmand.net/">OsmAnd</a> y <a href="https://maps.me/">MAPS.ME</a>).
|
||||
Estas aplicaciones necesitan soportar el formato de archivo KMZ (sólo las ubicaciones y las marcas de tiempo).
|
||||
De hecho, una aplicación necesita soportar <i>ACTION_VIEW</i> con MIME <i>application/vnd.google-earth.kmz</i>.</string>
|
||||
<string name="help_track_map_content"> Usa el icono del mapa pequeño.
|
||||
\na) (actualizaciones en vivo) Instalar <a href="https://github.com/OpenTracksApp/OSMDashboard">OSMDashboard</a>. Proporciona mapas en línea o fuera de línea a través de <a href="https://www.openstreetmap.org">OpenStreetMap</a>.
|
||||
\n En realidad, OSMDashboard es una aplicación independiente que sólo puede acceder (temporalmente) a los datos de las pist(as) que se van a mostrar.
|
||||
\n b) A través de una aplicación de mapas normal (por ejemplo, <a href="http://osmand.net/">OsmAnd</a> y <a href="https://maps.me/">MAPS.ME</a>). Estas aplicaciones necesitan soportar el formato de archivo KMZ (sólo las ubicaciones y las marcas de tiempo). De hecho, una aplicación necesita soportar <i>ACTION_VIEW</i> con MIME <i>application/vnd.google-earth.kmz</i>. </string>
|
||||
<string name="help_storage_pictures_title">¿Cómo almacena OpenTracks las fotos que fueron tomadas como marcadores?</string>
|
||||
<string name="help_storage_pictures_content">Estos se almacenan en el almacenamiento interno de la aplicación (es decir, sólo accesible dentro de OpenTracks).</string>
|
||||
<string name="help_track_export_title">¿Cómo puedo exportar/compartir las pistas grabadas?</string>
|
||||
<string name="help_track_export_content">
|
||||
Las pistas pueden ser exportadas desde los ajustes como KMZ, KML o GPX.
|
||||
KMZ/KML todos los datos; GPX sólo las ubicaciones.
|
||||
Las pistas compartidas a través del <i> icono de compartir </i> se comparten como KMZ.</string>
|
||||
<string name="help_track_export_content"> Las pistas pueden ser exportadas desde los ajustes como KMZ, KML o GPX. KMZ/KML todos los datos; GPX sólo las ubicaciones. Las pistas compartidas a través del <i> icono de compartir </i> se comparten como KMZ. </string>
|
||||
<string name="help_data_google_play_services_title">¿Requiere OpenTracks usar el servicio de Google Play?</string>
|
||||
<string name="help_recording_title">¿Cómo registra OpenTracks los datos?</string>
|
||||
<string name="help_recording_content">
|
||||
OpenTracks utiliza el sensor GPS del dispositivo para registrar la ubicación de forma continua.
|
||||
Si el dispositivo GPS reporta datos inexactos (por ejemplo, ubicación, velocidad, elevación), OpenTracks no puede hacer nada al respecto.</string>
|
||||
<string name="help_data_storage_title">¿Cómo almacena OpenTracks los datos de los sensores?</string>
|
||||
<string name="help_data_storage_content">
|
||||
Los datos de los sensores se almacenan por cada ubicación registrada.
|
||||
En términos de un sensor de frecuencia cardíaca, esto significa que sólo <i>uno</i> la medición de la frecuencia cardíaca se almacena por ubicación.
|
||||
Si está parado o en el interior, no se registrarán los datos del sensor (pero se mostrarán).</string>
|
||||
<string name="help_data_storage_content"> Los datos de los sensores se almacenan por cada ubicación registrada. En términos de un sensor de frecuencia cardíaca, esto significa que sólo <i>uno</i> la medición de la frecuencia cardíaca se almacena por ubicación. Si está parado o en el interior, no se registrarán los datos del sensor (pero se mostrarán). </string>
|
||||
<string name="help_gps_troubleshooting_title">OpenTracks no está grabando las localizaciones?</string>
|
||||
<string name="help_gps_troubleshooting_content">
|
||||
Lo que puedes hacer...
|
||||
a) Comprobar si el GPS está activado.
|
||||
b) mejorar la recepción del GPS (por ejemplo, ir al exterior),\n
|
||||
c) comprueba si con <a href="https://f-droid.org/en/packages/com.android.gpstest.osmdroid/">GPSTest</a> app, y\n
|
||||
d) desactivar el ahorro de energía.
|
||||
<string name="help_gps_troubleshooting_content"> Lo que puedes hacer:
|
||||
\na) compruebe si el GPS está habilitado,
|
||||
\nb) mejorar la recepción del GPS (por ejemplo, salir al aire libre),
|
||||
\nc) verifique con la aplicación <a href="https://f-droid.org/en/packages/com.android.gpstest.osmdroid/"> GPSTest </a>, y
|
||||
\nd) desactivar el ahorro de energía.
|
||||
\n
|
||||
Si todo no ayuda, por favor informe de un error.</string>
|
||||
\nSi todo no ayuda, informe un error. </string>
|
||||
<string name="help_support_title">¿Cómo puedo apoyar OpenTracks?</string>
|
||||
<string name="help_support_content">
|
||||
Puedes...
|
||||
a) difundir la palabra sobre OpenTracks,...
|
||||
b) <a href="https://github.com/OpenTracksApp/OpenTracks/issues">proponer características</a>,\n
|
||||
c) <a href="https://github.com/OpenTracksApp/OpenTracks/issues">report bugs</a>,\n
|
||||
d) ayudar en el desarrollo de software,\n
|
||||
e) comprar en <a href="https://play.google.com/store/apps/details?id=de.dennisguse.opentracks.playstore">Google Play Store</a>,\n
|
||||
f) donar, y
|
||||
g) haz lo que creas que ayude a OpenTracks.</string>
|
||||
<string name="help_support_content"> Usted puede:
|
||||
\n a) difundir la existencia de OpenTracks,
|
||||
\n b) <a href="https://github.com/OpenTracksApp/OpenTracks/issues">proponer características</a>,
|
||||
\n c) <a href="https://github.com/OpenTracksApp/OpenTracks/issues">reportar errores</a>,
|
||||
\n d) ayudar en el desarrollo del software,
|
||||
\n e) comprar en <a href="https://play.google.com/store/apps/details?id=de.dennisguse.opentracks.playstore">Google Play Store</a>,
|
||||
\n f) donar, y
|
||||
\n g) hacer lo que creas que ayuda a OpenTracks. </string>
|
||||
<!-- Activity Type -->
|
||||
<string name="activity_type_airplane">avión</string>
|
||||
<string name="activity_type_atv">vehículos todoterreno</string>
|
||||
@@ -94,8 +77,8 @@ g) haz lo que creas que ayude a OpenTracks.</string>
|
||||
<string name="activity_type_driving">conducir</string>
|
||||
<string name="activity_type_driving_bus">ruta en autobús</string>
|
||||
<string name="activity_type_driving_car">ruta en automóvil</string>
|
||||
<string name="activity_type_ferry">ferry</string>
|
||||
<string name="activity_type_frisbee">frisbee</string>
|
||||
<string name="activity_type_ferry">balsa</string>
|
||||
<string name="activity_type_frisbee">Friso</string>
|
||||
<string name="activity_type_gliding">vuelo sin motor</string>
|
||||
<string name="activity_type_hang_gliding">vuelo en ala delta</string>
|
||||
<string name="activity_type_helicopter">helicóptero</string>
|
||||
@@ -301,14 +284,14 @@ g) haz lo que creas que ayude a OpenTracks.</string>
|
||||
<!-- Search -->
|
||||
<string name="search_global_description">Recorridos y marcadores</string>
|
||||
<string name="search_list_empty_message">No hay rutas ni marcadores que coincidan con tu búsqueda</string>
|
||||
<string name="search_list_marker_track_location">En la ruta \"%1$s\"</string>
|
||||
<string name="search_list_marker_track_location">En la ruta \"%1$s\".</string>
|
||||
<!-- Sensor State -->
|
||||
<string name="sensor_state_battery">Nivel de batería</string>
|
||||
<string name="sensor_state_cadence">Cadencia</string>
|
||||
<string name="sensor_state_cadence_value">%1$d rpm</string>
|
||||
<string name="sensor_state_cadence_value">%1$d rym</string>
|
||||
<string name="sensor_state_disconnected">Desconectado</string>
|
||||
<string name="sensor_state_heart_rate">Frecuencia cardíaca</string>
|
||||
<string name="sensor_state_heart_rate_value">%1$d bpm</string>
|
||||
<string name="sensor_state_heart_rate_value">%1$d ppm</string>
|
||||
<string name="sensor_state_power">Potencia</string>
|
||||
<string name="sensor_state_power_value">%1$d W</string>
|
||||
<string name="sensor_not_known">Sensor %1$s es desconocido. Por favor, compruebe la configuración.</string>
|
||||
@@ -330,15 +313,15 @@ g) haz lo que creas que ayude a OpenTracks.</string>
|
||||
<string name="settings_recording">Grabación</string>
|
||||
<string name="settings_recording_auto_resume_track_timeout_always_summary">Reanudar siempre una grabación después de reiniciar</string>
|
||||
<string name="settings_recording_auto_resume_track_timeout_never_summary">No reanudar nunca una grabación después de reiniciar</string>
|
||||
<string name="settings_recording_auto_resume_track_timeout_summary">Esta opción permite reanudar una grabación después de reiniciar si la última ubicación grabada se encuentra dentro de los últimos %1$s.</string>
|
||||
<string name="settings_recording_auto_resume_track_timeout_summary">Esta opción permite reanudar una grabación después de reiniciar si la última ubicación grabada se encuentra dentro de los últimos %1$s</string>
|
||||
<string name="settings_recording_auto_resume_track_timeout_title">Reanudación automática</string>
|
||||
<string name="settings_recording_default_activity_title">Actividad predeterminada</string>
|
||||
<string name="settings_recording_location_frequency_summary">%1$s entre ubicaciones grabadas</string>
|
||||
<string name="settings_recording_max_recording_distance_summary">Esta opción permite crear un nuevo segmento si la distancia entre dos ubicaciones es superior a %1$s.</string>
|
||||
<string name="settings_recording_max_recording_distance_summary">Esta opción permite crear un nuevo segmento si la distancia entre dos ubicaciones es superior a %1$s</string>
|
||||
<string name="settings_recording_max_recording_distance_title">Distancia máxima</string>
|
||||
<string name="settings_recording_min_recording_distance_title">Intervalo de distancia</string>
|
||||
<string name="settings_recording_min_recording_interval_title">Intervalo de tiempo</string>
|
||||
<string name="settings_recording_min_required_accuracy_summary">Esta opción permite ignorar la ubicación si su precisión es inferior a %1$s.</string>
|
||||
<string name="settings_recording_min_required_accuracy_summary">Esta opción permite ignorar la ubicación si su precisión es inferior a %1$s</string>
|
||||
<string name="settings_recording_min_required_accuracy_title">Exactitud del GPS</string>
|
||||
<string name="settings_recording_track_name_date_iso_8601_option">Fecha (ISO 8601)</string>
|
||||
<string name="settings_recording_track_name_date_local_option">Fecha (local)</string>
|
||||
@@ -351,7 +334,7 @@ g) haz lo que creas que ayude a OpenTracks.</string>
|
||||
<string name="settings_reset_done">Se han restablecido los valores predeterminados de todas las opciones de configuración</string>
|
||||
<string name="settings_error_initial_values_restored">Lo siento, pero ha habido un error. Todos los ajustes se han vuelto a los valores predeterminados.</string>
|
||||
<!-- Settings Sensor -->
|
||||
<string name="settings_sensor">Sensor</string>
|
||||
<string name="settings_sensor">Objetivo</string>
|
||||
<string name="settings_sensor_bluetooth">Bluetooth</string>
|
||||
<string name="settings_sensor_bluetooth_sensor">Sensor Bluetooth</string>
|
||||
<string name="settings_sensor_connecting">Conectado a %1$s</string>
|
||||
@@ -364,7 +347,9 @@ g) haz lo que creas que ayude a OpenTracks.</string>
|
||||
<string name="settings_stats_units_title">Unidades preferidas</string>
|
||||
<!-- Share Track -->
|
||||
<string name="share_track_share_file">Compartir el archivo de la ruta</string>
|
||||
<string name="share_track_share_file_body">Creo que esta ruta te podría interesar.\n\nPara ver esta ruta, usa Google Earth u otro software de mapas.\n\n%1$s</string>
|
||||
<string name="share_track_share_file_body">Creo que esta ruta te podría interesar.
|
||||
\n
|
||||
\n%1$s</string>
|
||||
<string name="share_track_subject">Me gustaría compartir una ruta contigo</string>
|
||||
<!-- Stats -->
|
||||
<string name="stats_average_moving_pace">Ritmo de movimiento medio</string>
|
||||
@@ -512,7 +497,7 @@ g) haz lo que creas que ayude a OpenTracks.</string>
|
||||
<string name="waypoint_type_convenience_store">tienda de barrio</string>
|
||||
<string name="waypoint_type_drinking_water">agua potable</string>
|
||||
<string name="waypoint_type_falling_rocks">desprendimiento de rocas</string>
|
||||
<string name="waypoint_type_ferry">ferry</string>
|
||||
<string name="waypoint_type_ferry">barco</string>
|
||||
<string name="waypoint_type_fire_place">chimenea</string>
|
||||
<string name="waypoint_type_fire_station">parque de bomberos</string>
|
||||
<string name="waypoint_type_fishing">enclave pesquero</string>
|
||||
@@ -574,13 +559,13 @@ g) haz lo que creas que ayude a OpenTracks.</string>
|
||||
<string name="settings_night_mode_option_day">Claro</string>
|
||||
<string name="settings_night_mode_option_night">Oscuro</string>
|
||||
<string name="settings_night_mode_title">Elegir un tema</string>
|
||||
<string name="settings_sensor_wheel_circumference">Circunferencia de rueda (mm)</string>
|
||||
<string name="settings_sensor_wheel_circumference">Perímetro de la rueda (mm)</string>
|
||||
<string name="stats_intervals">Intervalos</string>
|
||||
<string name="stats_split_interval">Intervalo</string>
|
||||
<string name="track_detail_intervals_tab">Intervalos</string>
|
||||
<string name="aggregated_stats_empty_message">Graba tu primera pista para ver estadísticas agregadas</string>
|
||||
<string name="voice_pace_lap">Tiempo de la vuelta %1$s</string>
|
||||
<string name="hold_to_pause">Mantén pulsado para pausar la grabación</string>
|
||||
<string name="hold_to_pause">Mantener en pausa</string>
|
||||
<string name="instant_export_enabled_title">Exportación automática tras grabación</string>
|
||||
<string name="settings_default_export_uri_title">Carpeta de pistas exportadas</string>
|
||||
<string name="open_track_using_dashboard">Abrir con OpenTracks-Dashboard</string>
|
||||
@@ -596,7 +581,31 @@ g) haz lo que creas que ayude a OpenTracks.</string>
|
||||
<string name="settings_recording_fullscreen_on_while_recording_title">Pantalla completa</string>
|
||||
<string name="interval_list_empty_message">No hay intervalos todavía</string>
|
||||
<string name="voice_speed_lap">Velocidad de la vuelta %1$s</string>
|
||||
<string name="hold_to_stop">Mantén pulsado para terminar la grabación</string>
|
||||
<string name="hold_to_stop">Mantener para parar</string>
|
||||
<string name="toast_message_instant_track_was_exported">Última pista exportada</string>
|
||||
<string name="instant_export_enabled_summary">Exportar pista después de terminar la grabación</string>
|
||||
<string name="stats_loss">Pérdida</string>
|
||||
<string name="settings_prevent_reimport_tracks_title">Impedir la reimportación de pistas</string>
|
||||
<string name="sensor_state_power_avg">Potencia media</string>
|
||||
<string name="sensor_state_heart_rate_max">Frecuencia cardíaca máxima</string>
|
||||
<string name="sensor_state_heart_rate_avg">Ritmo cardíaco medio</string>
|
||||
<string name="export_error_post_workout">La exportación de post-entrenamiento ha fallado, por favor compruebe el directorio de exportación.</string>
|
||||
<string name="export_progress_summary_new_msg">Nuevas pistas exportadas</string>
|
||||
<string name="import_thread_interrupted">Hilo interrumpido</string>
|
||||
<string name="import_progress_summary_errors_msg">Errores</string>
|
||||
<string name="import_progress_summary_ok_msg">Archivos importados</string>
|
||||
<string name="description_elevation_loss">Pérdida de altura: %1$d m (%2$d pies)</string>
|
||||
<string name="value_float_mile_hour">%1$.2f mph</string>
|
||||
<string name="value_pace_kilometer">%1$d:%2$02d min/km</string>
|
||||
<string name="value_pace_mile">%1$d:%2$02d min/mi</string>
|
||||
<string name="sensor_state_cadence_max">Cadencia máxima</string>
|
||||
<string name="sensor_state_cadence_avg">Cadencia media</string>
|
||||
<string name="value_float_kilometer_hour">%1$.2f kp/h</string>
|
||||
<string name="export_progress_summary_overwrite_msg">Pistas sobrescritas</string>
|
||||
<string name="export_progress_summary_skip_msg">Pistas omitidas</string>
|
||||
<string name="export_progress_summary_errors_msg">Errores</string>
|
||||
<string name="generic_open_track">Pista abierta</string>
|
||||
<string name="import_progress_summary_exists_msg">Ya existen</string>
|
||||
<string name="import_no_kml_file_found">No se ha encontrado ningún archivo doc.kml en KMZ</string>
|
||||
<string name="import_prevent_reimport">Ha establecido impedir la reimportación de la pista</string>
|
||||
</resources>
|
||||
@@ -1,4 +1,5 @@
|
||||
<?xml version="1.0" encoding="utf-8"?><!--
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!--
|
||||
Copyright 2010 Google Inc.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
@@ -12,8 +13,7 @@ distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
-->
|
||||
<resources>
|
||||
--><resources>
|
||||
<string name="about_preference_title">Umbes OpenTracks</string>
|
||||
<string name="about_description"><i>OpenTracks</i> on spordi jälgimise rakendus, mis austab täielikult teie privaatsust.</string>
|
||||
<string name="about_url">Projekti veebileht: <a href="%1$s">%1$s</a></string>
|
||||
@@ -23,69 +23,45 @@ limitations under the License.
|
||||
<string name="about_libraries">Raamatukogud</string>
|
||||
<!-- Help -->
|
||||
<string name="help_feature_title">OpenTracks sellel on järgmised omadused:</string>
|
||||
<string name="help_feature_content">
|
||||
* GPS-radade loomine \ n
|
||||
* kasutage pulsisensorit (ainult Bluetooth LE) \ n
|
||||
* häälteated (aja või vahemaa järgi) \ n
|
||||
* loo markerid (koos piltidega) \ n
|
||||
* töötab täiesti võrguühenduseta \ n
|
||||
* näidata radu kaardil (vt allpool)</string>
|
||||
|
||||
<string name="help_feature_content">* loo GPS rajad
|
||||
\n* kasutage südame löögisageduse andurit ( ainult Bluetooth LE)
|
||||
\n* häälteated (aja või vahemaa järgi)
|
||||
\n* loo markerid (sh. digikuvat)
|
||||
\n* töötab täiesti offline
|
||||
\n* näita lugusid kaardil (vt allpool)</string>
|
||||
<string name="help_track_map_title">Kuidas näidata pala kaardil?</string>
|
||||
<string name="help_track_map_content">
|
||||
Kasutage väikest kaardiikooni. \n
|
||||
a)
|
||||
(reaalajas värskendused) Installige <a href="https://github.com/OpenTracksApp/OSMDashboard">OSMDashboard</a>.
|
||||
See pakub võrgu- või võrguühenduseta kaarte <a href="https://www.openstreetmap.org">OpenStreetMap</a> kaudu.\n
|
||||
Tegelikult on OSMDashboard eraldiseisev rakendus, mis pääseb juurde (ajutiselt) ainult kuvatavate rada(de) andmetele.
|
||||
\n
|
||||
b) „Tavalise” kaardirakenduse (nt <a href="http://osmand.net/">OsmAnd</a> ja <a href="https://maps.me/">MAPS.ME</a>.
|
||||
Need rakendused peavad toetama KMZ-failivormingut (ainult asukohad ja ajatemplid).
|
||||
Tegelikult peab rakendus toetama rakendust MIME <i>application/vnd.google-earth.</i> <i>ACTION_VIEW</i>.</string>
|
||||
|
||||
<string name="help_track_map_content"> Kasutage väikest kaardisümbolit.
|
||||
\n a) (reaalajas uuendused) Installige <a href="https://github.com/OpenTracksApp/OSMDashboard">OSMDashboard</a>. See pakub online või offline-kaarte <a href="https://www.openstreetmap.org">OpenStreetMap</a> kaudu.
|
||||
\n Tegelikult on OSMDashboard eraldi rakendus, mis saab ainult (ajutiselt) juurdepääsu näidatavate rada(de) andmetele.
|
||||
\n b) Tavalise kaardirakenduse kaudu (nt <a href="http://osmand.net/">OsmAnd</a> ja <a href="https://maps.me/">MAPS.ME</a>). Need rakendused peavad toetama KMZ-failiformaati (ainult asukohad ja ajatemplid). Tegelikult peab rakendus toetama <i>ACTION_VIEW</i> koos MIME <i>application/vnd.google-earth.kmz</i>-ga. </string>
|
||||
<string name="help_storage_pictures_title">Kuidas OpenTracks salvestab markeritena tehtud pilte?</string>
|
||||
<string name="help_storage_pictures_content">Neid hoitakse rakenduse sisemises salvestusruumis (st pääseb juurde ainult OpenTracks is).</string>
|
||||
|
||||
<string name="help_track_export_title">Kuidas saab salvestatud lugusid eksportida / jagada?</string>
|
||||
<string name="help_track_export_content">
|
||||
Lugusid saab seadetest eksportida KMZ-, KML- või GPX-vormingus.
|
||||
KMZ / KML kõik andmed; Ainult GPX-i asukohad.
|
||||
Radasid, mis on jagatud <i> jagamise ikooni </i> kaudu, jagatakse KMZ-na.</string>
|
||||
|
||||
<string name="help_track_export_content"> Lugusid saab seadetest eksportida KMZ-, KML- või GPX-vormingus. KMZ / KML kõik andmed; Ainult GPX-i asukohad. Radasid, mis on jagatud <i> jagamise ikooni </i> kaudu, jagatakse KMZ-na. </string>
|
||||
<string name="help_data_google_play_services_title">Kas OpenTracks nõuab / kasutab Google Play teenust?</string>
|
||||
|
||||
<string name="help_recording_title">Kuidas OpenTracks andmeid registreerib?</string>
|
||||
<string name="help_recording_content">
|
||||
OpenTracks kasutab seadme GPS-andurit asukoha pidevaks registreerimiseks.
|
||||
Kui GPS-seade teatab ebatäpsetest andmetest (nt asukoht, kiirus, kõrgus), ei saa OpenTracks sellega midagi teha.</string>
|
||||
|
||||
<string name="help_data_storage_title">Kuidas OpenTracks sensori andmeid salvestab?</string>
|
||||
<string name="help_data_storage_content">
|
||||
Anduri andmed salvestatakse salvestatud asukoha kohta.
|
||||
Südame löögisageduse anduri puhul tähendab see, et asukoha kohta salvestatakse ainult <i>üks</i> südame löögisageduse mõõtmine.
|
||||
Kui seisate paigal või siseruumides, siis anduri andmeid ei registreerita (kuid need näidatakse).</string>
|
||||
|
||||
<string name="help_data_storage_content"> Anduri andmed salvestatakse salvestatud asukoha kohta. Südame löögisageduse anduri puhul tähendab see, et asukoha kohta salvestatakse ainult <i>üks</i> südame löögisageduse mõõtmine. Kui seisate paigal või siseruumides, siis anduri andmeid ei registreerita (kuid need näidatakse). </string>
|
||||
<string name="help_gps_troubleshooting_title">OpenTracks ei salvesta asukohti?</string>
|
||||
<string name="help_gps_troubleshooting_content">
|
||||
Mida saate teha: \ n
|
||||
a) kontrollige, kas GPS on lubatud, \ n
|
||||
b) parandage GPS-vastuvõttu (nt käige õues), \ n
|
||||
c) kontrollige, kas rakendusega <a href="https://f-droid.org/en/packages/com.android.gpstest.osmdroid/"> GPSTest </a> ja \ n
|
||||
d) keelake energiasääst. \ n
|
||||
\ n
|
||||
Kui kõik ei aita, teatage veast.</string>
|
||||
|
||||
<string name="help_gps_troubleshooting_content"> Mida saate teha:
|
||||
\na) kontrollige, kas GPS on lubatud,
|
||||
\nb) parandada GPS vastuvõtt (NT minna õues),
|
||||
\nc) kontrollige <a href="https://f-droid.org/en/packages/com.android.gpstest.osmdroid /">GPSTest</a> app ja
|
||||
\nd) keelata energiasäästu.
|
||||
\n
|
||||
\nKui kõik ei aita, palun teatage veast. </string>
|
||||
<string name="help_support_title">Kuidas OpenTracks it toetada?</string>
|
||||
<string name="help_support_content">
|
||||
Saate: \ n
|
||||
a) levitage sõna OpenTracks kohta, \ n
|
||||
b) <a href="https://github.com/OpenTracksApp/OpenTracks/issues"> funktsioonide pakkumine </a>, \ n
|
||||
c) <a href="https://github.com/OpenTracksApp/OpenTracks/issues"> teatage vigadest </a>, \ n
|
||||
d) abi tarkvara arendamisel, \ n
|
||||
e) ostke sisse <a href="https://play.google.com/store/apps/details?id=de.dennisguse.opentracks.playstore"> Google Play pood </a>, \ n
|
||||
f) annetama ja \ n
|
||||
g) tehke kõike, mis teie arvates OpenTracks aitab.</string>
|
||||
|
||||
<string name="help_support_content"> Saate:
|
||||
\n a) levitada OpenTracks\'i kohta sõna,
|
||||
\n b) <a href="https://github.com/OpenTracksApp/OpenTracks/issues">ettepanekuid teha</a>,
|
||||
\n c) <a href="https://github.com/OpenTracksApp/OpenTracks/issues">raportida vigadest</a>,
|
||||
\n d) aidata tarkvara arendamisel,
|
||||
\n e) osta <a href="https://play.google.com/store/apps/details?id=de.dennisguse.opentracks.playstore">Google Play Store\'is</a>,
|
||||
\n f) annetada ja
|
||||
\n g) teha kõike, mis teie arvates aitab OpenTracks\'ile kaasa. </string>
|
||||
<!-- Activity Type -->
|
||||
<string name="activity_type_airplane">lennuk</string>
|
||||
<string name="activity_type_atv">ATV</string>
|
||||
@@ -147,7 +123,7 @@ g) tehke kõike, mis teie arvates OpenTracks aitab.</string>
|
||||
<string name="activity_type_trail_hiking">matkarajal matkamine</string>
|
||||
<string name="activity_type_trail_running">maastikujooks</string>
|
||||
<string name="activity_type_train">rong</string>
|
||||
<string name="activity_type_ultimate_frisbee">ultimate frisbee</string>
|
||||
<string name="activity_type_ultimate_frisbee">ülim frisbee</string>
|
||||
<string name="activity_type_unknown">tundmatu</string>
|
||||
<string name="activity_type_wakeboarding">veelauaga sõitmine</string>
|
||||
<string name="activity_type_walking">kõndimine</string>
|
||||
@@ -239,7 +215,7 @@ g) tehke kõike, mis teie arvates OpenTracks aitab.</string>
|
||||
<!-- Image -->
|
||||
<string name="image_application">Rakendus</string>
|
||||
<string name="image_arrow">Nool</string>
|
||||
<string name="image_marker">Marker</string>
|
||||
<string name="image_marker">Mark</string>
|
||||
<string name="image_marker_count">Markerite arv</string>
|
||||
<string name="image_my_location">Minu asukoht</string>
|
||||
<string name="image_pause">Peata</string>
|
||||
@@ -267,7 +243,7 @@ g) tehke kõike, mis teie arvates OpenTracks aitab.</string>
|
||||
<string name="marker_delete_multiple_confirm_message">Valitud markerid kustutatakse seadmest jäädavalt</string>
|
||||
<string name="marker_label_end">%1$s (Lõpp)</string>
|
||||
<string name="marker_label_start">%1$s (Algus)</string>
|
||||
<string name="marker_name_format">Marker %1$d</string>
|
||||
<string name="marker_name_format">Mark %1$d</string>
|
||||
<string name="marker_split_name_format">Jaotus %1$d</string>
|
||||
<string name="marker_title">%1$d/%2$d</string>
|
||||
<!-- Marker Detail -->
|
||||
@@ -311,7 +287,7 @@ g) tehke kõike, mis teie arvates OpenTracks aitab.</string>
|
||||
<!-- Sensor State -->
|
||||
<string name="sensor_state_battery">Aku tase</string>
|
||||
<string name="sensor_state_cadence">Rütm</string>
|
||||
<string name="sensor_state_cadence_value">%1$d rpm</string>
|
||||
<string name="sensor_state_cadence_value">%1$d p/min</string>
|
||||
<string name="sensor_state_disconnected">Ühendus on katkestatud</string>
|
||||
<string name="sensor_state_heart_rate">Pulsisagedus</string>
|
||||
<string name="sensor_state_heart_rate_value">%1$d lööki minutis</string>
|
||||
@@ -340,7 +316,7 @@ g) tehke kõike, mis teie arvates OpenTracks aitab.</string>
|
||||
<string name="settings_recording_auto_resume_track_timeout_title">Automaatne jätkamine</string>
|
||||
<string name="settings_recording_default_activity_title">Tegevuse vaiketüüp</string>
|
||||
<string name="settings_recording_location_frequency_summary">%1$s salvestatud asukohtade vahel</string>
|
||||
<string name="settings_recording_max_recording_distance_summary">Kui vahemaa kahe asukoha vahel on üle %1$s, luuakse uus lõik.</string>
|
||||
<string name="settings_recording_max_recording_distance_summary">Kui vahemaa kahe asukoha vahel on üle %1$s, luuakse uus lõik</string>
|
||||
<string name="settings_recording_max_recording_distance_title">Max vahemaa</string>
|
||||
<string name="settings_recording_min_recording_distance_title">Salvestamiskauguse intervall</string>
|
||||
<string name="settings_recording_min_recording_interval_title">Salvestamisaja intervall</string>
|
||||
@@ -362,17 +338,17 @@ g) tehke kõike, mis teie arvates OpenTracks aitab.</string>
|
||||
<string name="settings_sensor_bluetooth_sensor">Bluetoothi andur</string>
|
||||
<string name="settings_sensor_connecting">Ühendatud anduriga %1$s</string>
|
||||
<string name="settings_sensor_disconnected">Lahti ühendatud %1$s</string>
|
||||
|
||||
<!-- Settings Stats -->
|
||||
<string name="settings_stats_coordinate">Laiuskraad/pikkuskraad</string>
|
||||
<string name="settings_stats_rate_title">Eelistatud määr</string>
|
||||
<string name="settings_stats_units_imperial">Inglise mõõtühikud (miili, jalga)</string>
|
||||
<string name="settings_stats_units_metric">Meetermõõdustik (km, m)</string>
|
||||
<string name="settings_stats_units_title">Eelistatud ühikud</string>
|
||||
|
||||
<!-- Share Track -->
|
||||
<string name="share_track_share_file">Jagage rajafaili</string>
|
||||
<string name="share_track_share_file_body">Teile võib see rada huvi pakkuda.\n\nRaja vaatamiseks kasutage rakendust Google Earth või muud kaardistamistarkvara.\n\n%1$s</string>
|
||||
<string name="share_track_share_file_body">Teile võib see rada huvi pakkuda.
|
||||
\n
|
||||
\n%1$s</string>
|
||||
<string name="share_track_subject">Ma soovin teiega rada jagada</string>
|
||||
<!-- Stats -->
|
||||
<string name="stats_average_moving_pace">Keskmine liikumistempo</string>
|
||||
@@ -553,12 +529,82 @@ g) tehke kõike, mis teie arvates OpenTracks aitab.</string>
|
||||
<string name="waypoint_type_toilet">tualett</string>
|
||||
<string name="waypoint_type_tourist_information">turismiinfo</string>
|
||||
<string name="waypoint_type_tree">puu</string>
|
||||
<string name="waypoint_type_tunnel">tunnel</string>
|
||||
<string name="waypoint_type_tunnel">tunnelit</string>
|
||||
<string name="waypoint_type_waterfall">juga</string>
|
||||
<string name="waypoint_type_water_fountain">purskkaev</string>
|
||||
<string name="waypoint_type_waypoint">teekonnapunkt</string>
|
||||
<!-- Share image -->
|
||||
<string name="share_image_subject">Tahaksin teiega pilti jagada</string>
|
||||
<string name="share_image_body">Ma arvan, et see pilt võiks teid huvitada.</string>
|
||||
|
||||
</resources>
|
||||
<string name="open_track_as_trackfileformat">Ava kui %1$s</string>
|
||||
<string name="open_track_using_dashboard">Open kasutades OpenTracks-Dashboard</string>
|
||||
<string name="activity_type_escooter">eskuuter</string>
|
||||
<string name="open_track_as_GPX">Avatud kui GPX</string>
|
||||
<string name="open_track_as_KMZ">Avatud kui KMZ</string>
|
||||
<string name="description_elevation_loss">Kõrguse kadu: %1$d m (%2$d ft)</string>
|
||||
<string name="export_all_do_it_for_all">tehke seda kõigi konfliktide jaoks</string>
|
||||
<string name="export_error_post_workout">Treeningujärgne eksport nurjus, palun kontrolli ekspordikataloogi.</string>
|
||||
<string name="import_thread_interrupted">Teema katkestatud</string>
|
||||
<string name="import_parser_error">Parseri viga: %1$s</string>
|
||||
<string name="import_unable_to_import_file">Faili importimine nurjus: %1$s</string>
|
||||
<plurals name="generic_completed_with_errors">
|
||||
<item quantity="one">Lõpetatud ühe veaga</item>
|
||||
<item quantity="other">Lõpetatud %1$d vigadega</item>
|
||||
</plurals>
|
||||
<string name="gps_fixed_and_ready">GPS fikseeritud ja valmis</string>
|
||||
<string name="import_prevent_reimport">Olete määranud vältida re-import track</string>
|
||||
<string name="generic_overwrite">Ülekirjutamine</string>
|
||||
<string name="export_progress_summary_overwrite_msg">Ülekirjutatud lood</string>
|
||||
<string name="export_track_errors">Palasid ei ekspordita</string>
|
||||
<string name="export_track_already_exists_msg">Rada %1$s on sihtkataloogis juba olemas.</string>
|
||||
<string name="aggregated_stats_empty_message">Salvestage oma esimene pala, et näha koondatud statistikat</string>
|
||||
<string name="toast_message_instant_track_was_exported">Viimane eksporditud pala</string>
|
||||
<string name="voice_pace_lap">Ringiaeg %1$s</string>
|
||||
<string name="settings_prevent_reimport_tracks_title">Palade reimportimise vältimine</string>
|
||||
<string name="settings_night_mode_option_night">Öö</string>
|
||||
<string name="settings_night_mode_option_day">Päev</string>
|
||||
<string name="settings_night_mode_option_system">Süsteem</string>
|
||||
<string name="settings_night_mode_title">UI teema</string>
|
||||
<string name="settings_recording_fullscreen_on_while_recording_summary">Salvestamise ajal esitada täisekraanil.</string>
|
||||
<string name="settings_recording_fullscreen_on_while_recording_title">Täisekraan</string>
|
||||
<string name="settings_default_trackfileformat">Failivormingu eksport/jagamine</string>
|
||||
<string name="sensor_state_power_avg">Avg Võimsus</string>
|
||||
<string name="sensor_state_heart_rate_max">Maksimaalne pulss</string>
|
||||
<string name="sensor_state_heart_rate_avg">Keskmine pulsisagedus</string>
|
||||
<string name="sensor_state_cadence_max">Max kadentsus</string>
|
||||
<string name="sensor_state_cadence_avg">Keskmine kadentsus</string>
|
||||
<string name="settings_sensor_wheel_circumference">Ratta ümbermõõt (mm)</string>
|
||||
<string name="stats_loss">Kahjum</string>
|
||||
<string name="track_detail_intervals_tab">Intervallid</string>
|
||||
<string name="stats_split_interval">Jagatud intervall</string>
|
||||
<string name="stats_intervals">Intervallid</string>
|
||||
<string name="interval_list_empty_message">Ei ole ühtegi intervalli veel</string>
|
||||
<string name="value_pace_kilometer">%1$d:%2$02d min/km</string>
|
||||
<string name="value_pace_mile">%1$d:%2$02d min/mi</string>
|
||||
<string name="value_float_mile_hour">%1$.2f mph</string>
|
||||
<string name="value_float_kilometer_hour">%1$.2f km/h</string>
|
||||
<string name="voice_speed_lap">Ringi kiirus %1$s</string>
|
||||
<string name="hold_to_stop">Hoidke lõpetada</string>
|
||||
<string name="hold_to_pause">Hoidke pausi</string>
|
||||
<string name="instant_export_enabled_summary">Ekspordi pala salvestusruumi pärast salvestamise lõpetamist</string>
|
||||
<string name="settings_default_export_uri_title">Track ekspordi kataloog</string>
|
||||
<string name="instant_export_enabled_title">Vahetu treeningujärgne eksport</string>
|
||||
<string name="export_progress_summary_errors_msg">Viga</string>
|
||||
<string name="export_progress_summary_skip_msg">Palad vahele</string>
|
||||
<string name="export_progress_summary_new_msg">Uued palad eksporditud</string>
|
||||
<string name="generic_open_track">Avatud rada</string>
|
||||
<string name="generic_completed">Lõpetanud</string>
|
||||
<string name="generic_click_twice_cancel">Tühistamiseks klõpsake uuesti</string>
|
||||
<string name="gps_disabled_msg">GPS keelatud</string>
|
||||
<string name="import_unsupported_format">Toetamata failivorming</string>
|
||||
<string name="import_no_kml_file_found">Ei, doktor.KMZ-is leitud KML fail</string>
|
||||
<string name="import_error_info">%1$s: %2$s</string>
|
||||
<string name="import_error_list_dialog_title">Failid, mida ei impordita</string>
|
||||
<string name="import_file_imported">Fail %1$s imporditud</string>
|
||||
<string name="import_progress_summary_ok_msg">Imporditud failid</string>
|
||||
<string name="import_progress_summary_exists_msg">Juba olemas</string>
|
||||
<string name="generic_show_errors">Vigade Näitamine</string>
|
||||
<string name="generic_separator_done_total">/</string>
|
||||
<string name="generic_skip">Jäta</string>
|
||||
<string name="import_progress_summary_errors_msg">Viga</string>
|
||||
</resources>
|
||||
@@ -1,4 +1,5 @@
|
||||
<?xml version="1.0" encoding="utf-8"?><!--
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!--
|
||||
Copyright 2010 Google Inc.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
@@ -12,14 +13,13 @@ distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
-->
|
||||
<resources>
|
||||
--><resources>
|
||||
<string name="about_preference_title">OpenTracks-i buruz</string>
|
||||
<string name="about_description"><i> OpenTracks </i> kirol jarraipena egiteko aplikazioa da, zure pribatutasuna erabat errespetatzen duena.</string>
|
||||
<string name="about_url">Proiektuaren web orria: <a href="%1$s">%1$s</a></string>
|
||||
<string name="about_version_name">Bertsioaren izena: %1$s</string>
|
||||
<string name="about_version_code">Bertsio kodea: %1$d</string>
|
||||
<string name="about_history">OpenTracks <a href="https://code.google.com/archive/p/mytracks/"> <i> Google \ n MyTracks </i> </a> (horixe utzi zen 2016an).</string>
|
||||
<string name="about_history">OpenTracks <a href="https://code.google.com/archive/p/mytracks/"> <i> Google n MyTracks </i> </a> (horixe utzi zen 2016an).</string>
|
||||
<string name="about_libraries">Liburutegiak</string>
|
||||
<!-- Help -->
|
||||
<string name="help_feature_title">OpenTracks ek ezaugarri hauek ditu:</string>
|
||||
@@ -30,60 +30,39 @@ limitations under the License.
|
||||
* Sortu markatzaileak (irudiak barne)\n
|
||||
* guztiz lineaz kanpo funtzionatzen du\n
|
||||
* Erakutsi ibilbideak mapa batean (ikusi azpian)</string>
|
||||
|
||||
<string name="help_track_map_title">Nola erakutsi pista batean mapa batean?</string>
|
||||
<string name="help_track_map_content">
|
||||
Erabili maparen ikono txikia.\n
|
||||
a) (zuzeneko eguneratzeak) Instalatu <a href="https://github.com/OpenTracksApp/OSMDashboard">OSMDashboard</a>.
|
||||
Lineako edo lineaz kanpoko mapak eskaintzen ditu <a href="https://www.openstreetmap.org">OpenStreetMap</a>.\n
|
||||
Egia esan, OSMDashboard erakundea da erakutsiko diren pista (k) datuetara sartzeko soilik (aldi baterako) sar daitekeena.\n
|
||||
b) Mapa aplikazio normal baten bidez (adibidez, <a href="http://osmand.net/">OsmAnd</a> eta <a href="https://maps.me/">MAPS.ME</a>).
|
||||
Aplikazio horiek KMZ fitxategi formatua onartzen dute (kokapenak eta timestamps soilik).
|
||||
Izan ere, aplikazio batek <i>ACTION_VIEW</i> onartzen du MIME <i>application/vnd.google-earth.kmz</i> rekin.</string>
|
||||
|
||||
<string name="help_track_map_content"> Erabili maparen ikono txikia.
|
||||
\n a) (zuzeneko eguneratzeak) Instalatu <a href="https://github.com/OpenTracksApp/OSMDashboard">OSMDashboard</a>. Lineako edo lineaz kanpoko mapak eskaintzen ditu <a href="https://www.openstreetmap.org">OpenStreetMap</a>.
|
||||
\n Egia esan, OSMDashboard erakundea da erakutsiko diren pista (k) datuetara sartzeko soilik (aldi baterako) sar daitekeena.
|
||||
\n b) Mapa aplikazio normal baten bidez (adibidez, <a href="http://osmand.net/">OsmAnd</a> eta <a href="https://maps.me/">MAPS.ME</a>). Aplikazio horiek KMZ fitxategi formatua onartzen dute (kokapenak eta timestamps soilik). Izan ere, aplikazio batek <i>ACTION_VIEW</i> onartzen du MIME <i>application/vnd.google-earth.kmz</i> rekin. </string>
|
||||
<string name="help_storage_pictures_title">Nola gordetzen du OpenTracks markatzaile gisa ateratako argazkiak?</string>
|
||||
<string name="help_storage_pictures_content">Aplikazioaren barne biltegian gordetzen dira (hau da, OpenTracks en soilik eskura daiteke).</string>
|
||||
|
||||
<string name="help_track_export_title">Nola esportatu/berdinak diren ibilbideak grabatutako ibilbideak?</string>
|
||||
<string name="help_track_export_content">
|
||||
Pistak Ezarpenetatik KMZ, KML edo GPX gisa esportatu daitezke.
|
||||
KMZ / KML datu guztiak; GPX kokapenak soilik.
|
||||
<i> Partekatutako ikonoa </i> bidez partekatutako ibilbideak KMZ gisa partekatzen dira.</string>
|
||||
|
||||
<string name="help_track_export_content"> Pistak Ezarpenetatik KMZ, KML edo GPX gisa esportatu daitezke. KMZ / KML datu guztiak; GPX kokapenak soilik. <i> Partekatutako ikonoa </i> bidez partekatutako ibilbideak KMZ gisa partekatzen dira. </string>
|
||||
<string name="help_data_google_play_services_title">OpenTracks-ek Google Play zerbitzua erabiltzea eskatzen al du?</string>
|
||||
|
||||
<string name="help_recording_title">Nola grabatzen ditu OpenTracks datuak?</string>
|
||||
<string name="help_recording_content">
|
||||
OpenTracks gailuaren GPS sentsorea erabiltzen du kokapena etengabe grabatzeko.
|
||||
GPS gailuak datu okerrak salatzen baditu (adibidez, kokapena, abiadura, kota), OpenTracks-ek ezin du ezer egin.</string>
|
||||
|
||||
<string name="help_data_storage_title">Nola gordetzen du OpenTracks ek sentsoreen datuak?</string>
|
||||
<string name="help_data_storage_content">
|
||||
Sentsoreen datuak grabatutako kokapen bakoitzeko gordetzen dira.
|
||||
Bihotz-maiztasuneko sentsoreari dagokionez, horrek esan nahi du <i> bat </i> bihotz-tasaren neurketa kokapen bakoitzean gordetzen dela.
|
||||
Geldirik egon edo barrualdean bazaude, ez da sentsoreen daturik grabatuko (baina erakutsiko da).</string>
|
||||
|
||||
<string name="help_data_storage_content"> Sentsoreen datuak grabatutako kokapen bakoitzeko gordetzen dira. Bihotz-maiztasuneko sentsoreari dagokionez, horrek esan nahi du <i> bat </i> bihotz-tasaren neurketa kokapen bakoitzean gordetzen dela. Geldirik egon edo barrualdean bazaude, ez da sentsoreen daturik grabatuko (baina erakutsiko da). </string>
|
||||
<string name="help_gps_troubleshooting_title">OpenTracks ez dira kokapenak grabatzen?</string>
|
||||
<string name="help_gps_troubleshooting_content">
|
||||
Zer egin dezakezu: \n
|
||||
a) egiaztatu GPSa gaituta dagoen ala ez, \n
|
||||
b) GPS harrera hobetu (adibidez, kanpora joan), \n
|
||||
c) egiaztatu <a href="https://f-droid.org/en/packages/com.android.gpstest.osmdroid/"> GPSTest </a> aplikazioarekin eta \n
|
||||
d) desgaitu energia aurreztea. \n
|
||||
<string name="help_gps_troubleshooting_content"> Zer egin dezakezu:
|
||||
\n a) egiaztatu GPSa gaituta dagoen ala ez,
|
||||
\n b) GPS harrera hobetu (adibidez, kanpora joan),
|
||||
\n c) egiaztatu <a href="https://f-droid.org/en/packages/com.android.gpstest.osmdroid/"> GPSTest </a> aplikazioarekin eta
|
||||
\n d) desgaitu energia aurreztea.
|
||||
\n
|
||||
Dena laguntzen ez baduzu, jakinarazi akats bat.</string>
|
||||
|
||||
\n Dena laguntzen ez baduzu, jakinarazi akats bat. </string>
|
||||
<string name="help_support_title">Nola onartzen dut OpenTracks?</string>
|
||||
<string name="help_support_content">
|
||||
Aukera hau: \ n
|
||||
a) zabaldu OpenTracks-i buruzko hitza, \ n
|
||||
b) <a href="https://github.com/OpenTracksApp/OpenTracks/issues"> proposatu ezaugarriak </a>, \ n
|
||||
c) <a href="https://github.com/OpenTracksApp/OpenTracks/issues"> txostenen akatsak </a>, \ n
|
||||
d) softwarearen garapenean laguntzeko, \ n
|
||||
e) erosi <a href="https://play.google.com/store/apps/details?id=de.dennisguse.opentracks.playstore"> Google Play Store </a>, \ n
|
||||
f) eman, eta \ n
|
||||
g) Egin uste duzu OpenTracks-ek laguntzen duela.</string>
|
||||
|
||||
<string name="help_support_content"> Ahal izango duzu:
|
||||
\n a) buruz hitz zabaltzeko OpenTracks,
|
||||
\n b) <a href="https://github.com/OpenTracksApp/OpenTracks/issues">proposatzen ezaugarriak</a>,
|
||||
\n c) <a href="https://github.com/OpenTracksApp/OpenTracks/issues">txostena bugs</a>,
|
||||
\n d) laguntza software garapena,
|
||||
\n e) erosi <a href="https://play.google.com/store/apps/details?id=de.dennisguse.opentracks.playstore">Google Play Denda</a>,
|
||||
\n f) eman, eta
|
||||
\n g) ez edozein dela uste duzu laguntzen OpenTracks. </string>
|
||||
<!-- Activity Type -->
|
||||
<string name="activity_type_airplane">hegazkina</string>
|
||||
<string name="activity_type_atv">Lurreko ibilgailu guztiak</string>
|
||||
@@ -257,7 +236,7 @@ g) Egin uste duzu OpenTracks-ek laguntzen duela.</string>
|
||||
<!-- Marker -->
|
||||
<string name="marker_add_error">Ezin da markatzailea txertatu. Ez dago GPS seinalerik. Saiatu berriro.</string>
|
||||
<string name="marker_add_success">Markatzailea txertatu da</string>
|
||||
<string name="marker_add_canceled">Bertan behera utzi da; ez da markatzailea gehitu.</string>
|
||||
<string name="marker_add_canceled">Bertan behera utzi da; ez da markatzailea gehitu</string>
|
||||
<string name="marker_add_photo_canceled">Ezeztatu, ez da argazki-markarik gehitu</string>
|
||||
<string name="marker_delete_all_confirm_message">Ibilbideko markatzaile guztiak behin betiko ezabatuko dira gailutik</string>
|
||||
<string name="marker_delete_one_confirm_message">Markatzailea behin betiko ezabatuko da gailutik</string>
|
||||
@@ -309,16 +288,16 @@ g) Egin uste duzu OpenTracks-ek laguntzen duela.</string>
|
||||
<!-- Sensor State -->
|
||||
<string name="sensor_state_battery">Bateria maila</string>
|
||||
<string name="sensor_state_cadence">Erritmoa</string>
|
||||
<string name="sensor_state_cadence_value">%1$d rpm</string>
|
||||
<string name="sensor_state_cadence_value">%1$d mbi</string>
|
||||
<string name="sensor_state_disconnected">Deskonektatuta</string>
|
||||
<string name="sensor_state_heart_rate">Maiztasun kardiakoa</string>
|
||||
<string name="sensor_state_heart_rate_value">%1$d bpm</string>
|
||||
<string name="sensor_state_heart_rate_value">%1$d mbb</string>
|
||||
<string name="sensor_state_power">Potentzia</string>
|
||||
<string name="sensor_state_power_value">%1$d W</string>
|
||||
<string name="sensor_not_known">Sensor %1$s ezezaguna da. Egiaztatu ezarpenak.</string>
|
||||
<string name="sensor_could_not_scan">Ezin izan da Bluetooth gailuak bilatu (%1$i errorea).</string>
|
||||
<string name="bluetooth_disabled">Mesedez, aktibatu Bluetooth.</string>
|
||||
<string name="settings_not_while_recording">Ez dago erabilgarri ibilbidea grabatzen ari den bitartean.</string>
|
||||
<string name="settings_not_while_recording">Ez dago erabilgarri ibilbidea grabatzen ari den bitartean</string>
|
||||
<string name="settings_show">Erakutsi</string>
|
||||
<!-- Settings Advanced -->
|
||||
<string name="settings_advanced">Aurreratua</string>
|
||||
@@ -360,17 +339,17 @@ g) Egin uste duzu OpenTracks-ek laguntzen duela.</string>
|
||||
<string name="settings_sensor_bluetooth_sensor">Bluetooth sentsorea</string>
|
||||
<string name="settings_sensor_connecting">Honekin konektatuta: %1$s</string>
|
||||
<string name="settings_sensor_disconnected">Deskonektatuta %1$s</string>
|
||||
|
||||
<!-- Settings Stats -->
|
||||
<string name="settings_stats_coordinate">Latitudea/Longitudea</string>
|
||||
<string name="settings_stats_rate_title">Erritmo hobetsia</string>
|
||||
<string name="settings_stats_units_imperial">Inperiala (mi, ft)</string>
|
||||
<string name="settings_stats_units_metric">Metrikoa (km, m)</string>
|
||||
<string name="settings_stats_units_title">Unitate hobetsiak</string>
|
||||
|
||||
<!-- Share Track -->
|
||||
<string name="share_track_share_file">Partekatu ibilbide-fitxategia</string>
|
||||
<string name="share_track_share_file_body">Ibilbidea interesgarria gerta dakizukeela uste dut.\n\nIbilbidea ikusteko, erabili Google Earth edo beste kartografia-softwareren bat.\n\n%1$s</string>
|
||||
<string name="share_track_share_file_body">Ibilbidea interesgarria gerta dakizukeela uste dut.
|
||||
\n
|
||||
\n%1$s</string>
|
||||
<string name="share_track_subject">Bide bat partekatu nahi nuke zurekin</string>
|
||||
<!-- Stats -->
|
||||
<string name="stats_average_moving_pace">Batez besteko pausoa, mugitzerakoan</string>
|
||||
@@ -489,7 +468,7 @@ g) Egin uste duzu OpenTracks-ek laguntzen duela.</string>
|
||||
<string name="voice_speed_miles_per_hour_other">%1$.1f milia orduko</string>
|
||||
<string name="voice_speed_miles_per_hour_two">2 milia orduko</string>
|
||||
<string name="voice_speed_miles_per_hour_zero">0 milia orduko</string>
|
||||
<string name="voice_template">OpenTracks. Distantzia: %1$s. Denbora: %2$s. Abiadura: %3$s.</string>
|
||||
<string name="voice_template">OpenTracks. Distantzia: %1$s. Denbora: %2$s. Abiadura: %3$s</string>
|
||||
<string name="voice_total_distance_kilometers_few">distantzia, guztira: %1$.2f kilometro</string>
|
||||
<string name="voice_total_distance_kilometers_many">distantzia, guztira: %1$.2f kilometro</string>
|
||||
<string name="voice_total_distance_kilometers_one">distantzia, guztira: 1 kilometro</string>
|
||||
@@ -558,5 +537,78 @@ g) Egin uste duzu OpenTracks-ek laguntzen duela.</string>
|
||||
<!-- Share image -->
|
||||
<string name="share_image_subject">Irudi bat zurekin partekatu nahi nuke</string>
|
||||
<string name="share_image_body">Irudi hau interesatuko zaizuela uste dut.</string>
|
||||
|
||||
</resources>
|
||||
<string name="open_track_as_KMZ">Ireki gisa KMZ</string>
|
||||
<string name="open_track_using_dashboard">Ireki erabiliz OpenTracks-Arbel</string>
|
||||
<string name="open_track_as_trackfileformat">Ireki gisa %1$s</string>
|
||||
<string name="activity_type_escooter">eskooterra</string>
|
||||
<string name="settings_recording_fullscreen_on_while_recording_summary">Grabatzen ari zaren bitartean, pantaila osoan ikusi.</string>
|
||||
<string name="settings_recording_fullscreen_on_while_recording_title">Pantaila osoa</string>
|
||||
<string name="value_float_mile_hour">%1$.2f mph</string>
|
||||
<string name="value_float_kilometer_hour">%1$.2f km/h</string>
|
||||
<string name="value_pace_mile">%1$d:%2$02d min/mi</string>
|
||||
<string name="settings_default_export_uri_title">Track esportazio-direktorioa</string>
|
||||
<string name="instant_export_enabled_title">Berehalako post-entrenamendu esportatu</string>
|
||||
<string name="instant_export_enabled_summary">Esportazio-pista biltegiratze ondoren grabaketa amaitu da</string>
|
||||
<string name="value_pace_kilometer">%1$d:%2$02d min/km</string>
|
||||
<string name="interval_list_empty_message">Ez dira edozein tarte oraindik</string>
|
||||
<string name="aggregated_stats_empty_message">Grabatu zure lehen pista ikusteko batzen estatistikak</string>
|
||||
<string name="open_track_as_GPX">Ireki gisa GPX</string>
|
||||
<string name="description_elevation_loss">Kota galera: %1$d m (%2$d ft)</string>
|
||||
<string name="export_error_post_workout">Post-entrenamendu esportazio huts egin du, mesedez, egiaztatu esportazio direktorioa.</string>
|
||||
<string name="export_progress_summary_new_msg">Pista berriak esportatutako</string>
|
||||
<string name="export_progress_summary_overwrite_msg">Ibilbideak gainidatzi</string>
|
||||
<string name="generic_overwrite">Gainidatzi</string>
|
||||
<string name="generic_separator_done_total">/</string>
|
||||
<string name="settings_sensor_wheel_circumference">Gurpila zirkunferentzia (mm)</string>
|
||||
<string name="sensor_state_heart_rate_max">Max Bihotzean tasa</string>
|
||||
<string name="import_thread_interrupted">Haria eten</string>
|
||||
<string name="import_unsupported_format">Onartzen ez den fitxategi-formatua</string>
|
||||
<string name="import_progress_summary_ok_msg">Fitxategiak inportatu</string>
|
||||
<string name="export_track_already_exists_msg">Pista %1$s dagoeneko existitzen helmugara direktorioa.</string>
|
||||
<string name="export_all_do_it_for_all">ez da gatazka guztiak</string>
|
||||
<string name="export_track_errors">Ibilbideak ez esportatutako</string>
|
||||
<string name="settings_prevent_reimport_tracks_title">Saihesteko re-inportazio ibilbideak</string>
|
||||
<plurals name="generic_completed_with_errors">
|
||||
<item quantity="one">Osatu bat errorea</item>
|
||||
<item quantity="other">Bukatu %1$d akatsak</item>
|
||||
</plurals>
|
||||
<string name="sensor_state_heart_rate_avg">Avg Bihotzean tasa</string>
|
||||
<string name="sensor_state_cadence_max">Max Kadentzia</string>
|
||||
<string name="sensor_state_cadence_avg">Avg Kadentzia</string>
|
||||
<string name="settings_default_trackfileformat">Export/partekatzea fitxategi-formatua</string>
|
||||
<string name="settings_night_mode_title">UI Gaia</string>
|
||||
<string name="settings_night_mode_option_system">Sistema</string>
|
||||
<string name="settings_night_mode_option_day">Egun</string>
|
||||
<string name="settings_night_mode_option_night">Gauean</string>
|
||||
<string name="sensor_state_power_avg">Avg Boterea</string>
|
||||
<string name="track_detail_intervals_tab">Tarteak</string>
|
||||
<string name="stats_split_interval">Zatitu tarte</string>
|
||||
<string name="stats_loss">Galera</string>
|
||||
<string name="stats_intervals">Tarteak</string>
|
||||
<string name="voice_pace_lap">Itzulian-denbora %1$s</string>
|
||||
<string name="voice_speed_lap">Itzulian abiadura %1$s</string>
|
||||
<string name="toast_message_instant_track_was_exported">Azken pista esportatutako</string>
|
||||
<string name="hold_to_pause">Eutsi eteteko</string>
|
||||
<string name="hold_to_stop">Eutsi gelditzeko</string>
|
||||
<string name="export_progress_summary_skip_msg">Ibilbideak saltatu</string>
|
||||
<string name="export_progress_summary_errors_msg">Akatsak</string>
|
||||
<string name="generic_open_track">Ireki Track</string>
|
||||
<string name="generic_skip">Saltatzeko</string>
|
||||
<string name="generic_show_errors">Erakutsi Akatsak</string>
|
||||
<string name="generic_completed">Bukatu</string>
|
||||
<string name="generic_click_twice_cancel">Egin klik berriro bertan behera uzteko</string>
|
||||
<string name="gps_disabled_msg">GPS egokitua</string>
|
||||
<string name="import_progress_summary_errors_msg">Akatsak</string>
|
||||
<string name="import_parser_error">Analizatzailea errorea: %1$s</string>
|
||||
<string name="import_unable_to_import_file">Ezin izan da inportazio fitxategia: %1$s</string>
|
||||
<string name="import_no_kml_file_found">Ez doc.kml fitxategia aurkitu KMZ</string>
|
||||
<string name="import_error_info">%1$s: %2$s</string>
|
||||
<string name="import_progress_summary_exists_msg">Dagoeneko existitzen da</string>
|
||||
<string name="import_prevent_reimport">Ezarri duzu saihesteko re-import track</string>
|
||||
<string name="import_file_imported">File %1$s inportatutako</string>
|
||||
<string name="import_error_list_dialog_title">Fitxategiak ez inportatutako</string>
|
||||
<string name="gps_fixed_and_ready">GPS konpondu eta prest</string>
|
||||
<string name="track_distance_notification">Distantzia: %1$s</string>
|
||||
<string name="track_speed_notification">Abiadura: %1$s</string>
|
||||
<string name="track_pace_notification">Erritmoa: %1$s</string>
|
||||
</resources>
|
||||
@@ -38,20 +38,14 @@ limitations under the License.
|
||||
<string name="help_storage_pictures_title">Miten OpenTracks tallentaa merkkeihin liittyen otetut kuvat?</string>
|
||||
<string name="help_storage_pictures_content">Ne säilytetään sovelluksen sisäisessä tallennustilassa (eli ne näkyvät vain OpenTracksin sisällä).</string>
|
||||
<string name="help_track_export_title">Miten vien tai jaan tallennettuja reittejä?</string>
|
||||
<string name="help_track_export_content">
|
||||
Jäljet voi viedä asetuksista KMZ, KML tai GPX -muodossa.
|
||||
KMZ/KML sisältävät kaiken; GPX vain sijainnit.
|
||||
<i>Jakokuvakkeella</i> jaetut jäljet jaetaan KMZ-muodossa.</string>
|
||||
<string name="help_track_export_content"> Jäljet voi viedä asetuksista KMZ, KML tai GPX -muodossa. KMZ/KML sisältävät kaiken; GPX vain sijainnit. <i>Jakokuvakkeella</i> jaetut jäljet jaetaan KMZ-muodossa. </string>
|
||||
<string name="help_data_google_play_services_title">Käyttääkö tai vaatiiko OpenTracks Google Play -palveluita?</string>
|
||||
<string name="help_recording_title">Miten OpenTracks tallentaa tietoja?</string>
|
||||
<string name="help_recording_content">
|
||||
OpenTracks käyttää laitteen GPS:ää paikkatietojen jatkuvaan tallentamiseen.
|
||||
Jos GPS raportoi epätarkkoja tietoja (sijainti, nopeus, korkeus) OpenTracks ei voi tehdä sille mitään.</string>
|
||||
<string name="help_data_storage_title">Miten OpenTracks tallentaa anturidatan?</string>
|
||||
<string name="help_data_storage_content">
|
||||
Anturidata tallennetaan jokaiselle paikkapisteelle.
|
||||
Esimerkiksi sykemittarista tallennetaan vain <i>yksi</i> sykemittaus tallennettua paikkaa kohden.
|
||||
Jos olet paikallaan tai sisätiloissa, mitään anturidataa ei tallenneta (mutta se näytetään).</string>
|
||||
<string name="help_data_storage_content"> Anturidata tallennetaan jokaiselle paikkapisteelle. Esimerkiksi sykemittarista tallennetaan vain <i>yksi</i> sykemittaus tallennettua paikkaa kohden. Jos olet paikallaan tai sisätiloissa, mitään anturidataa ei tallenneta (mutta se näytetään). </string>
|
||||
<string name="help_gps_troubleshooting_title">Miksi OpenTracks ei tallenna sijaintia?</string>
|
||||
<string name="help_gps_troubleshooting_content"> Mitä voit tehdä:
|
||||
\na) tarkista, onko GPS käytössä,
|
||||
@@ -61,15 +55,13 @@ limitations under the License.
|
||||
\n
|
||||
\nJos kaikki ei auta, ilmoita virheestä. </string>
|
||||
<string name="help_support_title">Miten voin tukea OpenTracksin kehitystä?</string>
|
||||
<string name="help_support_content">
|
||||
Voit:\n
|
||||
a) levittää sanaa OpenTracksista,\n
|
||||
b) <a href="https://github.com/OpenTracksApp/OpenTracks/issues">ehdottaa ominaisuuksia</a>,\n
|
||||
c) <a href="https://github.com/OpenTracksApp/OpenTracks/issues">ilmoittaa ohjelmistovirheistä</a>,\n
|
||||
d) auttaa ohjelmiston kehittämisessä,\n
|
||||
e) ostaa sovelluksen <a href="https://play.google.com/store/apps/details?id=de.dennisguse.opentracks.playstore">Google Play -kaupasta</a>,\n
|
||||
f) lahjoittaa ja
|
||||
g) tehdä mikä mielestäsi edistäisi OpenTracksia.</string>
|
||||
<string name="help_support_content"> Voit:
|
||||
\n a) levittää sanaa OpenTracksista,
|
||||
\n b) <a href="https://github.com/OpenTracksApp/OpenTracks/issues">ehdottaa ominaisuuksia</a>,
|
||||
\n c) <a href="https://github.com/OpenTracksApp/OpenTracks/issues">ilmoittaa ohjelmistovirheistä</a>,
|
||||
\n d) auttaa ohjelmiston kehittämisessä,
|
||||
\n e) ostaa sovelluksen <a href="https://play.google.com/store/apps/details?id=de.dennisguse.opentracks.playstore">Google Play -kaupasta</a>,
|
||||
\n f) lahjoittaa ja g) tehdä mikä mielestäsi edistäisi OpenTracksia. </string>
|
||||
<string name="open_track_using_dashboard">Avaa OpenTracks-Dashboardilla</string>
|
||||
<string name="open_track_as_KMZ">Avaa KMZ-muodossa</string>
|
||||
<string name="open_track_as_GPX">Avaa GPX-muodossa</string>
|
||||
@@ -90,7 +82,7 @@ limitations under the License.
|
||||
<string name="activity_type_driving_bus">bussilla ajo</string>
|
||||
<string name="activity_type_driving_car">autolla ajo</string>
|
||||
<string name="activity_type_ferry">lautta</string>
|
||||
<string name="activity_type_frisbee">frisbee</string>
|
||||
<string name="activity_type_frisbee">liitokiekko</string>
|
||||
<string name="activity_type_gliding">liitely</string>
|
||||
<string name="activity_type_hang_gliding">riippuliito</string>
|
||||
<string name="activity_type_helicopter">helikopteri</string>
|
||||
@@ -568,7 +560,7 @@ jotta ne voidaan tuoda kohteeseen %1$s.</string>
|
||||
<string name="instant_export_enabled_summary">Vie raita varastoon tallennuksen jälkeen on valmis</string>
|
||||
<string name="voice_speed_lap">Kierrosnopeus %1$s</string>
|
||||
<string name="value_pace_kilometer">%1$d:%2$02d min/km</string>
|
||||
<string name="settings_sensor_wheel_circumference">Pyörän koko (mm)</string>
|
||||
<string name="settings_sensor_wheel_circumference">Pyörän ympärysmitta (mm)</string>
|
||||
<string name="stats_intervals">Välein</string>
|
||||
<string name="import_thread_interrupted">Lanka keskeytetty</string>
|
||||
<string name="import_unsupported_format">Tukematon tiedostomuoto</string>
|
||||
@@ -582,4 +574,41 @@ jotta ne voidaan tuoda kohteeseen %1$s.</string>
|
||||
<string name="import_progress_summary_ok_msg">Tuodut tiedostot</string>
|
||||
<string name="import_progress_summary_exists_msg">Jo olemassa</string>
|
||||
<string name="import_progress_summary_errors_msg">Virhe</string>
|
||||
<string name="generic_click_twice_cancel">Peruuta uudelleen napsauttamalla</string>
|
||||
<string name="settings_recording_fullscreen_on_while_recording_title">Kokoruutu</string>
|
||||
<string name="interval_list_empty_message">Ei ole mitään intervallia vielä</string>
|
||||
<string name="export_error_post_workout">Treenin jälkeinen vienti epäonnistui, tarkista vientihakemisto.</string>
|
||||
<string name="export_progress_summary_overwrite_msg">Kappaleet ylikirjoitettu</string>
|
||||
<plurals name="generic_completed_with_errors">
|
||||
<item quantity="one">Täydennetty yhdellä virheellä</item>
|
||||
<item quantity="other">Täydennetty %1$d virheillä</item>
|
||||
</plurals>
|
||||
<string name="export_track_already_exists_msg">Kappale %1$s on jo olemassa kohdehakemistossa.</string>
|
||||
<string name="export_all_do_it_for_all">tee se kaikkien konfliktien vuoksi</string>
|
||||
<string name="export_track_errors">Kappaleita ei viety</string>
|
||||
<string name="settings_recording_fullscreen_on_while_recording_summary">Tallennuksen aikana, läsnä koko näytön.</string>
|
||||
<string name="settings_night_mode_title">UI-teema</string>
|
||||
<string name="settings_night_mode_option_system">Järjestelmä</string>
|
||||
<string name="settings_night_mode_option_day">Päivä</string>
|
||||
<string name="settings_night_mode_option_night">Yö</string>
|
||||
<string name="export_progress_summary_new_msg">Uusia kappaleita viety</string>
|
||||
<string name="export_progress_summary_skip_msg">Kappaleet ohitettu</string>
|
||||
<string name="export_progress_summary_errors_msg">Virhe</string>
|
||||
<string name="generic_overwrite">Korvata</string>
|
||||
<string name="generic_skip">Ohittaa</string>
|
||||
<string name="generic_separator_done_total">/</string>
|
||||
<string name="generic_show_errors">Näytä Virheet</string>
|
||||
<string name="gps_disabled_msg">GPS pois käytöstä</string>
|
||||
<string name="generic_completed">Täydellinen</string>
|
||||
<string name="sensor_state_power_avg">Avg Teho</string>
|
||||
<string name="description_elevation_loss">Korkeussijaintitappio: %1$d m (%2$d ft)</string>
|
||||
<string name="sensor_state_heart_rate_max">Maksimisyke</string>
|
||||
<string name="sensor_state_heart_rate_avg">Avg syke</string>
|
||||
<string name="value_float_kilometer_hour">%1$.2f k/t</string>
|
||||
<string name="value_pace_mile">%1$d:%2$02d min/mailia</string>
|
||||
<string name="activity_type_escooter">eskootteri</string>
|
||||
<string name="sensor_state_cadence_avg">Keskim.Poljinnopeus</string>
|
||||
<string name="sensor_state_cadence_max">Suurin Poljinnopeus</string>
|
||||
<string name="value_float_mile_hour">%1$.2f mt</string>
|
||||
<string name="generic_open_track">Avaa Raita</string>
|
||||
</resources>
|
||||
@@ -73,7 +73,7 @@ Infatti, un\'applicazione deve supportare <i>ACTION_VIEW</i> con MIME <i>applica
|
||||
<string name="activity_type_driving_bus">guidare autobus</string>
|
||||
<string name="activity_type_driving_car">guidare auto</string>
|
||||
<string name="activity_type_ferry">traghetto</string>
|
||||
<string name="activity_type_frisbee">frisbee</string>
|
||||
<string name="activity_type_frisbee">di frisbee</string>
|
||||
<string name="activity_type_gliding">aliante</string>
|
||||
<string name="activity_type_hang_gliding">deltaplano</string>
|
||||
<string name="activity_type_helicopter">elicottero</string>
|
||||
@@ -106,7 +106,7 @@ Infatti, un\'applicazione deve supportare <i>ACTION_VIEW</i> con MIME <i>applica
|
||||
<string name="activity_type_skate_boarding">skateboarding</string>
|
||||
<string name="activity_type_skating">pattinaggio</string>
|
||||
<string name="activity_type_skiing">sci</string>
|
||||
<string name="activity_type_sky_jumping">sky jumping</string>
|
||||
<string name="activity_type_sky_jumping">salto nel cielo</string>
|
||||
<string name="activity_type_sled">slitta</string>
|
||||
<string name="activity_type_snow_boarding">snowboard</string>
|
||||
<string name="activity_type_snow_shoeing">racchette da neve</string>
|
||||
@@ -118,7 +118,7 @@ Infatti, un\'applicazione deve supportare <i>ACTION_VIEW</i> con MIME <i>applica
|
||||
<string name="activity_type_trail_hiking">escursionismo su sentiero</string>
|
||||
<string name="activity_type_trail_running">corsa campestre</string>
|
||||
<string name="activity_type_train">treno</string>
|
||||
<string name="activity_type_ultimate_frisbee">ultimate frisbee</string>
|
||||
<string name="activity_type_ultimate_frisbee">frisbee finale</string>
|
||||
<string name="activity_type_unknown">sconosciuto</string>
|
||||
<string name="activity_type_wakeboarding">wakeboard</string>
|
||||
<string name="activity_type_walking">camminare</string>
|
||||
@@ -459,7 +459,7 @@ Infatti, un\'applicazione deve supportare <i>ACTION_VIEW</i> con MIME <i>applica
|
||||
<string name="waypoint_type_atm">Bancomat</string>
|
||||
<string name="waypoint_type_bank">banca</string>
|
||||
<string name="waypoint_type_bar">bar</string>
|
||||
<string name="waypoint_type_barbecue">barbecue</string>
|
||||
<string name="waypoint_type_barbecue">grigliata</string>
|
||||
<string name="waypoint_type_building">edificio</string>
|
||||
<string name="waypoint_type_bus_stop">fermata dell\'autobus</string>
|
||||
<string name="waypoint_type_cafe">caffè</string>
|
||||
@@ -503,7 +503,7 @@ Infatti, un\'applicazione deve supportare <i>ACTION_VIEW</i> con MIME <i>applica
|
||||
<string name="waypoint_type_toilet">toilette</string>
|
||||
<string name="waypoint_type_tourist_information">informazioni turistiche</string>
|
||||
<string name="waypoint_type_tree">.</string>
|
||||
<string name="waypoint_type_tunnel">tunnel</string>
|
||||
<string name="waypoint_type_tunnel">galleria</string>
|
||||
<string name="waypoint_type_waterfall">cascata</string>
|
||||
<string name="waypoint_type_water_fountain">fontana ad acqua</string>
|
||||
<string name="waypoint_type_waypoint">tappa</string>
|
||||
@@ -519,7 +519,7 @@ Infatti, un\'applicazione deve supportare <i>ACTION_VIEW</i> con MIME <i>applica
|
||||
<string name="help_track_export_content"> Le tracce possono essere esportate dalle Impostazioni con formato KMZ, KML o GPX. KMZ/KML contiene tutti i dati; GPX contiene solo le posizioni. Le tracce condivise tramite l\'icona <i>condividi</i> sono esportate come KMZ. </string>
|
||||
<string name="help_recording_content">OpenTracks utilizza il sensore GPS del dispositivo per registrare continuativamente la posizione. Se il sensore GPS riporta dati imprecisi (ad es. posizione, velocità, altitudine), OpenTracks non può fare nulla.</string>
|
||||
<string name="help_data_storage_content"> I dati del sensore vengono memorizzati per ogni posizione registrata. Riguardo al sensore della frequenza cardiaca, ciò significa che viene memorizzata solo <i>una</i> misurazione della frequenza cardiaca per cambiamento di posizione. Se si è fermi o si è al chiuso, non verranno registrati i dati del sensore (ma saranno mostrati). </string>
|
||||
<string name="help_gps_troubleshooting_content"> Cosa puoi fare:
|
||||
<string name="help_gps_troubleshooting_content"> Cosa puoi fare:
|
||||
\na) verificare se il GPS è abilitato,
|
||||
\nb) migliorare la ricezione GPS (ad esempio, andare all\'aperto),
|
||||
\nc) verificare la ricezione del segnale GPS con l\' applicazione<a href="https://f-droid.org/en/packages/com.android.gpstest.osmdroid/">GPSTest</a>, e
|
||||
@@ -603,4 +603,16 @@ Infatti, un\'applicazione deve supportare <i>ACTION_VIEW</i> con MIME <i>applica
|
||||
<string name="import_progress_summary_ok_msg">File importati</string>
|
||||
<string name="import_progress_summary_exists_msg">Esiste già</string>
|
||||
<string name="import_progress_summary_errors_msg">Errore</string>
|
||||
<string name="sensor_state_power_avg">Potenza media</string>
|
||||
<string name="sensor_state_heart_rate_max">Frequenza cardiaca max</string>
|
||||
<string name="value_pace_kilometer">%1$d:%2$02d min/cm</string>
|
||||
<string name="value_float_kilometer_hour">%1$.2f cp/s</string>
|
||||
<string name="stats_loss">Perdita</string>
|
||||
<string name="settings_sensor_wheel_circumference">Circonferenza della ruota (mm)</string>
|
||||
<string name="description_elevation_loss">Perdita di quota: %1$d m (%2$d ft)</string>
|
||||
<string name="sensor_state_heart_rate_avg">Tasso cardiaco medio</string>
|
||||
<string name="sensor_state_cadence_max">Cadenza max</string>
|
||||
<string name="sensor_state_cadence_avg">Cadenza media</string>
|
||||
<string name="value_float_mile_hour">%1$.2f mpo</string>
|
||||
<string name="value_pace_mile">%1$d:%2$02d min/mi</string>
|
||||
</resources>
|
||||
@@ -603,4 +603,7 @@ limitations under the License.
|
||||
<string name="sensor_state_heart_rate_max">Maks puls</string>
|
||||
<string name="sensor_state_power_avg">Gjennomsnittlig effekt</string>
|
||||
<string name="settings_sensor_wheel_circumference">Hjulomkrets (mm)</string>
|
||||
<string name="track_distance_notification">Distanse: %1$s</string>
|
||||
<string name="track_speed_notification">Hastighet: %1$s</string>
|
||||
<string name="track_pace_notification">Tempo: %1$s</string>
|
||||
</resources>
|
||||
@@ -1,4 +1,5 @@
|
||||
<?xml version="1.0" encoding="utf-8"?><!--
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!--
|
||||
Copyright 2010 Google Inc.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
@@ -12,8 +13,7 @@ distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
-->
|
||||
<resources>
|
||||
--><resources>
|
||||
<string name="about_preference_title">Over OpenTracks</string>
|
||||
<string name="about_description"><i>OpenTracks</i> is een sporttrackingapplicatie die uw privacy volledig respecteert.</string>
|
||||
<string name="about_url">Project webpagina: <a href="%1$s">%1$s</a></string>
|
||||
@@ -23,71 +23,45 @@ limitations under the License.
|
||||
<string name="about_libraries">Bibliotheken</string>
|
||||
<!-- Help -->
|
||||
<string name="help_feature_title">OpenTracks heeft de volgende kenmerken:</string>
|
||||
<string name="help_feature_content">
|
||||
* creëer GPS tracks
|
||||
* gebruik een hartslagsensor (alleen Bluetooth LE)...
|
||||
* spraakaankondigingen (op tijd of afstand)...
|
||||
* creëer markers (incl. fotos)
|
||||
* werkt volledig offline
|
||||
* toon tracks op een kaart (zie hieronder)</string>
|
||||
|
||||
<string name="help_feature_content">* GPS tracks maken
|
||||
\n * een hartslagsensor gebruiken (alleen Bluetooth LE)
|
||||
\n * spraakaankondigingen (op tijd of afstand)
|
||||
\n * markers maken (incl. foto\'s)
|
||||
\n * werkt volledig offline
|
||||
\n * toon tracks op een kaart (zie hieronder)</string>
|
||||
<string name="help_track_map_title">Hoe toon je een spoor op een kaart?</string>
|
||||
<string name="help_track_map_content">
|
||||
Gebruik het kleine kaarticoontje.
|
||||
a)
|
||||
(live updates) Installeer <a href="https://github.com/OpenTracksApp/OSMDashboard">OSMDashboard</a>.
|
||||
Het biedt online of offline kaarten via <a href="https://www.openstreetmap.org">OpenStreetMap</a>.\n
|
||||
Eigenlijk is OSMDashboard een aparte app die alleen (tijdelijk) toegang heeft tot de gegevens van track(s) die getoond gaan worden.
|
||||
|
||||
\n
|
||||
b)
|
||||
Via een normale kaarttoepassing (bijv. <a href="http://osmand.net/">OsmAnd</a> en <a href="https://maps.me/">MAPS.ME</a>).
|
||||
Deze app moet het KMZ-bestandsformaat ondersteunen (alleen locaties en tijdstempels).
|
||||
In feite moet een app <i>ACTION_VIEW</i> ondersteunen met MIME <i>application/vnd.google-earth.kmz</i>.</string>
|
||||
|
||||
<string name="help_track_map_content"> Gebruik het kleine kaartpictogram.
|
||||
\n a) (live updates) Installeer <a href="https://github.com/OpenTracksApp/OSMDashboard">OSMDashboard</a>. Het biedt online of offline kaarten via <a href="https://www.openstreetmap.org">OpenStreetMap</a>.
|
||||
\n Eigenlijk is OSMDashboard een aparte app die alleen (tijdelijk) toegang heeft tot de gegevens van de track(s) die getoond gaan worden.
|
||||
\n b) Via een normale kaartapplicatie (bijv. <a href="http://osmand.net/">OsmAnd</a> en <a href="https://maps.me/">MAPS.ME</a>). Deze apps moeten het KMZ-bestandsformaat ondersteunen (alleen locaties en tijdstempels). In feite moet een app <i>ACTION_VIEW</i> ondersteunen met MIME <i>application/vnd.google-earth.kmz</i>. </string>
|
||||
<string name="help_storage_pictures_title">Hoe slaat OpenTracks fotos op die als markers zijn genomen?</string>
|
||||
<string name="help_storage_pictures_content">Deze worden opgeslagen in de interne opslag van de app (d.w.z. alleen toegankelijk binnen OpenTracks).</string>
|
||||
|
||||
<string name="help_track_export_title">Hoe kan ik opgenomen tracks exporteren/delen?</string>
|
||||
<string name="help_track_export_content">
|
||||
Tracks kunnen worden geëxporteerd vanuit de instellingen als KMZ, KML of GPX.
|
||||
KMZ/KML alle gegevens; GPX alleen locaties.
|
||||
Tracks die worden gedeeld via het <i>share-icoontje</i> worden gedeeld als KMZ.</string>
|
||||
|
||||
<string name="help_track_export_content"> Tracks kunnen worden geëxporteerd vanuit de instellingen als KMZ, KML of GPX. KMZ/KML alle gegevens; GPX alleen locaties. Tracks die worden gedeeld via het <i>share-icoontje</i> worden gedeeld als KMZ </string>
|
||||
<string name="help_data_google_play_services_title">Heeft OpenTracks Google Play Service nodig/gebruikt?</string>
|
||||
|
||||
<string name="help_recording_title">Hoe legt OpenTracks gegevens vast?</string>
|
||||
<string name="help_recording_content">
|
||||
OpenTracks gebruikt de GPS-sensor van het apparaat om de locatie continu te registreren.
|
||||
Als het GPS-apparaat onnauwkeurige gegevens rapporteert (bijv. locatie, snelheid, hoogte), kan OpenTracks daar niets aan doen.</string>
|
||||
|
||||
<string name="help_data_storage_title">Hoe slaat OpenTracks sensorgegevens op?</string>
|
||||
<string name="help_data_storage_content">
|
||||
De sensorgegevens worden per opgenomen locatie opgeslagen.
|
||||
In termen van een hartslagsensor betekent dit dat slechts <i>een</i> hartslagmeting per locatie wordt opgeslagen.
|
||||
Als u stilstaat of binnenshuis bent, worden er geen sensorgegevens geregistreerd (maar wel getoond).</string>
|
||||
|
||||
<string name="help_data_storage_content"> De sensorgegevens worden per opgenomen locatie opgeslagen. In termen van een hartslagsensor betekent dit dat slechts <i>een</i> hartslagmeting per locatie wordt opgeslagen. Als u stilstaat of binnenshuis bent, worden er geen sensorgegevens geregistreerd (maar wel getoond) </string>
|
||||
<string name="help_gps_troubleshooting_title">OpenTracks neemt geen locaties op?</string>
|
||||
<string name="help_gps_troubleshooting_content">
|
||||
Wat kun je doen?
|
||||
a) controleer of de GPS is ingeschakeld.
|
||||
b) het verbeteren van de GPS-ontvangst (bijv. naar buiten gaan),\n
|
||||
c) controleer of met <a href="https://f-droid.org/en/packages/com.android.gpstest.osmdroid/">GPSTest</a> app, en
|
||||
d) schakel stroombesparing uit.
|
||||
<string name="help_gps_troubleshooting_content"> Wat u kunt doen:
|
||||
\n a) Controleer of GPS is ingeschakeld,
|
||||
\n b) verbeter de GPS-ontvangst (ga bijvoorbeeld naar buiten),
|
||||
\n c) controleren met <a href="https://f-droid.org/en/packages/com.android.gpstest.osmdroid/">GPSTest</a> app, en
|
||||
\n d) schakel energiebesparing uit.
|
||||
\n
|
||||
Als alles niet helpt, meldt u dan alstublieft een bug.</string>
|
||||
|
||||
\n Als alles niet helpt, meld dan een bug. </string>
|
||||
<string name="help_support_title">Hoe kan ik OpenTracks ondersteunen?</string>
|
||||
<string name="help_support_content">
|
||||
Je kunt...
|
||||
a) het woord verspreiden over OpenTracks, en...
|
||||
b) <a href="https://github.com/OpenTracksApp/OpenTracks/issues">voorstellen van functies</a>,\n
|
||||
c) <a href="https://github.com/OpenTracksApp/OpenTracks/issues">melding van bugs</a>,\n
|
||||
d) hulp bij de ontwikkeling van software.
|
||||
e) koop in <a href="https://play.google.com/store/apps/details?id=de.dennisguse.opentracks.playstore">Google Play Store</a>,\n
|
||||
f) doneert u, en
|
||||
g) doe wat je denkt dat OpenTracks helpt.</string>
|
||||
|
||||
<string name="help_support_content"> U kunt:
|
||||
\n a) het woord verspreiden over OpenTracks,
|
||||
\n b) <a href="https://github.com/OpenTracksApp/OpenTracks/issues">functies voorstellen</a>,
|
||||
\n c) <a href="https://github.com/OpenTracksApp/OpenTracks/issues">bugs melden</a>,
|
||||
\n d) helpen bij de ontwikkeling van software,
|
||||
\n e) kopen in <a href="https://play.google.com/store/apps/details?id=de.dennisguse.opentracks.playstore">Google Play Store</a>,
|
||||
\n f) doneren, en
|
||||
\n g) doe wat u denkt dat OpenTracks helpt. </string>
|
||||
<!-- Activity Type -->
|
||||
<string name="activity_type_airplane">vliegtuig</string>
|
||||
<string name="activity_type_atv">quad</string>
|
||||
@@ -104,7 +78,7 @@ g) doe wat je denkt dat OpenTracks helpt.</string>
|
||||
<string name="activity_type_driving_bus">bus besturen</string>
|
||||
<string name="activity_type_driving_car">autorijden</string>
|
||||
<string name="activity_type_ferry">veerboot</string>
|
||||
<string name="activity_type_frisbee">frisbee</string>
|
||||
<string name="activity_type_frisbee">Frisbee</string>
|
||||
<string name="activity_type_gliding">zweefvliegen</string>
|
||||
<string name="activity_type_hang_gliding">deltavliegen</string>
|
||||
<string name="activity_type_helicopter">helikopter</string>
|
||||
@@ -149,7 +123,7 @@ g) doe wat je denkt dat OpenTracks helpt.</string>
|
||||
<string name="activity_type_trail_hiking">trektocht (pad)</string>
|
||||
<string name="activity_type_trail_running">trailrunning</string>
|
||||
<string name="activity_type_train">trein</string>
|
||||
<string name="activity_type_ultimate_frisbee">ultimate frisbee</string>
|
||||
<string name="activity_type_ultimate_frisbee">ultieme frisbee</string>
|
||||
<string name="activity_type_unknown">onbekend</string>
|
||||
<string name="activity_type_wakeboarding">wakeboarden</string>
|
||||
<string name="activity_type_walking">wandelen</string>
|
||||
@@ -185,14 +159,14 @@ g) doe wat je denkt dat OpenTracks helpt.</string>
|
||||
<string name="description_sensor_cadence">Cadans (tpm)</string>
|
||||
<string name="description_sensor_heart_rate">Hartslag (spm)</string>
|
||||
<string name="description_sensor_power">Vermogen (W)</string>
|
||||
<string name="description_default_speed_or_pace">By activity type</string>
|
||||
<string name="description_default_speed_or_pace">Op type activiteit</string>
|
||||
<string name="description_speed_imperial">Snelheid (mi/u)</string>
|
||||
<string name="description_speed_metric">Snelheid (km/u)</string>
|
||||
<string name="description_time">Tijd</string>
|
||||
<string name="description_total_distance">Totale afstand: %1$.2f km (%2$.1f mi)</string>
|
||||
<string name="description_total_time">Totale tijd: %1$s</string>
|
||||
<string name="description_track_point">Punt</string>
|
||||
<string name="description_track_segment">Segment</string>
|
||||
<string name="description_track_segment">segment</string>
|
||||
<!-- Export -->
|
||||
<string name="export_all_option">Alles als %1$s</string>
|
||||
<string name="export_all_title">Exporteren naar externe opslag</string>
|
||||
@@ -331,7 +305,7 @@ g) doe wat je denkt dat OpenTracks helpt.</string>
|
||||
<string name="settings_chart_by_time">Op tijd</string>
|
||||
<string name="settings_chart_x_axis">X-as</string>
|
||||
<!-- Settings Recording -->
|
||||
<string name="settings_recording_show_on_lockscreen_while_recording_title">Toon de statistieken op het slotscherm.</string>
|
||||
<string name="settings_recording_show_on_lockscreen_while_recording_title">Toon de statistieken op het slotscherm</string>
|
||||
<string name="settings_recording_show_on_lockscreen_while_recording_summary">Toon tijdens de opname de statistieken zonder het apparaat te ontgrendelen.</string>
|
||||
<string name="settings_recording_keep_screen_on_while_recording_title">Houd het scherm aan</string>
|
||||
<string name="settings_recording_keep_screen_on_while_recording_summary">Houd tijdens de opname het scherm aan.</string>
|
||||
@@ -359,22 +333,22 @@ g) doe wat je denkt dat OpenTracks helpt.</string>
|
||||
<string name="settings_reset_done">Voor alle instellingen zijn de standaardwaarden teruggezet</string>
|
||||
<string name="settings_error_initial_values_restored">Sorry, maar er is een fout gemaakt. Alle instellingen zijn teruggezet naar de standaardwaarden.</string>
|
||||
<!-- Settings Sensor -->
|
||||
<string name="settings_sensor">Sensor</string>
|
||||
<string name="settings_sensor">sensor</string>
|
||||
<string name="settings_sensor_bluetooth">Bluetooth</string>
|
||||
<string name="settings_sensor_bluetooth_sensor">Bluetooth-sensor</string>
|
||||
<string name="settings_sensor_connecting">Verbonden met %1$s</string>
|
||||
<string name="settings_sensor_disconnected">Ontkoppeld van %1$s</string>
|
||||
|
||||
<!-- Settings Stats -->
|
||||
<string name="settings_stats_coordinate">Breedte-/lengtegraad</string>
|
||||
<string name="settings_stats_rate_title">Voorkeursverhouding</string>
|
||||
<string name="settings_stats_units_imperial">Engels (mi, ft)</string>
|
||||
<string name="settings_stats_units_metric">Metrisch (km, m)</string>
|
||||
<string name="settings_stats_units_title">Voorkeurseenheden</string>
|
||||
|
||||
<!-- Share Track -->
|
||||
<string name="share_track_share_file">Routebestand delen</string>
|
||||
<string name="share_track_share_file_body">Ik denk dat u misschien wel geïnteresseerd bent in deze route.\n\nAls u deze route wilt weergeven, kunt u Google Earth of andere kaartsoftware gebruiken.\n\n%1$s</string>
|
||||
<string name="share_track_share_file_body">Ik denk dat u misschien wel geïnteresseerd bent in deze route.
|
||||
\n
|
||||
\n%1$s</string>
|
||||
<string name="share_track_subject">Ik wil graag een route met u delen</string>
|
||||
<!-- Stats -->
|
||||
<string name="stats_average_moving_pace">Gem. verplaatsingstempo</string>
|
||||
@@ -473,7 +447,7 @@ g) doe wat je denkt dat OpenTracks helpt.</string>
|
||||
<string name="voice_minutes_other">%1$d minuten</string>
|
||||
<string name="voice_minutes_two">2 minuten</string>
|
||||
<string name="voice_minutes_zero">0 minuten</string>
|
||||
<string name="voice_pace_per_kilometer">%1$s per kilometer</string>
|
||||
<string name="voice_pace_per_kilometer">%1$s te kilometer</string>
|
||||
<string name="voice_pace_per_mile">%1$s per mijl</string>
|
||||
<string name="voice_seconds_few">%1$d seconden</string>
|
||||
<string name="voice_seconds_many">%1$d seconden</string>
|
||||
@@ -511,7 +485,7 @@ g) doe wat je denkt dat OpenTracks helpt.</string>
|
||||
<string name="waypoint_type_atm">Pinautomaat</string>
|
||||
<string name="waypoint_type_bank">bank</string>
|
||||
<string name="waypoint_type_bar">bar</string>
|
||||
<string name="waypoint_type_barbecue">barbecue</string>
|
||||
<string name="waypoint_type_barbecue">barbecueën</string>
|
||||
<string name="waypoint_type_building">gebouw</string>
|
||||
<string name="waypoint_type_bus_stop">bushalte</string>
|
||||
<string name="waypoint_type_cafe">café</string>
|
||||
@@ -551,16 +525,86 @@ g) doe wat je denkt dat OpenTracks helpt.</string>
|
||||
<string name="waypoint_type_store">winkel</string>
|
||||
<string name="waypoint_type_swimming_pool">zwembad</string>
|
||||
<string name="waypoint_type_telephone">telefoon</string>
|
||||
<string name="waypoint_type_theater">theater</string>
|
||||
<string name="waypoint_type_toilet">toilet</string>
|
||||
<string name="waypoint_type_theater">schouwburg</string>
|
||||
<string name="waypoint_type_toilet">restroom</string>
|
||||
<string name="waypoint_type_tourist_information">toeristische informatie</string>
|
||||
<string name="waypoint_type_tree">boom</string>
|
||||
<string name="waypoint_type_tunnel">tunnel</string>
|
||||
<string name="waypoint_type_tunnel">schacht</string>
|
||||
<string name="waypoint_type_waterfall">waterval</string>
|
||||
<string name="waypoint_type_water_fountain">waterfontein</string>
|
||||
<string name="waypoint_type_waypoint">routepunt</string>
|
||||
<!-- Share image -->
|
||||
<string name="share_image_subject">Ik wil graag een beeld met u delen</string>
|
||||
<string name="share_image_body">Ik denk dat je misschien geïnteresseerd bent in dit beeld.</string>
|
||||
|
||||
</resources>
|
||||
<string name="export_track_already_exists_msg">De track %1$s bestaat al in de doelmap.</string>
|
||||
<string name="export_track_errors">Tracks niet geëxporteerd</string>
|
||||
<string name="description_elevation_loss">Hoogteverlies: %1$d m (%2$d ft)</string>
|
||||
<string name="export_error_post_workout">Post-workout export mislukt, controleer de export map.</string>
|
||||
<string name="open_track_using_dashboard">Openen met behulp van OpenTracks-Dashboard</string>
|
||||
<string name="open_track_as_KMZ">Openen als KMZ</string>
|
||||
<string name="open_track_as_GPX">Openen als GPX</string>
|
||||
<string name="open_track_as_trackfileformat">Openen als %1$s</string>
|
||||
<string name="activity_type_escooter">scooter</string>
|
||||
<string name="export_all_do_it_for_all">doe het voor alle conflicten</string>
|
||||
<string name="export_progress_summary_new_msg">Nieuwe sporen geëxporteerd</string>
|
||||
<string name="generic_click_twice_cancel">Klik nogmaals terug om te annuleren</string>
|
||||
<string name="settings_default_export_uri_title">Spoor export map</string>
|
||||
<string name="import_thread_interrupted">Draad onderbroken</string>
|
||||
<string name="import_no_kml_file_found">Geen doc.kml bestand gevonden in KMZ</string>
|
||||
<string name="settings_recording_fullscreen_on_while_recording_title">Volledig scherm</string>
|
||||
<string name="gps_fixed_and_ready">GPS vast en klaar</string>
|
||||
<string name="value_float_kilometer_hour">%1$.2f km/h</string>
|
||||
<string name="aggregated_stats_empty_message">Neem uw eerste track op om geaggregeerde statistieken te zien</string>
|
||||
<string name="track_detail_intervals_tab">Intervallen</string>
|
||||
<string name="settings_sensor_wheel_circumference">Wielomtrek (mm)</string>
|
||||
<string name="settings_prevent_reimport_tracks_title">Voorkom herinvoer van tracks</string>
|
||||
<plurals name="generic_completed_with_errors">
|
||||
<item quantity="one">Voltooid met één fout</item>
|
||||
<item quantity="other">Afgerond met %1$d fouten</item>
|
||||
</plurals>
|
||||
<string name="settings_recording_fullscreen_on_while_recording_summary">Tijdens opname, aanwezig in volledig scherm.</string>
|
||||
<string name="settings_default_trackfileformat">Bestandsformaat exporteren/delen</string>
|
||||
<string name="sensor_state_power_avg">Gem. Vermogen</string>
|
||||
<string name="sensor_state_heart_rate_max">Max. Hartslag</string>
|
||||
<string name="sensor_state_heart_rate_avg">Gem. Hartslag</string>
|
||||
<string name="sensor_state_cadence_avg">Avg cadans</string>
|
||||
<string name="sensor_state_cadence_max">Max. cadans</string>
|
||||
<string name="settings_night_mode_option_night">Nacht</string>
|
||||
<string name="settings_night_mode_option_day">Dag</string>
|
||||
<string name="settings_night_mode_option_system">Systeem</string>
|
||||
<string name="settings_night_mode_title">UI Thema</string>
|
||||
<string name="stats_loss">Verlies</string>
|
||||
<string name="value_float_mile_hour">%1$.2f mph</string>
|
||||
<string name="interval_list_empty_message">Er zijn nog geen intervallen</string>
|
||||
<string name="stats_split_interval">Gesplitst interval</string>
|
||||
<string name="stats_intervals">Intervallen</string>
|
||||
<string name="value_pace_kilometer">%1$d:%2$02d min/km</string>
|
||||
<string name="value_pace_mile">%1$d:%2$02d min/mi</string>
|
||||
<string name="voice_pace_lap">Rondetijd van %1$s</string>
|
||||
<string name="voice_speed_lap">Ronde snelheid van %1$s</string>
|
||||
<string name="instant_export_enabled_title">Directe export na de training</string>
|
||||
<string name="instant_export_enabled_summary">Exporteer track naar opslag nadat opname is voltooid</string>
|
||||
<string name="toast_message_instant_track_was_exported">Laatste geëxporteerde track</string>
|
||||
<string name="hold_to_pause">Vasthouden om te pauzeren</string>
|
||||
<string name="export_progress_summary_skip_msg">Tracks overgeslagen</string>
|
||||
<string name="export_progress_summary_overwrite_msg">Tracks overschreven</string>
|
||||
<string name="export_progress_summary_errors_msg">Fouten</string>
|
||||
<string name="generic_open_track">Open spoor</string>
|
||||
<string name="generic_overwrite">Overschrijven</string>
|
||||
<string name="generic_skip">Sla over.</string>
|
||||
<string name="generic_separator_done_total">/</string>
|
||||
<string name="generic_show_errors">Fouten tonen</string>
|
||||
<string name="generic_completed">Voltooid</string>
|
||||
<string name="import_unsupported_format">Niet-ondersteund bestandsformaat</string>
|
||||
<string name="import_error_list_dialog_title">Bestanden niet geïmporteerd</string>
|
||||
<string name="import_prevent_reimport">Jij hebt ingesteld voorkom her-import spoor</string>
|
||||
<string name="import_progress_summary_ok_msg">Ingevoerde bestanden</string>
|
||||
<string name="import_progress_summary_exists_msg">Bestaat reeds</string>
|
||||
<string name="import_progress_summary_errors_msg">Fouten</string>
|
||||
<string name="gps_disabled_msg">GPS uitgeschakeld</string>
|
||||
<string name="import_parser_error">Parser fout: %1$s</string>
|
||||
<string name="import_unable_to_import_file">Kan bestand niet importeren: %1$s</string>
|
||||
<string name="import_error_info">%1$s: %2$s</string>
|
||||
<string name="import_file_imported">Bestand %1$s geimporteerd</string>
|
||||
<string name="hold_to_stop">Hou vast om te stoppen</string>
|
||||
</resources>
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user