Compare commits

..

33 Commits

Author SHA1 Message Date
Dennis Guse 823dc1c12b Release: v3.18.5 2021-06-24 18:44:36 +02:00
Dennis Guse 26490f5a66 Launcher icon (API <26): does not have a background circle.
Rendering a multilayer image is not consistent (size).

Fixes #814.
2021-06-24 18:29:50 +02:00
Dennis Guse d299dbdf06 Also send speed to UI. 2021-06-24 12:02:22 +02:00
Dennis Guse b5555e302f Bugfix: TrackRecordingService now computes EGM2008 for UI (i.e., StatisticsRecordingFragment).
Fixes #828.
2021-06-24 12:02:22 +02:00
Dennis Guse 3030b4dfd9 Bugfix: TrackRecordingService needs to send data from lastTrackPoint in it's 1s updates.
This also removes UI updates if a GPS measurement is received as 1s update should be sufficient.

Fixes #828.
2021-06-24 12:02:22 +02:00
dependabot[bot] a5431b1cf5 Bump reactivecircus/android-emulator-runner from 2.17.0 to 2.18.0
Bumps [reactivecircus/android-emulator-runner](https://github.com/reactivecircus/android-emulator-runner) from 2.17.0 to 2.18.0.
- [Release notes](https://github.com/reactivecircus/android-emulator-runner/releases)
- [Changelog](https://github.com/ReactiveCircus/android-emulator-runner/blob/main/CHANGELOG.md)
- [Commits](https://github.com/reactivecircus/android-emulator-runner/compare/v2.17.0...v2.18.0)

---
updated-dependencies:
- dependency-name: reactivecircus/android-emulator-runner
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2021-06-23 07:35:59 +02:00
serpico 9314221647 Translated using Weblate (Indonesian)
Currently translated at 79.3% (415 of 523 strings)

Translation: OpenTracks/opentracks.src-main-res-values-strings-xml--main
Translate-URL: https://weblate.bubu1.eu/projects/OpenTracks/opentracks/id/
2021-06-22 17:19:35 +02:00
Dennis Guse 9bb9d5c283 Voice announcement: make rate configurable.
Fixes #777.
2021-06-22 17:19:31 +02:00
Patrick Georgi 1095a1442f Fix typo in German translation
It's Geschwindigkeit, not Geshwindigkeit.
2021-06-22 17:00:22 +02:00
Dennis Guse d373fdaaeb Downgrade espresso-coret to 3.3.0; no need to use an alpha. 2021-06-20 17:43:05 +02:00
serpico 5278ba4d35 Translated using Weblate (French)
Currently translated at 98.5% (67 of 68 strings)

Translation: OpenTracks/App store metadata files
Translate-URL: https://weblate.bubu1.eu/projects/OpenTracks/app-store-metadata-files/fr/
2021-06-17 23:36:18 +02:00
FTno 9b71b7f88c Translated using Weblate (Norwegian Bokmål)
Currently translated at 100.0% (523 of 523 strings)

Translation: OpenTracks/opentracks.src-main-res-values-strings-xml--main
Translate-URL: https://weblate.bubu1.eu/projects/OpenTracks/opentracks/nb_NO/
2021-06-17 23:36:18 +02:00
serpico bc96c2c05b Translated using Weblate (French)
Currently translated at 99.8% (522 of 523 strings)

Translation: OpenTracks/opentracks.src-main-res-values-strings-xml--main
Translate-URL: https://weblate.bubu1.eu/projects/OpenTracks/opentracks/fr/
2021-06-17 23:36:18 +02:00
Dennis Guse e655762c72 EspressoCategoryUnitTest needs to set metricUnits. 2021-06-17 23:26:05 +02:00
Dennis Guse 71c385a1a9 Android12: add new Bluetooth connect permission.
Fixes #821.
2021-06-17 23:18:04 +02:00
Dennis Guse b947a727e7 Bugfix: while recording UI values were doubled.
Issue: copy constructor of  TrackStatisticsUpdater was not copying completely and the same reference used.
2021-06-17 23:08:06 +02:00
Román Martínez 470f23afa8 Bugfix: pace or speed header text is not showing in intervals.
Bugfix: viewModel checking onPause.
2021-06-17 14:36:30 +02:00
Dennis Guse 0a1f9dfe41 Cleanup. 2021-06-15 22:55:42 +02:00
Weblate 6fcd19bf2e Merge branch 'origin/main' into Weblate. 2021-06-15 22:43:54 +02:00
serpico 32d9705a4c Translated using Weblate (French)
Currently translated at 99.6% (521 of 523 strings)

Translation: OpenTracks/opentracks.src-main-res-values-strings-xml--main
Translate-URL: https://weblate.bubu1.eu/projects/OpenTracks/opentracks/fr/
2021-06-15 18:08:46 +02:00
Xosé M 2ac66c08f2 Translated using Weblate (Galician)
Currently translated at 100.0% (523 of 523 strings)

Translation: OpenTracks/opentracks.src-main-res-values-strings-xml--main
Translate-URL: https://weblate.bubu1.eu/projects/OpenTracks/opentracks/gl/
2021-06-15 18:08:46 +02:00
Dennis Guse 9234da4957 Release: v3.18.4 2021-06-15 17:53:30 +02:00
Román Martínez 24e18347a6 IntervalsFragment doesn't use TrackDataHub anymore. This improve intervals performance. Fixes #808. 2021-06-15 17:09:24 +02:00
serpico 3b776407fa Translated using Weblate (French)
Currently translated at 99.6% (521 of 523 strings)

Translation: OpenTracks/opentracks.src-main-res-values-strings-xml--main
Translate-URL: https://weblate.bubu1.eu/projects/OpenTracks/opentracks/fr/
2021-06-15 17:08:24 +02:00
Xosé M 399f90a8f0 Translated using Weblate (Galician)
Currently translated at 100.0% (523 of 523 strings)

Translation: OpenTracks/opentracks.src-main-res-values-strings-xml--main
Translate-URL: https://weblate.bubu1.eu/projects/OpenTracks/opentracks/gl/
2021-06-15 17:08:24 +02:00
Román Martínez 4c68592a0c Intervals are computated through TrackStatisticsUpdater. Fixes #703. 2021-06-15 17:08:16 +02:00
Román Martínez b2c2047ca2 Dennis Review. 2021-06-15 17:08:16 +02:00
Román Martínez 838671e2ce IntervalsFragment doesn't use TrackDataHub anymore. This improve intervals performance. Fixes #808. 2021-06-15 17:08:16 +02:00
Dennis Guse 47a340578f Bugfix: prevent crash when creating TRACKPOINT without location.
Introduced in 2f0449c420
2021-06-14 12:11:23 +02:00
Dennis Guse e9e48e4a79 Bugfix: cycling sensors report wheel count as UINT32 - data type must be long, int is too small.
Also forgot to fix tests.

Fixes #810.
2021-06-13 19:17:05 +02:00
Dennis Guse 40188774b1 Bugfix: cycling sensors report wheel count as UINT32.
Garmin Speed Sensor 2 actually counts forward: it was an UINT16 overflow problem (removed workaround).

Fixes #810.
2021-06-11 19:25:40 +02:00
Dennis Guse 2f0449c420 TrackRecordingService: compute temporary TrackStatistics for UI.
Fixes #810.
2021-06-11 19:25:40 +02:00
Dennis Guse 21376cd3da Speed and distance each only use one formatting rule.
Fixes #648.
2021-06-08 17:10:23 +02:00
98 changed files with 794 additions and 779 deletions
+1 -1
View File
@@ -13,7 +13,7 @@ jobs:
uses: actions/checkout@v2.3.4
- name: run tests
uses: reactivecircus/android-emulator-runner@v2.17.0
uses: reactivecircus/android-emulator-runner@v2.18.0
with:
api-level: ${{ matrix.api-level }}
target: ${{ matrix.target }}
-2
View File
@@ -23,8 +23,6 @@ However, often only one value is provided.
* 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)
+3 -3
View File
@@ -55,8 +55,8 @@ android {
defaultConfig {
applicationId "de.dennisguse.opentracks"
versionCode 4143
versionName "v3.18.3"
versionCode 4176
versionName "v3.18.5"
buildConfigField "String", "VERSION_NAME_FULL", "\"${getVersionName()}\""
@@ -90,7 +90,6 @@ android {
}
dependencies {
androidTestImplementation 'androidx.test.espresso:espresso-core:3.4.0-alpha06'
coreLibraryDesugaring 'com.android.tools:desugar_jdk_libs:1.1.5'
implementation 'androidx.appcompat:appcompat:1.3.0'
@@ -103,5 +102,6 @@ dependencies {
androidTestImplementation 'androidx.test.ext:junit:1.1.2'
androidTestImplementation 'androidx.test:rules:1.3.0'
androidTestImplementation 'androidx.test:runner:1.3.0'
androidTestImplementation 'androidx.test.espresso:espresso-core:3.3.0'
androidTestImplementation 'org.mockito:mockito-android:3.10.0'
}
@@ -0,0 +1,10 @@
v3.18.4: OpenTracks
Changes:
- Speed/distance are consistently formatted
- Intervals tab is now faster
Bugfix:
- Cycling speed/distance sensor:
- fixed parsing (UINT32)
- transmit data to UI properly
@@ -0,0 +1,12 @@
v3.18.5: OpenTracks
Changes:
- Voice announcement speed configurable
- API <26: Launcher icon omits background
Bugfixes:
- Recording UI: show correct values
- Recording UI: show (again) altitude in EGM2008
Developer:
- Android12 permissions (BLUETOOTH_CONNECT)
@@ -4,6 +4,8 @@ Fonctionnalités :
* Suivi d'activité : enregistre vos activités sportives et de plein air
* Photos et repères : ajout de photos et de repères pendant l'enregistrement de l'activité
* Rythme cardiaque : support des capteurs Bluetooth LE
* Export : export des traces au format KML ou GPX
* Dénivelé positif et négatif: via capteur de pression barométrique
* Synthèse vocale
* Prise en charge des capteurs Bluetooth LE : rythme cardiaque, vitesse et distance (vélo), cadence de pédalage (vélo), et capteur de puissance (vélo)
* Export : export des traces au format KMZ (photos incluses), KML ou GPX
* Pas d'accès internet requis : Internet n'est pas nécessaire pour le fonctionnement de l'application
@@ -1 +1 @@
OpenTracks - Suivi d'activités sportives respectueux de votre vie privée.
OpenTracks - Suivi d'activités sportives respectueux de votre vie privée.
@@ -1,6 +1,7 @@
package de.dennisguse.opentracks;
import android.content.Context;
import android.view.View;
import android.view.ViewGroup;
import android.view.ViewParent;
@@ -9,16 +10,18 @@ import androidx.test.espresso.DataInteraction;
import androidx.test.espresso.UiController;
import androidx.test.espresso.ViewAction;
import androidx.test.espresso.ViewInteraction;
import androidx.test.filters.LargeTest;
import androidx.test.rule.ActivityTestRule;
import androidx.test.rule.GrantPermissionRule;
import androidx.test.ext.junit.rules.ActivityScenarioRule;
import androidx.test.ext.junit.runners.AndroidJUnit4;
import androidx.test.filters.LargeTest;
import androidx.test.platform.app.InstrumentationRegistry;
import androidx.test.rule.GrantPermissionRule;
import org.hamcrest.Description;
import org.hamcrest.Matcher;
import org.hamcrest.TypeSafeMatcher;
import org.junit.Rule;
import org.junit.Test;
import org.junit.rules.TestRule;
import org.junit.runner.RunWith;
import static androidx.test.espresso.Espresso.onData;
@@ -44,12 +47,15 @@ import static org.hamcrest.Matchers.is;
public class EspressoCategoryUnitTest {
@Rule
public ActivityTestRule<TrackListActivity> mActivityTestRule = new ActivityTestRule<>(TrackListActivity.class);
public ActivityScenarioRule<TrackListActivity> mActivityTestRule = new ActivityScenarioRule<>(TrackListActivity.class);
@Rule
public GrantPermissionRule mGrantPermissionRule =
GrantPermissionRule.grant(
"android.permission.ACCESS_FINE_LOCATION");
public TestRule mRuntimePermissionRule = GrantPermissionRule.grant(android.Manifest.permission.ACCESS_FINE_LOCATION);
private final Context context = InstrumentationRegistry.getInstrumentation().getTargetContext();
@Rule
public TestRule mLocaleRule = new PreferenceMetricUnitRule(context, true);
@Test
public void changeBetweenCategoryUpdateProperlyUnitTest() {
@@ -4,7 +4,6 @@ package de.dennisguse.opentracks;
import android.app.ActivityManager;
import android.app.Instrumentation;
import android.content.Context;
import android.util.Log;
import android.view.MotionEvent;
import android.view.View;
import android.view.ViewGroup;
@@ -19,10 +18,10 @@ import androidx.test.espresso.ViewInteraction;
import androidx.test.espresso.action.MotionEvents;
import androidx.test.espresso.assertion.ViewAssertions;
import androidx.test.ext.junit.rules.ActivityScenarioRule;
import androidx.test.ext.junit.runners.AndroidJUnit4;
import androidx.test.filters.LargeTest;
import androidx.test.platform.app.InstrumentationRegistry;
import androidx.test.rule.GrantPermissionRule;
import androidx.test.ext.junit.runners.AndroidJUnit4;
import org.hamcrest.Description;
import org.hamcrest.Matcher;
@@ -56,7 +55,7 @@ public class EspressoDeleteTrackTest {
public ActivityScenarioRule<TrackListActivity> mActivityTestRule = new ActivityScenarioRule<>(TrackListActivity.class);
@Rule
public GrantPermissionRule mGrantPermissionRule = GrantPermissionRule.grant("android.permission.ACCESS_FINE_LOCATION");
public GrantPermissionRule mGrantPermissionRule = GrantPermissionRule.grant(android.Manifest.permission.ACCESS_FINE_LOCATION);
private MyIdlingResource idlingResource;
@@ -242,7 +241,7 @@ public class EspressoDeleteTrackTest {
private class MyIdlingResource implements IdlingResource {
private Context context;
private final Context context;
private ResourceCallback resourceCallback;
public MyIdlingResource(Context context) {
@@ -35,7 +35,7 @@ public class EspressoUITest {
public ActivityScenarioRule<TrackListActivity> mActivityTestRule = new ActivityScenarioRule<>(TrackListActivity.class);
@Rule
public GrantPermissionRule mGrantPermissionRule = GrantPermissionRule.grant("android.permission.ACCESS_FINE_LOCATION");
public GrantPermissionRule mGrantPermissionRule = GrantPermissionRule.grant(android.Manifest.permission.ACCESS_FINE_LOCATION);
@LargeTest
@Test
@@ -0,0 +1,40 @@
package de.dennisguse.opentracks;
import android.content.Context;
import android.content.SharedPreferences;
import androidx.annotation.NonNull;
import org.junit.rules.TestRule;
import org.junit.runner.Description;
import org.junit.runners.model.Statement;
import de.dennisguse.opentracks.util.PreferencesUtils;
public class PreferenceMetricUnitRule implements TestRule {
private final Context context;
private final boolean metricUnits;
public PreferenceMetricUnitRule(@NonNull Context context, boolean metricUnits) {
this.context = context;
this.metricUnits = metricUnits;
}
@Override
public Statement apply(final Statement base, final Description description) {
return new Statement() {
@Override
public void evaluate() throws Throwable {
SharedPreferences sharedPreferences = PreferencesUtils.getSharedPreferences(context);
final boolean previousMetricUnits = PreferencesUtils.isMetricUnits(sharedPreferences, context);
try {
PreferencesUtils.setMetricUnits(sharedPreferences, context, metricUnits);
base.evaluate();
} finally {
PreferencesUtils.setMetricUnits(sharedPreferences, context, previousMetricUnits);
}
}
};
}
}
@@ -14,6 +14,7 @@ import java.util.List;
import de.dennisguse.opentracks.content.provider.ContentProviderUtils;
import de.dennisguse.opentracks.content.provider.TrackPointIterator;
import de.dennisguse.opentracks.stats.TrackStatistics;
import de.dennisguse.opentracks.stats.TrackStatisticsUpdater;
import de.dennisguse.opentracks.util.FileUtils;
public class TestDataUtil {
@@ -166,4 +167,20 @@ public class TestDataUtil {
}
return trackPoints;
}
public static Pair<Track.Id, TrackStatistics> buildTrackWithTrackPoints(ContentProviderUtils contentProviderUtils, int numberOfPoints) {
Track dummyTrack = new Track();
dummyTrack.setId(new Track.Id(System.currentTimeMillis()));
dummyTrack.setName("Dummy Track");
contentProviderUtils.insertTrack(dummyTrack);
TrackStatisticsUpdater trackStatisticsUpdater = new TrackStatisticsUpdater();
for (int i = 0; i < numberOfPoints; i++) {
TrackPoint tp = TestDataUtil.createTrackPoint(i);
contentProviderUtils.insertTrackPoint(tp, dummyTrack.getId());
trackStatisticsUpdater.addTrackPoint(tp, Distance.of(0));
}
dummyTrack.setTrackStatistics(trackStatisticsUpdater.getTrackStatistics());
contentProviderUtils.updateTrack(dummyTrack);
return new Pair<>(dummyTrack.getId(), trackStatisticsUpdater.getTrackStatistics());
}
}
@@ -111,7 +111,7 @@ public class SensorDataCyclingTest {
@Test
public void compute_speed_rollOverCount() {
// given
SensorDataCycling.DistanceSpeed previous = new SensorDataCycling.DistanceSpeed("sensorAddress", "sensorName", UintUtils.UINT16_MAX - 1, 1024);
SensorDataCycling.DistanceSpeed previous = new SensorDataCycling.DistanceSpeed("sensorAddress", "sensorName", UintUtils.UINT32_MAX - 1, 1024);
SensorDataCycling.DistanceSpeed current = new SensorDataCycling.DistanceSpeed("sensorAddress", "sensorName", 0, 2048);
// when
@@ -193,4 +193,31 @@ public class TrackStatisticsUpdaterTest {
@Test
public void addTrackPoint_speed_from_GPS_moving_and_sensor_speed() {
}
@Test
public void copy_constructor() {
// given
TrackStatisticsUpdater subject = new TrackStatisticsUpdater();
TrackPoint tp1 = new TrackPoint(TrackPoint.Type.SEGMENT_START_MANUAL, Instant.ofEpochMilli(1000));
TrackPoint tp2 = new TrackPoint(0, 0, Altitude.WGS84.of(5.0), Instant.ofEpochMilli(2000));
TrackPoint tp3 = new TrackPoint(0.00001, 0, Altitude.WGS84.of(5.0), Instant.ofEpochMilli(3000));
TrackPoint tp4 = new TrackPoint(0.0005, 0, Altitude.WGS84.of(5.0), Instant.ofEpochMilli(4000));
TrackPoint tp5 = new TrackPoint(TrackPoint.Type.SEGMENT_END_MANUAL, Instant.ofEpochMilli(5000));
subject.addTrackPoint(tp1, GPS_DISTANCE);
subject.addTrackPoint(tp2, GPS_DISTANCE);
subject.addTrackPoint(tp3, GPS_DISTANCE);
subject.addTrackPoint(tp4, GPS_DISTANCE);
// when
TrackStatisticsUpdater copy = new TrackStatisticsUpdater(subject);
subject.addTrackPoint(tp5, GPS_DISTANCE);
copy.addTrackPoint(tp5, GPS_DISTANCE);
// then
assertEquals(55.287, subject.getTrackStatistics().getTotalDistance().toM(), 0.01);
assertEquals(55.287, copy.getTrackStatistics().getTotalDistance().toM(), 0.01);
}
}
@@ -1,21 +1,23 @@
package de.dennisguse.opentracks.util;
import android.content.Context;
import android.util.Pair;
import androidx.test.core.app.ApplicationProvider;
import androidx.test.ext.junit.runners.AndroidJUnit4;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import java.time.Duration;
import java.util.List;
import de.dennisguse.opentracks.content.data.Distance;
import de.dennisguse.opentracks.content.data.Speed;
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;
import de.dennisguse.opentracks.content.provider.TrackPointIterator;
import de.dennisguse.opentracks.stats.TrackStatistics;
import de.dennisguse.opentracks.viewmodels.IntervalStatistics;
@@ -25,6 +27,12 @@ import static org.junit.Assert.assertEquals;
public class AnnouncementUtilsTest {
private final Context context = ApplicationProvider.getApplicationContext();
private ContentProviderUtils contentProviderUtils;
@Before
public void setUp() {
contentProviderUtils = new ContentProviderUtils(context);
}
@Test
public void getAnnouncement_metric_speed() {
@@ -44,22 +52,31 @@ public class AnnouncementUtilsTest {
@Test
public void getAnnouncement_withInterval_metric_speed() {
TrackStatistics stats = new TrackStatistics();
stats.setTotalDistance(Distance.of(20000));
stats.setTotalTime(Duration.ofHours(2).plusMinutes(5).plusSeconds(10));
stats.setMovingTime(Duration.ofHours(1).plusMinutes(5).plusSeconds(10));
stats.setMaxSpeed(Speed.of(100));
stats.setTotalAltitudeGain(6000f);
List<TrackPoint> trackPoints = TestDataUtil.createTrack(new Track.Id(System.currentTimeMillis()), 10).second;
IntervalStatistics intervalStatistics = new IntervalStatistics(trackPoints, Distance.of(1000));
IntervalStatistics.Interval lastInterval = intervalStatistics.getIntervalList().get(intervalStatistics.getIntervalList().size() - 1);
// given
int numberOfPoints = 1000;
Pair<Track.Id, TrackStatistics> trackWithStats = TestDataUtil.buildTrackWithTrackPoints(contentProviderUtils, numberOfPoints);
Track.Id trackId = trackWithStats.first;
TrackStatistics stats = trackWithStats.second;
IntervalStatistics.Interval lastInterval;
try (TrackPointIterator trackPointIterator = contentProviderUtils.getTrackPointLocationIterator(trackId, null)) {
assertEquals(trackPointIterator.getCount(), numberOfPoints);
IntervalStatistics intervalStatistics = new IntervalStatistics(Distance.of(1000), Distance.of(0));
lastInterval = intervalStatistics.getIntervalList().get(intervalStatistics.getIntervalList().size() - 1);
}
// when
String announcement = AnnouncementUtils.getAnnouncement(context, stats, true, true, lastInterval);
// then
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);
assertEquals(
"OpenTracks total distance " +
StringUtils.getDistanceParts(context, stats.getTotalDistance(), true).first +
" kilometers in " + buildAndGetTimeText(stats.getTotalTime(), false) + " at " +
StringUtils.getSpeedParts(context, stats.getAverageMovingSpeed(), true, true).first +
" kilometers per hour Lap speed of " +
StringUtils.getSpeedParts(context, lastInterval.getSpeed(), true, true).first +
" kilometers per hour",
announcement);
}
@Test
@@ -80,22 +97,31 @@ public class AnnouncementUtilsTest {
@Test
public void getAnnouncement_withInterval_metric_pace() {
TrackStatistics stats = new TrackStatistics();
stats.setTotalDistance(Distance.of(20000));
stats.setTotalTime(Duration.ofHours(2).plusMinutes(5).plusSeconds(10));
stats.setMovingTime(Duration.ofHours(1).plusMinutes(5).plusSeconds(10));
stats.setMaxSpeed(Speed.of(100));
stats.setTotalAltitudeGain(6000f);
List<TrackPoint> trackPoints = TestDataUtil.createTrack(new Track.Id(System.currentTimeMillis()), 10).second;
IntervalStatistics intervalStatistics = new IntervalStatistics(trackPoints, Distance.of(1000));
IntervalStatistics.Interval lastInterval = intervalStatistics.getIntervalList().get(intervalStatistics.getIntervalList().size() - 1);
// given
int numberOfPoints = 1000;
Pair<Track.Id, TrackStatistics> trackWithStats = TestDataUtil.buildTrackWithTrackPoints(contentProviderUtils, numberOfPoints);
Track.Id trackId = trackWithStats.first;
TrackStatistics stats = trackWithStats.second;
IntervalStatistics.Interval lastInterval;
try (TrackPointIterator trackPointIterator = contentProviderUtils.getTrackPointLocationIterator(trackId, null)) {
assertEquals(trackPointIterator.getCount(), numberOfPoints);
IntervalStatistics intervalStatistics = new IntervalStatistics(Distance.of(1000), Distance.of(0));
lastInterval = intervalStatistics.getIntervalList().get(intervalStatistics.getIntervalList().size() - 1);
}
// when
String announcement = AnnouncementUtils.getAnnouncement(context, stats, true, false, lastInterval);
// then
assertEquals("OpenTracks total distance 20.00 kilometers in 1 hour 5 minutes 10 seconds at 3 minutes 15 seconds per kilometer Lap time of 1 minute 10 seconds per kilometer", announcement);
assertEquals(
"OpenTracks total distance " +
StringUtils.getDistanceParts(context, stats.getTotalDistance(), true).first +
" kilometers in " + buildAndGetTimeText(stats.getTotalTime(), false) + " at " +
buildAndGetTimeText(stats.getAverageMovingSpeed().toPace(true), true) +
" per kilometer Lap time of " +
buildAndGetTimeText(lastInterval.getSpeed().toPace(true), true) +
" per kilometer",
announcement);
}
@Test
@@ -116,22 +142,31 @@ public class AnnouncementUtilsTest {
@Test
public void getAnnouncement_withInterval_imperial_speed() {
TrackStatistics stats = new TrackStatistics();
stats.setTotalDistance(Distance.of(20000));
stats.setTotalTime(Duration.ofHours(2).plusMinutes(5).plusSeconds(10));
stats.setMovingTime(Duration.ofHours(1).plusMinutes(5).plusSeconds(10));
stats.setMaxSpeed(Speed.of(100));
stats.setTotalAltitudeGain(6000f);
List<TrackPoint> trackPoints = TestDataUtil.createTrack(new Track.Id(System.currentTimeMillis()), 10).second;
IntervalStatistics intervalStatistics = new IntervalStatistics(trackPoints, Distance.of(1000));
IntervalStatistics.Interval lastInterval = intervalStatistics.getIntervalList().get(intervalStatistics.getIntervalList().size() - 1);
// given
int numberOfPoints = 1000;
Pair<Track.Id, TrackStatistics> trackWithStats = TestDataUtil.buildTrackWithTrackPoints(contentProviderUtils, numberOfPoints);
Track.Id trackId = trackWithStats.first;
TrackStatistics stats = trackWithStats.second;
IntervalStatistics.Interval lastInterval;
try (TrackPointIterator trackPointIterator = contentProviderUtils.getTrackPointLocationIterator(trackId, null)) {
assertEquals(trackPointIterator.getCount(), numberOfPoints);
IntervalStatistics intervalStatistics = new IntervalStatistics(Distance.of(1000), Distance.of(0));
lastInterval = intervalStatistics.getIntervalList().get(intervalStatistics.getIntervalList().size() - 1);
}
// when
String announcement = AnnouncementUtils.getAnnouncement(context, stats, false, true, lastInterval);
// then
assertEquals("OpenTracks total distance 12.43 miles in 1 hour 5 minutes 10 seconds at 11.4 miles per hour Lap speed of 31.8 miles per hour", announcement);
assertEquals(
"OpenTracks total distance " +
StringUtils.getDistanceParts(context, stats.getTotalDistance(), false).first +
" miles in " + buildAndGetTimeText(stats.getTotalTime(), false) + " at " +
StringUtils.getSpeedParts(context, stats.getAverageMovingSpeed(), false, true).first +
" miles per hour Lap speed of " +
StringUtils.getSpeedParts(context, lastInterval.getSpeed(), false, true).first +
" miles per hour",
announcement);
}
@Test
@@ -152,21 +187,60 @@ public class AnnouncementUtilsTest {
@Test
public void getAnnouncement_withInterval_imperial_pace() {
TrackStatistics stats = new TrackStatistics();
stats.setTotalDistance(Distance.of(20000));
stats.setTotalTime(Duration.ofHours(2).plusMinutes(5).plusSeconds(10));
stats.setMovingTime(Duration.ofHours(1).plusMinutes(5).plusSeconds(10));
stats.setMaxSpeed(Speed.of(100));
stats.setTotalAltitudeGain(6000f);
List<TrackPoint> trackPoints = TestDataUtil.createTrack(new Track.Id(System.currentTimeMillis()), 10).second;
IntervalStatistics intervalStatistics = new IntervalStatistics(trackPoints, Distance.of(1000));
IntervalStatistics.Interval lastInterval = intervalStatistics.getIntervalList().get(intervalStatistics.getIntervalList().size() - 1);
// given
int numberOfPoints = 1000;
Pair<Track.Id, TrackStatistics> trackWithStats = TestDataUtil.buildTrackWithTrackPoints(contentProviderUtils, numberOfPoints);
Track.Id trackId = trackWithStats.first;
TrackStatistics stats = trackWithStats.second;
IntervalStatistics.Interval lastInterval;
try (TrackPointIterator trackPointIterator = contentProviderUtils.getTrackPointLocationIterator(trackId, null)) {
assertEquals(trackPointIterator.getCount(), numberOfPoints);
IntervalStatistics intervalStatistics = new IntervalStatistics(Distance.of(1000), Distance.of(0));
lastInterval = intervalStatistics.getIntervalList().get(intervalStatistics.getIntervalList().size() - 1);
}
// when
String announcement = AnnouncementUtils.getAnnouncement(context, stats, false, false, lastInterval);
// then
assertEquals("OpenTracks total distance 12.43 miles in 1 hour 5 minutes 10 seconds at 5 minutes 15 seconds per mile Lap time of 1 minute 53 seconds per mile", announcement);
//assertEquals("OpenTracks total distance 12.43 miles in 1 hour 5 minutes 10 seconds at 5 minutes 15 seconds per mile Lap time of 1 minute 53 seconds per mile", announcement);
assertEquals(
"OpenTracks total distance " +
StringUtils.getDistanceParts(context, stats.getTotalDistance(), false).first +
" miles in " + buildAndGetTimeText(stats.getTotalTime(), false) + " at " +
buildAndGetTimeText(stats.getAverageMovingSpeed().toPace(false), true) +
" per mile Lap time of " +
buildAndGetTimeText(lastInterval.getSpeed().toPace(false), true) +
" per mile",
announcement);
}
/**
* Builds an returns the text representing the duration's time.
*
* @param duration Duration object.
* @param showFromMinutes show minutes tough it's 0.
* @return text representing the duratin's time.
*/
private String buildAndGetTimeText(Duration duration, boolean showFromMinutes) {
long hours = Math.abs(duration.getSeconds()) / 3600;
long minutes = (Math.abs(duration.getSeconds()) % 3600) / 60;
long seconds = Math.abs(duration.getSeconds()) % 60;
String hUnit = hours > 1 || hours == 0 ? "hours" : "hour";
String mUnit = minutes > 1 || minutes == 0 ? "minutes" : "minute";
String sUnit = seconds > 1 || seconds == 0 ? "seconds" : "second";
String res = hours > 0 ? hours + " " + hUnit : "";
if (hours > 0) {
res += minutes > 0 || showFromMinutes ? " " + minutes + " " + mUnit : "";
} else {
res += minutes > 0 || showFromMinutes ? minutes + " " + mUnit : "";
}
if (hours > 0 || minutes > 0 || showFromMinutes) {
res += " " + seconds + " " + sUnit;
} else {
res += seconds + " " + sUnit;
}
return res;
}
}
@@ -153,15 +153,15 @@ public class StringUtilsTest {
assertEquals("10", StringUtils.formatDecimal(10.1, 0));
assertEquals("-0", StringUtils.formatDecimal(-0.1, 0));
assertEquals("0", StringUtils.formatDecimal(0.0, 2));
assertEquals("0.1", StringUtils.formatDecimal(0.1, 2));
assertEquals("1.1", StringUtils.formatDecimal(1.1, 2));
assertEquals("10", StringUtils.formatDecimal(10, 2));
assertEquals("10.1", StringUtils.formatDecimal(10.1, 2));
assertEquals("0.00", StringUtils.formatDecimal(0.0, 2));
assertEquals("0.10", StringUtils.formatDecimal(0.1, 2));
assertEquals("1.10", StringUtils.formatDecimal(1.1, 2));
assertEquals("10.00", StringUtils.formatDecimal(10, 2));
assertEquals("10.10", StringUtils.formatDecimal(10.1, 2));
assertEquals("10.11", StringUtils.formatDecimal(10.111, 2));
assertEquals("-0.1", StringUtils.formatDecimal(-0.1, 2));
assertEquals("-0.10", StringUtils.formatDecimal(-0.1, 2));
assertEquals("1", StringUtils.formatDecimal(0.99, 1));
assertEquals("1.0", StringUtils.formatDecimal(0.99, 1));
}
@Test
@@ -181,7 +181,7 @@ public class StringUtilsTest {
assertEquals("4:59 min/km", StringUtils.formatSpeed(context, Speed.of(3.34), true, false));
assertEquals("8:02 min/mi", StringUtils.formatSpeed(context, Speed.of(3.34), false, false));
assertEquals("12.02 km/h", StringUtils.formatSpeed(context, Speed.of(3.34), true, true));
assertEquals("7.47 mph", StringUtils.formatSpeed(context, Speed.of(3.34), false, true));
assertEquals("12.0 km/h", StringUtils.formatSpeed(context, Speed.of(3.34), true, true));
assertEquals("7.5 mph", StringUtils.formatSpeed(context, Speed.of(3.34), false, true));
}
}
@@ -0,0 +1,23 @@
package de.dennisguse.opentracks.util;
import org.junit.Test;
import static org.junit.Assert.assertEquals;
public class UintUtilsTest {
@Test
public void diff() {
assertEquals(0, UintUtils.diff(1, 1, UintUtils.UINT16_MAX));
assertEquals(1, UintUtils.diff(2, 1, UintUtils.UINT16_MAX));
assertEquals(3, UintUtils.diff(5, 2, UintUtils.UINT16_MAX));
assertEquals(65534, UintUtils.diff(1, 2, UintUtils.UINT16_MAX));
assertEquals(65530, UintUtils.diff(UintUtils.UINT16_MAX, 5, UintUtils.UINT16_MAX));
}
@Test
public void realData() {
assertEquals(1, UintUtils.diff(381616, 381615, UintUtils.UINT32_MAX));
}
}
@@ -1,5 +1,11 @@
package de.dennisguse.opentracks.viewmodels;
import android.content.Context;
import android.util.Pair;
import androidx.test.core.app.ApplicationProvider;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.runners.JUnit4;
@@ -10,6 +16,8 @@ import de.dennisguse.opentracks.content.data.Distance;
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;
import de.dennisguse.opentracks.content.provider.TrackPointIterator;
import de.dennisguse.opentracks.stats.TrackStatistics;
import de.dennisguse.opentracks.stats.TrackStatisticsUpdater;
import de.dennisguse.opentracks.util.UnitConversions;
@@ -21,8 +29,12 @@ public class IntervalStatisticsTest {
private static final String TAG = IntervalStatisticsTest.class.getSimpleName();
private List<TrackPoint> buildTrackPoints(int numberOfTrackPoints) {
return TestDataUtil.createTrack(new Track.Id(System.currentTimeMillis()), numberOfTrackPoints).second;
private final Context context = ApplicationProvider.getApplicationContext();
private ContentProviderUtils contentProviderUtils;
@Before
public void setUp() {
contentProviderUtils = new ContentProviderUtils(context);
}
private TrackStatistics buildTrackStatistics(List<TrackPoint> trackPoints) {
@@ -41,12 +53,10 @@ public class IntervalStatisticsTest {
// With 50 points and interval distance of 1000m.
// given
List<TrackPoint> trackPoints = buildTrackPoints(50);
TrackStatistics trackStatistics = buildTrackStatistics(trackPoints);
float distanceInterval = 1000f;
// when and then
whenAndThen(trackPoints, trackStatistics, distanceInterval);
whenAndThen(50, distanceInterval);
}
/**
@@ -57,12 +67,10 @@ public class IntervalStatisticsTest {
// With 200 points and interval distance of 1000m.
// given
List<TrackPoint> trackPoints = buildTrackPoints(200);
TrackStatistics trackStatistics = buildTrackStatistics(trackPoints);
float distanceInterval = 1000f;
// when and then
whenAndThen(trackPoints, trackStatistics, distanceInterval);
whenAndThen(200, distanceInterval);
}
/**
@@ -73,12 +81,10 @@ public class IntervalStatisticsTest {
// With 200 points and interval distance of 3000m.
// given
List<TrackPoint> trackPoints = buildTrackPoints(200);
TrackStatistics trackStatistics = buildTrackStatistics(trackPoints);
float distanceInterval = 3000f;
// when and then
whenAndThen(trackPoints, trackStatistics, distanceInterval);
whenAndThen(3000, distanceInterval);
}
/**
@@ -89,12 +95,10 @@ public class IntervalStatisticsTest {
// With 1000 points and interval distance of 3000m.
// given
List<TrackPoint> trackPoints = buildTrackPoints(1000);
TrackStatistics trackStatistics = buildTrackStatistics(trackPoints);
float distanceInterval = 3000f;
// when and then
whenAndThen(trackPoints, trackStatistics, distanceInterval);
whenAndThen(1000, distanceInterval);
}
/**
@@ -105,16 +109,21 @@ public class IntervalStatisticsTest {
// With 10000 points and interval distance of 1000m.
// given
List<TrackPoint> trackPoints = buildTrackPoints(10000);
TrackStatistics trackStatistics = buildTrackStatistics(trackPoints);
float distanceInterval = 1000f;
// when and then
whenAndThen(trackPoints, trackStatistics, distanceInterval);
whenAndThen(10000, distanceInterval);
}
private void whenAndThen(List<TrackPoint> trackPoints, TrackStatistics trackStatistics, float distanceInterval) {
IntervalStatistics intervalStatistics = new IntervalStatistics(trackPoints, Distance.of(distanceInterval));
private void whenAndThen(int numberOfPoints, float distanceInterval) {
IntervalStatistics intervalStatistics = new IntervalStatistics(Distance.of(distanceInterval), Distance.of(0));
Pair<Track.Id, TrackStatistics> trackWithStats = TestDataUtil.buildTrackWithTrackPoints(contentProviderUtils, numberOfPoints);
Track.Id trackId = trackWithStats.first;
TrackStatistics trackStatistics = trackWithStats.second;
try (TrackPointIterator trackPointIterator = contentProviderUtils.getTrackPointLocationIterator(trackId, null)) {
assertEquals(trackPointIterator.getCount(), numberOfPoints);
intervalStatistics.addTrackPoints(trackPointIterator);
}
List<IntervalStatistics.Interval> intervalList = intervalStatistics.getIntervalList();
Distance totalDistance = Distance.of(0);
float totalTime = 0L;
@@ -129,7 +138,7 @@ public class IntervalStatisticsTest {
assertEquals(trackStatistics.getTotalDistance().toM(), totalDistance.toM(), 0.01);
assertEquals(trackStatistics.getTotalTime().toMillis(), totalTime * UnitConversions.S_TO_MS, 1);
assertEquals(intervalList.size(), (int) Math.ceil(trackStatistics.getTotalDistance().toM() / distanceInterval));
assertEquals(totalGain, trackPoints.size() * TestDataUtil.ALTITUDE_GAIN, 0.1);
assertEquals(totalGain, numberOfPoints * TestDataUtil.ALTITUDE_GAIN, 0.1);
for (int i = 0; i < intervalList.size() - 1; i++) {
assertEquals(intervalList.get(i).getDistance().toM(), distanceInterval, 0.001);
+1
View File
@@ -46,6 +46,7 @@ limitations under the License.
<uses-permission android:name="android.permission.WAKE_LOCK" />
<uses-permission android:name="android.permission.BLUETOOTH" />
<uses-permission android:name="android.permission.BLUETOOTH_ADMIN" />
<uses-permission android:name="android.permission.BLUETOOTH_CONNECT" />
<application
android:name=".Startup"
@@ -30,8 +30,7 @@ public class Startup extends Application {
// Set default values of preferences on first start.
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(sharedPreferences, this, R.string.stats_units_key, statsUnits);
PreferencesUtils.setMetricUnits(sharedPreferences, this, !Locale.US.equals(Locale.getDefault()));
}
ActivityUtils.applyNightMode(sharedPreferences, this);
@@ -289,7 +289,7 @@ public class TrackRecordedActivity extends AbstractTrackDeleteActivity implement
case 0:
return StatisticsRecordedFragment.newInstance(trackId);
case 1:
return IntervalsFragment.newInstance(true);
return IntervalsFragment.newInstance(trackId, true);
case 2:
return ChartFragment.newInstance(false);
case 3:
@@ -360,7 +360,7 @@ public class TrackRecordingActivity extends AbstractActivity implements ChooseAc
case 0:
return StatisticsRecordingFragment.newInstance();
case 1:
return IntervalsFragment.newInstance(false);
return IntervalsFragment.newInstance(trackId, false);
case 2:
return ChartFragment.newInstance(false);
case 3:
@@ -72,10 +72,6 @@ public class IntervalStatisticsAdapter extends RecyclerView.Adapter<RecyclerView
}
public List<IntervalStatistics.Interval> swapData(List<IntervalStatistics.Interval> data, boolean metricUnits, boolean isReportSpeed) {
if (intervalList == data && this.metricUnits == metricUnits && this.isReportSpeed == isReportSpeed) {
return null;
}
this.metricUnits = metricUnits;
this.isReportSpeed = isReportSpeed;
intervalList = data;
@@ -1,5 +1,7 @@
package de.dennisguse.opentracks.content.data;
import de.dennisguse.opentracks.R;
public abstract class Altitude {
private final double altitude_m;
@@ -12,12 +14,18 @@ public abstract class Altitude {
return altitude_m;
}
public abstract int getLabelId();
public static class WGS84 extends Altitude {
private WGS84(double altitude_m) {
super(altitude_m);
}
@Override
public int getLabelId() {
return R.string.wgs84;
}
public static Altitude of(double altitude_m) {
return new WGS84(altitude_m);
@@ -30,6 +38,10 @@ public abstract class Altitude {
super(altitude_m);
}
@Override
public int getLabelId() {
return R.string.egm2008;
}
public static Altitude of(double altitude_m) {
return new EGM2008(altitude_m);
@@ -48,7 +48,9 @@ public class TrackPoint {
private TrackPoint.Id id;
@NonNull
private Instant time;
private Double latitude;
private Double longitude;
private Float accuracy;
@@ -96,10 +98,16 @@ public class TrackPoint {
public TrackPoint(@NonNull Type type) {
this.type = type;
this.time = Instant.now(); //TODO This may result in #800
}
public TrackPoint(@NonNull Type type, Instant time) {
this(type);
this.time = time;
}
public TrackPoint(@NonNull Location location) {
this(Type.TRACKPOINT, location, Instant.now());
this(Type.TRACKPOINT, location, Instant.now()); //TODO This may result in #800
}
public TrackPoint(@NonNull Type type, @NonNull Location location, @NonNull Instant time) {
@@ -116,11 +124,6 @@ public class TrackPoint {
setTime(time);
}
public TrackPoint(@NonNull Type type, Instant time) {
this(type);
this.time = time;
}
public TrackPoint(double latitude, double longitude, Altitude altitude, Instant time) {
this(Type.TRACKPOINT);
this.latitude = latitude;
@@ -37,6 +37,7 @@ import java.util.Collections;
import java.util.Iterator;
import java.util.List;
import java.util.UUID;
import java.util.stream.StreamSupport;
import de.dennisguse.opentracks.BuildConfig;
import de.dennisguse.opentracks.content.data.Altitude;
@@ -15,8 +15,6 @@ import de.dennisguse.opentracks.util.UnitConversions;
/**
* Provides cadence in rpm and speed in milliseconds from Bluetooth LE Cycling Cadence and Speed sensors.
* <p>
* https://www.bluetooth.org/docman/handlers/downloaddoc.ashx?doc_id=261449
*/
public final class SensorDataCycling {
@@ -96,7 +94,7 @@ public final class SensorDataCycling {
public static class DistanceSpeed extends SensorData<DistanceSpeed.Data> {
private final Integer wheelRevolutionsCount; // UINT16
private final Long wheelRevolutionsCount; // UINT32
private final Integer wheelRevolutionsTime; // UINT16; 1/1024s
public DistanceSpeed(String sensorAddress) {
@@ -105,7 +103,7 @@ public final class SensorDataCycling {
this.wheelRevolutionsTime = null;
}
public DistanceSpeed(String sensorAddress, String sensorName, int wheelRevolutionsCount, int wheelRevolutionsTime) {
public DistanceSpeed(String sensorAddress, String sensorName, long wheelRevolutionsCount, int wheelRevolutionsTime) {
super(sensorAddress, sensorName);
this.wheelRevolutionsCount = wheelRevolutionsCount;
this.wheelRevolutionsTime = wheelRevolutionsTime;
@@ -115,7 +113,7 @@ public final class SensorDataCycling {
return wheelRevolutionsCount != null && wheelRevolutionsTime != null;
}
public int getWheelRevolutionsCount() {
public long getWheelRevolutionsCount() {
return wheelRevolutionsCount;
}
@@ -128,20 +126,20 @@ public final class SensorDataCycling {
float timeDiff_ms = UintUtils.diff(wheelRevolutionsTime, previous.wheelRevolutionsTime, UintUtils.UINT16_MAX) / 1024f * UnitConversions.S_TO_MS;
Duration timeDiff = Duration.ofMillis((long) timeDiff_ms);
if (timeDiff.isZero() || timeDiff.isNegative()) {
Log.e(TAG, "Timestamps difference is invalid: cannot compute cadence.");
Log.e(TAG, "Timestamps difference is invalid: cannot compute speed.");
value = null;
} else {
long wheelDiff = UintUtils.diff(wheelRevolutionsCount, previous.wheelRevolutionsCount, UintUtils.UINT16_MAX);
wheelDiff = Math.abs(wheelDiff); //HACK for Garmin Speed 2 as some of those seem to count backwards
Distance distance = wheelCircumference.multipliedBy(wheelDiff);
Distance distanceOverall = distance;
if (previous.hasValue()) {
distanceOverall = distance.plus(previous.getValue().distanceOverall);
}
Speed speed_mps = Speed.of(distance, timeDiff);
value = new Data(distance, distanceOverall, speed_mps);
return;
}
long wheelDiff = UintUtils.diff(wheelRevolutionsCount, previous.wheelRevolutionsCount, UintUtils.UINT32_MAX);
Distance distance = wheelCircumference.multipliedBy(wheelDiff);
Distance distanceOverall = distance;
if (previous.hasValue()) {
distanceOverall = distance.plus(previous.getValue().distanceOverall);
}
Speed speed_mps = Speed.of(distance, timeDiff);
value = new Data(distance, distanceOverall, speed_mps);
}
}
@@ -163,11 +161,11 @@ public final class SensorDataCycling {
if (!(obj instanceof DistanceSpeed)) return false;
DistanceSpeed comp = (DistanceSpeed) obj;
if (hasData() && comp.hasData() == hasData()) {
return getWheelRevolutionsCount() == comp.getWheelRevolutionsCount() && getWheelRevolutionsTime() == comp.getWheelRevolutionsTime();
} else {
if (!(hasData() && comp.hasData())) {
return false;
}
return getWheelRevolutionsCount() == comp.getWheelRevolutionsCount() && getWheelRevolutionsTime() == comp.getWheelRevolutionsTime();
}
public static class Data {
@@ -12,7 +12,6 @@ import android.widget.TextView;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.fragment.app.Fragment;
import androidx.fragment.app.FragmentActivity;
import androidx.lifecycle.LiveData;
import androidx.lifecycle.ViewModelProvider;
import androidx.recyclerview.widget.LinearLayoutManager;
@@ -20,15 +19,10 @@ import androidx.recyclerview.widget.LinearLayoutManager;
import java.util.List;
import de.dennisguse.opentracks.R;
import de.dennisguse.opentracks.TrackActivityDataHubInterface;
import de.dennisguse.opentracks.adapters.IntervalStatisticsAdapter;
import de.dennisguse.opentracks.content.TrackDataHub;
import de.dennisguse.opentracks.content.TrackDataListener;
import de.dennisguse.opentracks.content.data.Speed;
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.databinding.IntervalListViewBinding;
import de.dennisguse.opentracks.stats.TrackStatistics;
import de.dennisguse.opentracks.util.PreferencesUtils;
import de.dennisguse.opentracks.viewmodels.IntervalStatistics;
import de.dennisguse.opentracks.viewmodels.IntervalStatisticsModel;
@@ -36,31 +30,31 @@ import de.dennisguse.opentracks.viewmodels.IntervalStatisticsModel;
/**
* A fragment to display the intervals from recorded track.
*/
public class IntervalsFragment extends Fragment implements TrackDataListener {
public class IntervalsFragment extends Fragment {
private static final String TAG = IntervalsFragment.class.getSimpleName();
private static final String FROM_TOP_TO_BOTTOM_KEY = "fromTopToBottom";
private static final String TRACK_ID_KEY = "trackId";
private IntervalStatisticsModel viewModel;
protected IntervalStatisticsAdapter.StackMode stackModeListView;
private IntervalStatisticsModel.IntervalOption selectedInterval;
private Track.Id trackId;
private boolean metricUnits;
private IntervalStatisticsAdapter adapter;
private ArrayAdapter<IntervalStatisticsModel.IntervalOption> spinnerAdapter;
private SharedPreferences sharedPreferences;
private TrackDataHub trackDataHub;
private boolean isReportSpeed;
private IntervalListViewBinding viewBinding;
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(sharedPreferences, getContext());
uploadIntervals();
updateIntervals(PreferencesUtils.isMetricUnits(sharedPreferences, getContext()), selectedInterval);
if (spinnerAdapter != null) {
spinnerAdapter.notifyDataSetChanged();
}
@@ -70,12 +64,14 @@ public class IntervalsFragment extends Fragment implements TrackDataListener {
/**
* Creates an instance of this class.
*
* @param trackId
* @param fromTopToBottom If true then the intervals are shown from top to bottom (the first interval on top). Otherwise the intervals are shown from bottom to top.
* @return IntervalsFragment instance.
*/
public static Fragment newInstance(boolean fromTopToBottom) {
public static Fragment newInstance(Track.Id trackId, boolean fromTopToBottom) {
Bundle bundle = new Bundle();
bundle.putBoolean(FROM_TOP_TO_BOTTOM_KEY, fromTopToBottom);
bundle.putParcelable(TRACK_ID_KEY, trackId);
IntervalsFragment intervalsFragment = new IntervalsFragment();
intervalsFragment.setArguments(bundle);
return intervalsFragment;
@@ -85,6 +81,7 @@ public class IntervalsFragment extends Fragment implements TrackDataListener {
public void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
stackModeListView = getArguments().getBoolean(FROM_TOP_TO_BOTTOM_KEY, true) ? IntervalStatisticsAdapter.StackMode.STACK_FROM_TOP : IntervalStatisticsAdapter.StackMode.STACK_FROM_BOTTOM;
trackId = getArguments().getParcelable(TRACK_ID_KEY);
}
@Override
@@ -127,8 +124,7 @@ public class IntervalsFragment extends Fragment implements TrackDataListener {
viewBinding.spinnerIntervals.setOnItemSelectedListener(new AdapterView.OnItemSelectedListener() {
@Override
public void onItemSelected(AdapterView<?> adapterView, View view, int i, long l) {
selectedInterval = IntervalStatisticsModel.IntervalOption.values()[i];
uploadIntervals();
updateIntervals(metricUnits, IntervalStatisticsModel.IntervalOption.values()[i]);
}
@Override
@@ -140,18 +136,27 @@ public class IntervalsFragment extends Fragment implements TrackDataListener {
@Override
public void onResume() {
super.onResume();
sharedPreferences.registerOnSharedPreferenceChangeListener(sharedPreferenceChangeListener);
sharedPreferenceChangeListener.onSharedPreferenceChanged(sharedPreferences, null);
ContentProviderUtils contentProviderUtils = new ContentProviderUtils(getContext());
Track track = contentProviderUtils.getTrack(trackId);
if (track != null) {
isReportSpeed = PreferencesUtils.isReportSpeed(sharedPreferences, getContext(), track.getCategory());
}
viewModel = new ViewModelProvider(getActivity()).get(IntervalStatisticsModel.class);
loadIntervals();
resumeTrackDataHub();
}
@Override
public void onPause() {
super.onPause();
pauseTrackDataHub();
sharedPreferences.unregisterOnSharedPreferenceChangeListener(sharedPreferenceChangeListener);
if (viewModel != null) {
viewModel.onPause();
}
}
@Override
@@ -177,88 +182,18 @@ public class IntervalsFragment extends Fragment implements TrackDataListener {
if (viewModel == null) {
return;
}
LiveData<List<IntervalStatistics.Interval>> liveData = viewModel.getIntervalStats(metricUnits, selectedInterval);
viewBinding.intervalRate.setText(isReportSpeed ? getString(R.string.stats_speed) : getString(R.string.stats_pace));
LiveData<List<IntervalStatistics.Interval>> liveData = viewModel.getIntervalStats(trackId, metricUnits, selectedInterval);
liveData.observe(getActivity(), intervalList -> adapter.swapData(intervalList, metricUnits, isReportSpeed));
}
private synchronized void uploadIntervals() {
if (viewModel == null) {
return;
}
viewModel.upload(metricUnits, selectedInterval);
}
private synchronized void updateIntervals(boolean metricUnits, IntervalStatisticsModel.IntervalOption selectedInterval) {
boolean update = metricUnits != this.metricUnits || !selectedInterval.sameMultiplier(this.selectedInterval);
this.metricUnits = metricUnits;
this.selectedInterval = selectedInterval;
/**
* Resumes the trackDataHub.
* Needs to be synchronized because trackDataHub can be accessed by multiple threads.
*/
private synchronized void resumeTrackDataHub() {
trackDataHub = ((TrackActivityDataHubInterface) getActivity()).getTrackDataHub();
trackDataHub.registerTrackDataListener(this);
}
/**
* Pauses the trackDataHub.
* Needs to be synchronized because trackDataHub can be accessed by multiple threads.
*/
private synchronized void pauseTrackDataHub() {
trackDataHub.unregisterTrackDataListener(this);
trackDataHub = null;
}
@Override
public void onTrackUpdated(Track track) {
if (isResumed()) {
getActivity().runOnUiThread(() -> {
if (isResumed()) {
// Set category.
String category = track != null ? track.getCategory() : "";
// Set rate label.
isReportSpeed = PreferencesUtils.isReportSpeed(sharedPreferences, getContext(), category);
viewBinding.intervalRate.setText(isReportSpeed ? R.string.stats_speed : R.string.stats_pace);
}
});
}
}
@Override
public void clearTrackPoints() {
if (isResumed()) {
viewModel.clear();
}
}
@Override
public void onSampledInTrackPoint(@NonNull TrackPoint trackPoint, @NonNull TrackStatistics unused, Speed unused2, double unused3) {
if (isResumed()) {
viewModel.add(trackPoint);
}
}
@Override
public void onSampledOutTrackPoint(@NonNull TrackPoint trackPoint, @NonNull TrackStatistics unused) {
if (isResumed()) {
viewModel.add(trackPoint);
}
}
@Override
public void onNewTrackPointsDone() {
if (isResumed()) {
runOnUiThread(viewModel::onNewTrackPoints);
}
}
/**
* Runs a runnable on the UI thread.
*
* @param runnable the runnable
*/
private void runOnUiThread(Runnable runnable) {
FragmentActivity fragmentActivity = getActivity();
if (fragmentActivity != null) {
fragmentActivity.runOnUiThread(runnable);
if (update && viewModel != null) {
viewModel.update(trackId, this.metricUnits, this.selectedInterval);
}
}
}
@@ -276,10 +276,18 @@ public class StatisticsRecordingFragment extends Fragment {
if (preferenceShowAltitude) {
// Current altitude
Float altitude = latestTrackPoint != null && latestTrackPoint.hasAltitude() ? (float) latestTrackPoint.getAltitude().toM() : null;
Float altitude = null;
int labelId = R.string.value_unknown;
if (latestTrackPoint != null && latestTrackPoint.hasAltitude()) {
altitude = (float) latestTrackPoint.getAltitude().toM();
labelId = latestTrackPoint.getAltitude().getLabelId();
}
Pair<String, String> parts = StringUtils.formatAltitude(getContext(), altitude, preferenceMetricUnits);
viewBinding.statsAltitudeCurrentValue.setText(parts.first);
viewBinding.statsAltitudeCurrentUnit.setText(parts.second);
viewBinding.statsAltitudeCurrentLabelEgm.setText(labelId);
}
// Set coordinate
@@ -313,7 +321,7 @@ public class StatisticsRecordingFragment extends Fragment {
sharedPreferenceChangeListener.onSharedPreferenceChanged(sharedPreferences, getString(R.string.stats_rate_key));
}
this.latestTrackPoint = recordingData.getLatestTrackPoint();
latestTrackPoint = recordingData.getLatestTrackPoint();
if (latestTrackPoint != null && latestTrackPoint.hasLocation() && !latestTrackPoint.isRecent()) {
latestTrackPoint = null;
}
@@ -36,12 +36,14 @@ import androidx.core.app.TaskStackBuilder;
import androidx.lifecycle.LiveData;
import androidx.lifecycle.MutableLiveData;
import java.io.IOException;
import java.time.Duration;
import java.time.Instant;
import de.dennisguse.opentracks.R;
import de.dennisguse.opentracks.TrackListActivity;
import de.dennisguse.opentracks.TrackRecordingActivity;
import de.dennisguse.opentracks.content.data.Altitude;
import de.dennisguse.opentracks.content.data.Distance;
import de.dennisguse.opentracks.content.data.Marker;
import de.dennisguse.opentracks.content.data.Track;
@@ -59,6 +61,7 @@ import de.dennisguse.opentracks.services.tasks.PeriodicTaskExecutor;
import de.dennisguse.opentracks.settings.SettingsActivity;
import de.dennisguse.opentracks.stats.TrackStatistics;
import de.dennisguse.opentracks.stats.TrackStatisticsUpdater;
import de.dennisguse.opentracks.util.EGM2008Utils;
import de.dennisguse.opentracks.util.ExportUtils;
import de.dennisguse.opentracks.util.IntentUtils;
import de.dennisguse.opentracks.util.PreferencesUtils;
@@ -128,6 +131,8 @@ public class TrackRecordingService extends Service implements HandlerServer.Hand
private BluetoothRemoteSensorManager remoteSensorManager;
private AltitudeSumManager altitudeSumManager;
private EGM2008Utils.EGM2008Correction egm2008Correction;
private TrackStatisticsUpdater trackStatisticsUpdater;
private TrackPoint lastTrackPoint;
private boolean isIdle;
@@ -539,6 +544,8 @@ public class TrackRecordingService extends Service implements HandlerServer.Hand
fillWithSensorDataSet(trackPoint);
correctAltitude(trackPoint);
notificationManager.updateTrackPoint(this, track.getTrackStatistics(), trackPoint, recordingGpsAccuracy);
//TODO Figure out how to avoid loading the lastValidTrackPoint from the database
@@ -602,6 +609,25 @@ public class TrackRecordingService extends Service implements HandlerServer.Hand
lastTrackPoint = trackPoint;
}
//TODO Identical to TrackDataHub
private void correctAltitude(TrackPoint trackPoint) {
if (!trackPoint.hasLocation() || !trackPoint.hasAltitude()) {
Log.d(TAG, "No altitude correction necessary.");
return;
}
if (egm2008Correction == null || !egm2008Correction.canCorrect(trackPoint.getLocation())) {
try {
egm2008Correction = EGM2008Utils.createCorrection(this, trackPoint.getLocation());
} catch (IOException e) {
Log.e(TAG, "Could not load altitude correction for " + trackPoint, e);
return;
}
}
trackPoint.setAltitude(Altitude.EGM2008.of(egm2008Correction.correctAltitude(trackPoint.getLocation())));
}
@Override
public void newGpsStatus(GpsStatusValue gpsStatusValue) {
notificationManager.updateContent(getString(gpsStatusValue.message));
@@ -652,8 +678,6 @@ public class TrackRecordingService extends Service implements HandlerServer.Hand
trackStatisticsUpdater.addTrackPoint(trackPoint, recordingDistanceInterval);
track.setTrackStatistics(trackStatisticsUpdater.getTrackStatistics());
recordingDataObservable.postValue(new RecordingData(track, trackPoint, sensorDataSet));
contentProviderUtils.updateTrack(track);
} catch (SQLiteException e) {
/*
@@ -670,7 +694,7 @@ public class TrackRecordingService extends Service implements HandlerServer.Hand
return null;
}
SensorDataSet sensorData = remoteSensorManager.getSensorData();
SensorDataSet sensorData = remoteSensorManager.getSensorData(); //TODO Should return a copy of SensorDataSet
if (sensorData == null) {
return null;
}
@@ -756,10 +780,26 @@ public class TrackRecordingService extends Service implements HandlerServer.Hand
return;
}
Track track = contentProviderUtils.getTrack(recordingStatus.getTrackId());
track.getTrackStatistics().setTotalTime(TrackRecordingService.this.getTotalTime());
SensorDataSet sensorDataSet = fillWithSensorDataSet(lastTrackPoint);
recordingDataObservable.postValue(new RecordingData(track, lastTrackPoint, sensorDataSet));
// Compute temporary track statistics using sensorData and update time.
//TODO This somehow should happen in the HandlerServer as we create a new TrackPoint.
TrackStatisticsUpdater tmpTrackStatisticsUpdater = new TrackStatisticsUpdater(trackStatisticsUpdater);
TrackPoint tmpLastTrackPoint = new TrackPoint(TrackPoint.Type.TRACKPOINT);
if (lastTrackPoint != null && lastTrackPoint.hasLocation()) {
//TODO Should happen in TrackPoint? via constructor
tmpLastTrackPoint.setSpeed(lastTrackPoint.getSpeed());
tmpLastTrackPoint.setAltitude(lastTrackPoint.getAltitude());
tmpLastTrackPoint.setLongitude(lastTrackPoint.getLongitude());
tmpLastTrackPoint.setLatitude(lastTrackPoint.getLatitude());
}
SensorDataSet sensorDataSet = fillWithSensorDataSet(tmpLastTrackPoint);
tmpTrackStatisticsUpdater.addTrackPoint(tmpLastTrackPoint, recordingDistanceInterval);
track.setTrackStatistics(tmpTrackStatisticsUpdater.getTrackStatistics());
recordingDataObservable.postValue(new RecordingData(track, tmpLastTrackPoint, sensorDataSet));
}
public LiveData<RecordingStatus> getRecordingStatusObservable() {
@@ -25,13 +25,11 @@ import android.util.Log;
import androidx.annotation.NonNull;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Locale;
import de.dennisguse.opentracks.content.data.Distance;
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;
@@ -47,11 +45,6 @@ import de.dennisguse.opentracks.viewmodels.IntervalStatistics;
*/
public class AnnouncementPeriodicTask implements PeriodicTask {
/**
* The rate at which announcements are spoken.
*/
private static final float TTS_SPEECH_RATE = 0.9f;
private static final String TAG = AnnouncementPeriodicTask.class.getSimpleName();
private final Context context;
@@ -178,17 +171,13 @@ public class AnnouncementPeriodicTask implements PeriodicTask {
}
String category = track.getCategory();
//TODO Querying all TrackPoints all the time is inefficient; get infos from TrackRecordingService
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);
Distance minGPSDistance = PreferencesUtils.getRecordingDistanceInterval(sharedPreferences, context);
IntervalStatistics intervalStatistics = new IntervalStatistics(trackPoints, Distance.one(isMetricUnits));
TrackPointIterator trackPointIterator = contentProviderUtils.getTrackPointLocationIterator(track.getId(), null);
IntervalStatistics intervalStatistics = new IntervalStatistics(Distance.one(isMetricUnits), minGPSDistance);
intervalStatistics.addTrackPoints(trackPointIterator);
IntervalStatistics.Interval lastInterval = intervalStatistics.getLastInterval();
String announcement = AnnouncementUtils.getAnnouncement(context, trackStatistics, isMetricUnits, isReportSpeed, lastInterval);
@@ -217,10 +206,7 @@ public class AnnouncementPeriodicTask implements PeriodicTask {
*/
}
tts.setLanguage(locale);
// Slow down the speed just a bit as it is hard to hear when exercising.
tts.setSpeechRate(TTS_SPEECH_RATE);
tts.setSpeechRate(PreferencesUtils.getVoiceSpeedRate(PreferencesUtils.getSharedPreferences(context), context));
tts.setOnUtteranceProgressListener(utteranceListener);
}
@@ -76,16 +76,10 @@ final class PreferenceHelper {
}
} else {
int feet = (int) (value * UnitConversions.M_TO_FT);
if (feet < 2000) {
displayValue = context.getString(R.string.value_integer_feet, feet);
if (value == maxRecordingDistanceDefault) {
entries[i] = context.getString(R.string.value_integer_feet_recommended, feet);
} else {
entries[i] = displayValue;
}
displayValue = context.getString(R.string.value_integer_feet, feet);
if (value == maxRecordingDistanceDefault) {
entries[i] = context.getString(R.string.value_integer_feet_recommended, feet);
} else {
double mile = feet * UnitConversions.FT_TO_MI;
displayValue = context.getString(R.string.value_float_mile, mile);
entries[i] = displayValue;
}
}
@@ -118,24 +112,14 @@ final class PreferenceHelper {
}
} else {
int feet = (int) (value * UnitConversions.M_TO_FT);
if (feet < 2000) {
displayValue = context.getString(R.string.value_integer_feet, feet);
displayValue = context.getString(R.string.value_integer_feet, feet);
if (value == recordingGPSAccuracyDefault) {
entries[i] = context.getString(R.string.value_integer_feet_recommended, feet);
} else if (value == recordingGPSAccuracyExcellent) {
entries[i] = context.getString(R.string.value_integer_feet_excellent_gps, feet);
} else {
entries[i] = displayValue;
}
if (value == recordingGPSAccuracyDefault) {
entries[i] = context.getString(R.string.value_integer_feet_recommended, feet);
} else if (value == recordingGPSAccuracyExcellent) {
entries[i] = context.getString(R.string.value_integer_feet_excellent_gps, feet);
} else {
double mile = feet * UnitConversions.FT_TO_MI;
displayValue = context.getString(R.string.value_float_mile, mile);
if (value == recordingGPSAccuracyPoor) {
entries[i] = context.getString(R.string.value_float_mile_poor_gps, mile);
} else {
entries[i] = displayValue;
}
entries[i] = displayValue;
}
}
}
@@ -130,7 +130,8 @@ public class SettingsActivity extends AbstractActivity implements ChooseActivity
// Used to forward update from ChooseActivityTypeDialogFragment; TODO Could be replaced with LiveData.
private ActivityTypePreference.ActivityPreferenceDialog activityPreferenceDialog;
private Preference voiceAnnouncements;
private Preference announcementsFrequency;
private Preference announcementsSpeed;
@Override
public void onCreate(@Nullable Bundle savedInstanceState) {
@@ -140,7 +141,7 @@ public class SettingsActivity extends AbstractActivity implements ChooseActivity
ttsInitStatus = status;
Log.i(TAG, "TextToSpeech initialized with status " + status);
if (voiceAnnouncements != null) {
if (announcementsFrequency != null && announcementsSpeed != null) {
updateVoiceAnnouncements();
}
});
@@ -172,7 +173,8 @@ public class SettingsActivity extends AbstractActivity implements ChooseActivity
return directory != null ? directory.getName() : getString(R.string.not_set);
});
voiceAnnouncements = findPreference(getString(R.string.voice_frequency_key));
announcementsFrequency = findPreference(getString(R.string.voice_frequency_key));
announcementsSpeed = findPreference(getString(R.string.voice_speed_rate_key));
updateVoiceAnnouncements();
}
@@ -232,7 +234,8 @@ public class SettingsActivity extends AbstractActivity implements ChooseActivity
super.onDestroy();
trackRecordingServiceConnection.unbind(getContext());
sharedPreferences = null;
voiceAnnouncements = null;
announcementsFrequency = null;
announcementsSpeed = null;
tts = null;
}
@@ -323,7 +326,9 @@ public class SettingsActivity extends AbstractActivity implements ChooseActivity
}
private void updateVoiceAnnouncements() {
voiceAnnouncements.setEnabled(ttsInitStatus == TextToSpeech.SUCCESS);
boolean enabled = ttsInitStatus == TextToSpeech.SUCCESS;
announcementsFrequency.setEnabled(enabled);
announcementsSpeed.setEnabled(enabled);
}
private void onRecordingStatusChanged(TrackRecordingService.RecordingStatus status) {
@@ -18,6 +18,8 @@ package de.dennisguse.opentracks.stats;
import androidx.annotation.NonNull;
import java.util.Arrays;
/**
* This class maintains a ring buffer of doubles.
* This buffer is a convenient class for storing a series of doubles and calculating information about them.
@@ -49,6 +51,12 @@ class DoubleRingBuffer {
reset();
}
DoubleRingBuffer(DoubleRingBuffer toCopy) {
this.buffer = Arrays.copyOf(toCopy.buffer, toCopy.buffer.length);
this.index = toCopy.index;
this.isFull = toCopy.isFull;
}
/**
* Resets the buffer.
*/
@@ -64,18 +64,18 @@ public class TrackStatisticsUpdater {
private final TrackStatistics trackStatistics;
private final DoubleRingBuffer altitudeBuffer_m = new DoubleRingBuffer(ALTITUDE_SMOOTHING_FACTOR);
private final DoubleRingBuffer speedBuffer_mps = new DoubleRingBuffer(SPEED_SMOOTHING_FACTOR);
private final DoubleRingBuffer altitudeBuffer_m;
private final DoubleRingBuffer speedBuffer_mps;
// The current segment's statistics
private final TrackStatistics currentSegment = new TrackStatistics();
private final TrackStatistics currentSegment;
// Current segment's last trackPoint
private TrackPoint lastTrackPoint;
// Current segment's last moving trackPoint
private TrackPoint lastMovingTrackPoint;
public TrackStatisticsUpdater() {
trackStatistics = new TrackStatistics();
this(new TrackStatistics());
}
/**
@@ -85,7 +85,25 @@ public class TrackStatisticsUpdater {
*/
public TrackStatisticsUpdater(TrackStatistics trackStatistics) {
this.trackStatistics = trackStatistics;
this.currentSegment = new TrackStatistics();
trackInitialized = true;
altitudeBuffer_m = new DoubleRingBuffer(ALTITUDE_SMOOTHING_FACTOR);
speedBuffer_mps = new DoubleRingBuffer(SPEED_SMOOTHING_FACTOR);
}
public TrackStatisticsUpdater(TrackStatisticsUpdater toCopy) {
this.currentSegment = new TrackStatistics(toCopy.currentSegment);
this.trackStatistics = new TrackStatistics(toCopy.trackStatistics);
this.trackInitialized = toCopy.trackInitialized;
this.segmentInitialized = toCopy.segmentInitialized;
this.altitudeBuffer_m = new DoubleRingBuffer(toCopy.altitudeBuffer_m);
this.speedBuffer_mps = new DoubleRingBuffer(toCopy.speedBuffer_mps);
this.lastTrackPoint = toCopy.lastTrackPoint;
this.lastMovingTrackPoint = toCopy.lastMovingTrackPoint;
}
public TrackStatistics getTrackStatistics() {
@@ -109,11 +127,6 @@ public class TrackStatisticsUpdater {
* @param minGPSDistance the min recording distance
*/
public void addTrackPoint(TrackPoint trackPoint, Distance minGPSDistance) {
internalAddTrackPoint(trackPoint, minGPSDistance);
Log.v(TAG, this.toString());
}
private void internalAddTrackPoint(TrackPoint trackPoint, Distance minGPSDistance) {
if (!trackInitialized) {
trackStatistics.setStartTime(trackPoint.getTime());
trackInitialized = true;
@@ -161,13 +174,14 @@ public class TrackStatisticsUpdater {
return;
}
if (!trackPoint.hasSensorDistance()) {
if (!trackPoint.hasSensorDistance()
&& trackPoint.hasLocation() && lastMovingTrackPoint.hasLocation()) {
// GPS-based distance/speed
Distance movingDistance = trackPoint.distanceToPrevious(lastMovingTrackPoint);
if (movingDistance.lessThan(minGPSDistance) && !trackPoint.isMoving()) {
if (movingDistance != null && movingDistance.lessThan(minGPSDistance) && !trackPoint.isMoving()) {
speedBuffer_mps.reset();
lastTrackPoint = trackPoint;
return;
return; //TOOD Why? Is there nothing to be done afterwards?
}
// Update total distance
currentSegment.addTotalDistance(movingDistance);
@@ -96,6 +96,22 @@ public class PreferencesUtils {
}
}
private static float getFloat(SharedPreferences sharedPreferences, Context context, int keyId, float defaultValue) {
try {
return sharedPreferences.getFloat(getKey(context, keyId), defaultValue);
} catch (ClassCastException e) {
//Ignore
}
//NOTE: We assume that the data was stored as String due to use of ListPreference.
try {
String stringValue = sharedPreferences.getString(getKey(context, keyId), null);
return Float.parseFloat(stringValue);
} catch (NumberFormatException e) {
return defaultValue;
}
}
public static String getString(SharedPreferences sharedPreferences, Context context, int keyId, String defaultValue) {
return sharedPreferences.getString(getKey(context, keyId), defaultValue);
}
@@ -112,6 +128,16 @@ public class PreferencesUtils {
return STATS_UNIT.equals(getString(sharedPreferences, context, R.string.stats_units_key, STATS_UNIT));
}
public static void setMetricUnits(SharedPreferences sharedPreferences, Context context, boolean metricUnits) {
String unit;
if (metricUnits) {
unit = context.getString(R.string.stats_units_metric);
} else {
unit = context.getString(R.string.stats_units_imperial);
}
setString(sharedPreferences, context, R.string.stats_units_key, 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(sharedPreferences, context, R.string.stats_rate_key, STATS_RATE_DEFAULT);
@@ -181,6 +207,11 @@ public class PreferencesUtils {
return getInt(sharedPreferences, context, R.string.voice_frequency_key, VOICE_FREQUENCY_DEFAULT);
}
public static float getVoiceSpeedRate(SharedPreferences sharedPreferences, Context context) {
final float DEFAULT = Float.parseFloat(context.getResources().getString(R.string.voice_frequency_default));
return getFloat(sharedPreferences, context, R.string.voice_speed_rate_key, DEFAULT);
}
public static Distance getRecordingDistanceInterval(SharedPreferences sharedPreferences, Context context) {
return Distance.of(getInt(sharedPreferences, context, R.string.recording_distance_interval_key, getRecordingDistanceIntervalDefaultInternal(context)));
}
@@ -33,7 +33,6 @@ import java.time.ZoneOffset;
import java.time.ZonedDateTime;
import java.time.format.DateTimeFormatter;
import java.time.temporal.TemporalAccessor;
import java.util.Locale;
import de.dennisguse.opentracks.R;
import de.dennisguse.opentracks.content.data.Distance;
@@ -96,38 +95,15 @@ public class StringUtils {
return context.getString(R.string.value_unknown);
}
if (metricUnits) {
if (distance.greaterThan(Distance.of(500))) {
return context.getString(R.string.value_float_kilometer, distance.toKM());
} else {
return context.getString(R.string.value_float_meter, distance.toM());
}
} else {
if (distance.greaterThan(Distance.ofMile(0.5))) {
return context.getString(R.string.value_float_mile, distance.toMI());
} else {
return context.getString(R.string.value_float_feet, distance.toFT());
}
}
Pair<String, String> distanceParts = getDistanceParts(context, distance, metricUnits);
return context.getString(R.string.distance_with_unit, distanceParts.first, distanceParts.second);
}
public static String formatSpeed(Context context, Speed speed, boolean metricUnits, boolean reportSpeed) {
if (reportSpeed) {
if (metricUnits) {
return context.getString(R.string.value_float_kilometer_hour, speed.toKMH());
} else {
return context.getString(R.string.value_float_mile_hour, speed.toMPH());
}
} else {
int pace = (int) speed.toPace(metricUnits).getSeconds();
int minutes = pace / 60;
int seconds = pace % 60;
if (metricUnits) {
return context.getString(R.string.value_pace_kilometer, minutes, seconds);
} else {
return context.getString(R.string.value_pace_mile, minutes, seconds);
}
}
Pair<String, String> distanceParts = getSpeedParts(context, speed, metricUnits, reportSpeed);
return context.getString(R.string.speed_with_unit, distanceParts.first, distanceParts.second);
}
private static String formatDecimal(double value) {
@@ -139,6 +115,7 @@ public class StringUtils {
*/
public static String formatDecimal(double value, int decimalPlaces) {
DecimalFormat df = new DecimalFormat();
df.setMinimumFractionDigits(decimalPlaces);
df.setMaximumFractionDigits(decimalPlaces);
df.setRoundingMode(RoundingMode.HALF_EVEN);
return df.format(value);
@@ -173,7 +150,7 @@ public class StringUtils {
return new Pair<>(formatDecimal(distance.toM()), context.getString(R.string.unit_meter));
}
} else {
if (distance.greaterThan(Distance.of(0.5 * UnitConversions.M_TO_MI))) {
if (distance.greaterThan(Distance.ofMile(0.5))) {
return new Pair<>(formatDecimal(distance.toMI()), context.getString(R.string.unit_mile));
} else {
return new Pair<>(formatDecimal(distance.toFT()), context.getString(R.string.unit_feet));
@@ -211,7 +188,7 @@ public class StringUtils {
int minutes = pace / 60;
int seconds = pace % 60;
return new Pair<>(String.format(Locale.US, "%d:%02d", minutes, seconds), unitString);
return new Pair<>(context.getString(R.string.time, minutes, seconds), unitString);
}
/**
@@ -238,7 +215,7 @@ public class StringUtils {
}
StringBuilder builder = new StringBuilder();
builder.append("[").append(category).append("]");
builder.append(getCategory(category));
if (description != null && description.length() != 0) {
builder.append(" ").append(description);
}
@@ -11,14 +11,14 @@ public class UintUtils {
/**
* Computes a - b for UINT with overflow (b < a).
*
* @return diff or -1 (invalid)
* @return diff
*/
public static long diff(long a, long b, final long UINT_MAX) {
if (a < 0 || b < 0) {
return -1;
throw new RuntimeException("a or b cannot be less than zero.");
}
if (a > UINT_MAX || b > UINT_MAX) {
return -1;
throw new RuntimeException("a or b are outside of the allowed range.");
}
if (a >= b) {
@@ -1,6 +1,6 @@
package de.dennisguse.opentracks.viewmodels;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import java.time.Duration;
import java.util.ArrayList;
@@ -9,45 +9,72 @@ import java.util.List;
import de.dennisguse.opentracks.content.data.Distance;
import de.dennisguse.opentracks.content.data.Speed;
import de.dennisguse.opentracks.content.data.TrackPoint;
import de.dennisguse.opentracks.content.provider.TrackPointIterator;
import de.dennisguse.opentracks.stats.TrackStatistics;
import de.dennisguse.opentracks.stats.TrackStatisticsUpdater;
public class IntervalStatistics {
private final List<Interval> intervalList = new ArrayList<>();
private TrackStatisticsUpdater trackStatisticsUpdater = new TrackStatisticsUpdater();
private final List<Interval> intervalList;
private final Distance distanceInterval;
private final Distance minGPSDistance;
private Interval interval, lastInterval;
public IntervalStatistics(@NonNull List<TrackPoint> trackPoints, Distance distanceInterval) {
intervalList.clear();
/**
* @param distanceInterval distance of every interval.
* @param minGPSDistance the setting value for GPS distance.
*/
public IntervalStatistics(Distance distanceInterval, Distance minGPSDistance) {
this.distanceInterval = distanceInterval;
this.minGPSDistance = minGPSDistance;
if (trackPoints.size() == 0) {
return;
}
interval = new Interval();
lastInterval = new Interval();
intervalList = new ArrayList<>();
intervalList.add(lastInterval);
}
Interval interval = new Interval();
interval.gain_m += trackPoints.get(0).hasAltitudeGain() ? trackPoints.get(0).getAltitudeGain() : 0;
interval.loss_m += trackPoints.get(0).hasAltitudeLoss() ? trackPoints.get(0).getAltitudeLoss() : 0;
for (int i = 1; i < trackPoints.size(); i++) {
TrackPoint prevTrackPoint = trackPoints.get(i - 1);
TrackPoint trackPoint = trackPoints.get(i);
/**
* Complete intervals with the tracks points from the iterator.
*
* @param trackPointIterator
* @return the last track point's id used to compute the intervals.
*/
public TrackPoint.Id addTrackPoints(TrackPointIterator trackPointIterator) {
boolean newIntervalAdded = false;
TrackPoint trackPoint = null;
if (trackPoint.hasLocation() && prevTrackPoint.hasLocation()) {
interval.distance = interval.distance.plus(prevTrackPoint.distanceToPrevious(trackPoint));
interval.time = interval.time.plus(Duration.between(prevTrackPoint.getTime(), trackPoint.getTime()));
interval.gain_m += trackPoint.hasAltitudeGain() ? trackPoint.getAltitudeGain() : 0;
interval.loss_m += trackPoint.hasAltitudeLoss() ? trackPoint.getAltitudeLoss() : 0;
while (trackPointIterator.hasNext()) {
trackPoint = trackPointIterator.next();
trackStatisticsUpdater.addTrackPoint(trackPoint, minGPSDistance);
if (interval.distance.greaterThan(distanceInterval)) {
Interval adjustedInterval = new Interval(interval, distanceInterval.dividedBy(interval.distance));
if (trackStatisticsUpdater.getTrackStatistics().getTotalDistance().plus(interval.distance).greaterOrEqualThan(distanceInterval)) {
interval.add(trackStatisticsUpdater.getTrackStatistics(), trackPoint);
intervalList.add(adjustedInterval);
double adjustFactor = distanceInterval.dividedBy(interval.distance);
Interval adjustedInterval = new Interval(interval);
adjustedInterval.adjust(adjustFactor);
interval = new Interval(interval.distance.minus(adjustedInterval.distance), interval.time.minus(adjustedInterval.time));
}
intervalList.set(intervalList.size() - 1, adjustedInterval);
interval = new Interval(interval.distance.minus(adjustedInterval.distance), interval.time.minus(adjustedInterval.time));
trackStatisticsUpdater = new TrackStatisticsUpdater();
trackStatisticsUpdater.addTrackPoint(trackPoint, minGPSDistance);
lastInterval = new Interval(interval);
intervalList.add(lastInterval);
newIntervalAdded = true;
}
}
if (interval.distance.greaterThan(Distance.of(1))) {
intervalList.add(interval);
if (newIntervalAdded) {
lastInterval.add(trackStatisticsUpdater.getTrackStatistics(), null);
} else {
lastInterval.set(trackStatisticsUpdater.getTrackStatistics());
}
return trackPoint != null ? trackPoint.getId() : null;
}
public List<Interval> getIntervalList() {
@@ -77,8 +104,8 @@ public class IntervalStatistics {
public static class Interval {
private Distance distance = Distance.of(0);
private Duration time = Duration.ofSeconds(0);
private float gain_m = 0f;
private float loss_m = 0f;
private float gain_m;
private float loss_m;
public Interval() {
}
@@ -96,6 +123,18 @@ public class IntervalStatistics {
loss_m = i.loss_m;
}
public Interval(Interval i) {
distance = i.distance;
time = i.time;
gain_m = i.gain_m;
loss_m = i.loss_m;
}
private void adjust(double adjustFactor) {
distance = distance.multipliedBy(adjustFactor);
time = Duration.ofMillis((long) (time.toMillis() * adjustFactor));
}
public Distance getDistance() {
return distance;
}
@@ -111,5 +150,23 @@ public class IntervalStatistics {
public float getLoss_m() {
return loss_m;
}
private void add(TrackStatistics trackStatistics, @Nullable TrackPoint lastTrackPoint) {
distance = distance.plus(trackStatistics.getTotalDistance());
time = time.plus(trackStatistics.getTotalTime());
gain_m = trackStatistics.hasTotalAltitudeGain() ? trackStatistics.getTotalAltitudeGain() : gain_m;
loss_m = trackStatistics.hasTotalAltitudeLoss() ? trackStatistics.getTotalAltitudeLoss() : loss_m;
if (lastTrackPoint != null) {
gain_m = lastTrackPoint.hasAltitudeGain() ? gain_m - lastTrackPoint.getAltitudeGain() : gain_m;
loss_m = lastTrackPoint.hasAltitudeLoss() ? loss_m - lastTrackPoint.getAltitudeLoss() : loss_m;
}
}
private void set(TrackStatistics trackStatistics) {
distance = trackStatistics.getTotalDistance();
time = trackStatistics.getTotalTime();
gain_m = trackStatistics.hasTotalAltitudeGain() ? trackStatistics.getTotalAltitudeGain() : gain_m;
loss_m = trackStatistics.hasTotalAltitudeLoss() ? trackStatistics.getTotalAltitudeLoss() : loss_m;
}
}
}
@@ -1,17 +1,28 @@
package de.dennisguse.opentracks.viewmodels;
import android.app.Application;
import android.content.ContentResolver;
import android.database.ContentObserver;
import android.net.Uri;
import android.os.Handler;
import android.os.HandlerThread;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.lifecycle.AndroidViewModel;
import androidx.lifecycle.MutableLiveData;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.Executor;
import java.util.concurrent.Executors;
import de.dennisguse.opentracks.content.data.Distance;
import de.dennisguse.opentracks.content.data.Track;
import de.dennisguse.opentracks.content.data.TrackPoint;
import de.dennisguse.opentracks.content.data.TrackPointsColumns;
import de.dennisguse.opentracks.content.provider.ContentProviderUtils;
import de.dennisguse.opentracks.content.provider.TrackPointIterator;
import de.dennisguse.opentracks.util.PreferencesUtils;
/**
* This model is used to load intervals for a track.
@@ -19,63 +30,92 @@ import de.dennisguse.opentracks.content.data.TrackPoint;
*/
public class IntervalStatisticsModel extends AndroidViewModel {
private final List<TrackPoint> trackPoints = new ArrayList<>();
private static final String TAG = IntervalStatisticsModel.class.getSimpleName();
private MutableLiveData<List<IntervalStatistics.Interval>> intervalsLiveData;
private IntervalStatistics intervalStatistics;
private Distance distanceInterval;
private final Distance minGPSDistance;
private final ContentResolver contentResolver;
private ContentObserver trackPointsTableObserver;
private TrackPoint.Id lastTrackPointId;
private final Executor executor = Executors.newSingleThreadExecutor();
private HandlerThread handlerThread;
private Handler handler;
public IntervalStatisticsModel(@NonNull Application application) {
super(application);
minGPSDistance = PreferencesUtils.getRecordingDistanceInterval(PreferencesUtils.getSharedPreferences(application), application);
contentResolver = getApplication().getContentResolver();
handlerThread = new HandlerThread(TAG);
handlerThread.start();
handler = new Handler(handlerThread.getLooper());
}
public MutableLiveData<List<IntervalStatistics.Interval>> getIntervalStats(boolean metricUnits, @Nullable IntervalOption interval) {
synchronized (trackPoints) {
if (intervalsLiveData == null) {
if (interval == null) {
interval = IntervalOption.OPTION_1;
}
intervalsLiveData = new MutableLiveData<>();
distanceInterval = interval.getDistance(metricUnits);
loadIntervalStatistics();
}
return intervalsLiveData;
@Override
protected void onCleared() {
super.onCleared();
if (trackPointsTableObserver != null) {
contentResolver.unregisterContentObserver(trackPointsTableObserver);
trackPointsTableObserver = null;
}
}
private void loadIntervalStatistics() {
IntervalStatistics intervalStatistics = new IntervalStatistics(trackPoints, distanceInterval);
intervalsLiveData.postValue(intervalStatistics.getIntervalList());
}
public void add(TrackPoint trackPoint) {
synchronized (trackPoints) {
trackPoints.add(trackPoint);
if (handlerThread != null) {
handlerThread.getLooper().quit();
handlerThread = null;
}
handler = null;
}
public void onNewTrackPoints() {
synchronized (trackPoints) {
if (intervalsLiveData != null) {
loadIntervalStatistics();
}
}
}
public void clear() {
synchronized (trackPoints) {
trackPoints.clear();
}
}
public void upload(boolean metricUnits, @Nullable IntervalOption interval) {
synchronized (trackPoints) {
public MutableLiveData<List<IntervalStatistics.Interval>> getIntervalStats(Track.Id trackId, boolean metricUnits, @Nullable IntervalOption interval) {
if (intervalsLiveData == null) {
if (interval == null) {
interval = IntervalOption.OPTION_1;
}
intervalsLiveData = new MutableLiveData<>();
distanceInterval = interval.getDistance(metricUnits);
loadIntervalStatistics();
intervalStatistics = new IntervalStatistics(distanceInterval, minGPSDistance);
loadIntervalStatistics(trackId);
}
trackPointsTableObserver = new ContentObserver(handler) {
@Override
public void onChange(boolean selfChange, Uri uri) {
loadIntervalStatistics(trackId);
}
};
contentResolver.registerContentObserver(TrackPointsColumns.CONTENT_URI_BY_TRACKID, false, trackPointsTableObserver);
return intervalsLiveData;
}
private void loadIntervalStatistics(Track.Id trackId) {
executor.execute(() -> {
ContentProviderUtils contentProviderUtils = new ContentProviderUtils(getApplication());
try (TrackPointIterator trackPointIterator = contentProviderUtils.getTrackPointLocationIterator(trackId, lastTrackPointId)) {
lastTrackPointId = intervalStatistics.addTrackPoints(trackPointIterator);
intervalsLiveData.postValue(intervalStatistics.getIntervalList());
}
});
}
public void onPause() {
if (trackPointsTableObserver != null) {
contentResolver.unregisterContentObserver(trackPointsTableObserver);
}
}
public void update(Track.Id trackId, boolean metricUnits, @Nullable IntervalOption interval) {
if (interval == null) {
interval = IntervalOption.OPTION_1;
}
lastTrackPointId = null;
distanceInterval = interval.getDistance(metricUnits);
intervalStatistics = new IntervalStatistics(distanceInterval, minGPSDistance);
loadIntervalStatistics(trackId);
}
/**
@@ -103,6 +143,10 @@ public class IntervalStatisticsModel extends AndroidViewModel {
.multipliedBy(multiplier);
}
public boolean sameMultiplier(IntervalOption intervalOption) {
return intervalOption != null && this.multiplier == intervalOption.multiplier;
}
@Override
public String toString() {
return "" + multiplier; //TODO Somehow IntervalsFragment relies on a parsable Integer.
+2 -13
View File
@@ -1,14 +1,3 @@
<?xml version="1.0" encoding="utf-8"?>
<layer-list xmlns:android="http://schemas.android.com/apk/res/android">
<item>
<shape android:shape="oval">
<solid android:color="@color/ic_launcher_background" />
</shape>
</item>
<item>
<inset
android:drawable="@drawable/ic_logo_color_24dp"
android:inset="8dp" />
</item>
</layer-list>
<inset xmlns:android="http://schemas.android.com/apk/res/android"
android:drawable="@drawable/ic_logo_color_24dp" />
+2 -2
View File
@@ -269,7 +269,7 @@
android:id="@+id/stats_altitude_group"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
app:constraint_referenced_ids="stats_altitude_horizontal_line,stats_altitude_current_label,stats_altitude_current_value,stats_altitude_current_unit,stats_altitude_gain_label,stats_altitude_gain_value,stats_altitude_gain_unit,stats_altitude_loss_label,stats_altitude_loss_value,stats_altitude_loss_unit,stats_elevation_current_label_egm" />
app:constraint_referenced_ids="stats_altitude_horizontal_line,stats_altitude_current_label,stats_altitude_current_value,stats_altitude_current_unit,stats_altitude_gain_label,stats_altitude_gain_value,stats_altitude_gain_unit,stats_altitude_loss_label,stats_altitude_loss_value,stats_altitude_loss_unit,@+id/stats_altitude_current_label_egm" />
<!-- altitude -->
<TextView
@@ -282,7 +282,7 @@
app:layout_constraintTop_toBottomOf="@id/stats_altitude_horizontal_line" />
<TextView
android:id="@+id/stats_elevation_current_label_egm"
android:id="@+id/stats_altitude_current_label_egm"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="0dp"
-5
View File
@@ -339,11 +339,6 @@ limitations under the License.
<string name="value_adapt_accuracy">تكييف من أجل الدقة</string>
<string name="value_adapt_battery_life">التكيف بحسب عمر البطارية</string>
<string name="value_always">دائمًا</string>
<string name="value_float_feet">%1$.2f‏ قدم</string>
<string name="value_float_kilometer">%1$.2f‏ كم‏</string>
<string name="value_float_meter">%1$.2f‏ م</string>
<string name="value_float_mile">%1$.2f ميل</string>
<string name="value_float_mile_poor_gps">%1$.1f ميل (إشارة نظام تحديد المواقع سيئة)</string>
<string name="value_integer_feet">%1$d قدم</string>
<string name="value_integer_feet_excellent_gps">%1$d قدم (إشارة نظام تحديد المواقع ممتازة)</string>
<string name="value_integer_feet_recommended">%1$d قدم (موصى به)</string>
-5
View File
@@ -291,11 +291,6 @@
<string name="value_adapt_accuracy">Adapt for accuracy</string>
<string name="value_adapt_battery_life">Adapt for battery life</string>
<string name="value_always">Always</string>
<string name="value_float_feet">%1$.2f ft</string>
<string name="value_float_kilometer">%1$.2f km</string>
<string name="value_float_meter">%1$.2f m</string>
<string name="value_float_mile">%1$.2f mi</string>
<string name="value_float_mile_poor_gps">%1$.1f mi (poor GPS)</string>
<string name="value_integer_feet">%1$d ft</string>
<string name="value_integer_feet_excellent_gps">%1$d ft (excellent GPS)</string>
<string name="value_integer_feet_recommended">%1$d ft (recommended)</string>
-9
View File
@@ -295,11 +295,6 @@
<string name="value_adapt_accuracy">Adaptar para precisión</string>
<string name="value_adapt_battery_life">Adaptar a la duración de la batería</string>
<string name="value_always">Siempre</string>
<string name="value_float_feet">%1$.2f pies</string>
<string name="value_float_kilometer">%1$.2f km</string>
<string name="value_float_meter">%1$.2f m</string>
<string name="value_float_mile">%1$.2f millas</string>
<string name="value_float_mile_poor_gps">%1$.1f millas (señal de GPS deficiente)</string>
<string name="value_integer_feet">%1$d pies</string>
<string name="value_integer_feet_excellent_gps">%1$d pies (señal de GPS excelente)</string>
<string name="value_integer_feet_recommended">%1$d pies (recomendado)</string>
@@ -462,10 +457,6 @@
\nd) desactivar el ahorro de energía.
\n
\nSi todo no ayuda, informe un error. </string>
<string name="value_float_kilometer_hour">%1$.2f km/h</string>
<string name="value_float_mile_hour">%1$.2f mph</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="help_support_content"> Usted puede:
\na) haga correr la voz sobre OpenTracks,
\nb) <a href="https://github.com/OpenTracksApp/OpenTracks/issues"> proponer funciones </a>,
-5
View File
@@ -341,11 +341,6 @@ limitations under the License.
<string name="value_adapt_accuracy">Адаптиране за точност</string>
<string name="value_adapt_battery_life">Адаптиране спрямо батерията</string>
<string name="value_always">Винаги</string>
<string name="value_float_feet">%1$.2f фт</string>
<string name="value_float_kilometer">%1$.2f км</string>
<string name="value_float_meter">%1$.2f м</string>
<string name="value_float_mile">%1$.2f мил</string>
<string name="value_float_mile_poor_gps">%1$.1f мил (слаб GPS)</string>
<string name="value_integer_feet">%1$d фт</string>
<string name="value_integer_feet_excellent_gps">%1$d фт (отличен GPS)</string>
<string name="value_integer_feet_recommended">%1$d фт (препоръчително)</string>
-5
View File
@@ -341,11 +341,6 @@ es vegi el cel. Torneu-ho a provar més tard.</string>
<string name="value_adapt_accuracy">Adapta\'n la precisió</string>
<string name="value_adapt_battery_life">Adapta a la durada de la bateria</string>
<string name="value_always">Sempre</string>
<string name="value_float_feet">%1$.2f peus</string>
<string name="value_float_kilometer">%1$.2f km</string>
<string name="value_float_meter">%1$.2f m</string>
<string name="value_float_mile">%1$.2f milles</string>
<string name="value_float_mile_poor_gps">%1$.1f milles (GPS amb poca senyal)</string>
<string name="value_integer_feet">%1$d peus</string>
<string name="value_integer_feet_excellent_gps">%1$d peus (senyal de GPS excel·lent)</string>
<string name="value_integer_feet_recommended">%1$d peus (recomanat)</string>
-9
View File
@@ -411,11 +411,6 @@ Pokud zařízení GPS hlásí nepřesná data (např. Polohu, rychlost, nadmořs
<string name="value_adapt_accuracy">Upřednostnit přesnost</string>
<string name="value_adapt_battery_life">Upřednostnit výdrži baterie</string>
<string name="value_always">Vždy</string>
<string name="value_float_feet">%1$.2f stop</string>
<string name="value_float_kilometer">%1$.2f km</string>
<string name="value_float_meter">%1$.2f m</string>
<string name="value_float_mile">%1$.2f mil</string>
<string name="value_float_mile_poor_gps">%1$.1f mil (špatný signál GPS)</string>
<string name="value_integer_feet">%1$d stop</string>
<string name="value_integer_feet_excellent_gps">%1$d stop (vynikající signál GPS)</string>
<string name="value_integer_feet_recommended">%1$d stop (doporučeno)</string>
@@ -556,10 +551,6 @@ Pokud zařízení GPS hlásí nepřesná data (např. Polohu, rychlost, nadmořs
<string name="stats_intervals">Intervaly</string>
<string name="stats_split_interval">Rozdělit interval</string>
<string name="track_detail_intervals_tab">Intervaly</string>
<string name="value_float_kilometer_hour">%1$.2f km/h</string>
<string name="value_float_mile_hour">%1$.2f mil/h</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="voice_pace_lap">Čas na kolo %1$s</string>
<string name="hold_to_pause">Podržet pro pozastavení</string>
<string name="toast_message_instant_track_was_exported">Poslední trasa exportována</string>
-9
View File
@@ -417,11 +417,6 @@ Hvis GPS-enheden rapporterer unøjagtige data (f.eks. Placering, hastighed, høj
<string name="value_adapt_accuracy">Juster nøjagtighed</string>
<string name="value_adapt_battery_life">Tilpas til batterilevetid</string>
<string name="value_always">Altid</string>
<string name="value_float_feet">%1$.2f fod</string>
<string name="value_float_kilometer">%1$.2f km</string>
<string name="value_float_meter">%1$.2f m</string>
<string name="value_float_mile">%1$.2f mil</string>
<string name="value_float_mile_poor_gps">%1$.1f mil (dårlig GPS)</string>
<string name="value_integer_feet">%1$d fod</string>
<string name="value_integer_feet_excellent_gps">%1$d fod (fremragende GPS)</string>
<string name="value_integer_feet_recommended">%1$d fod (anbefales)</string>
@@ -545,10 +540,7 @@ Hvis GPS-enheden rapporterer unøjagtige data (f.eks. Placering, hastighed, høj
<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>
@@ -583,7 +575,6 @@ Hvis GPS-enheden rapporterer unøjagtige data (f.eks. Placering, hastighed, høj
<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>
+2 -9
View File
@@ -360,7 +360,7 @@ g) das tun, was Deiner Meinung nach OpenTracks hilft.</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">Durchschnittsgeshwindigkeit
<string name="stats_average_moving_speed">Durchschnittsgeschwindigkeit
\n(Bewegung)</string>
<string name="stats_average_pace">Durchschnitt</string>
<string name="stats_average_speed">Durchschnitt</string>
@@ -422,11 +422,6 @@ g) das tun, was Deiner Meinung nach OpenTracks hilft.</string>
<string name="value_adapt_accuracy">Genauigkeit anpassen</string>
<string name="value_adapt_battery_life">An Akkulaufzeit anpassen</string>
<string name="value_always">Immer</string>
<string name="value_float_feet">%1$.2f ft</string>
<string name="value_float_kilometer">%1$.2f km</string>
<string name="value_float_meter">%1$.2f m</string>
<string name="value_float_mile">%1$.2f Meilen</string>
<string name="value_float_mile_poor_gps">%1$.1f Meilen (schlechtes GPS-Signal)</string>
<string name="value_integer_feet">%1$d Fuß</string>
<string name="value_integer_feet_excellent_gps">%1$d Fuß (ausgezeichnetes GPS-Signal)</string>
<string name="value_integer_feet_recommended">%1$d Fuß (empfohlen)</string>
@@ -601,6 +596,4 @@ g) das tun, was Deiner Meinung nach OpenTracks hilft.</string>
<string name="track_distance_notification">Strecke: %1$s</string>
<string name="track_speed_notification">Geschwindigkeit: %1$s</string>
<string name="track_pace_notification">Tempo: %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>
</resources>
</resources>
-5
View File
@@ -340,11 +340,6 @@ limitations under the License.
<string name="value_adapt_accuracy">Προσαρμογή για ακρίβεια</string>
<string name="value_adapt_battery_life">Καλύτερο για μπατ.</string>
<string name="value_always">Πάντα</string>
<string name="value_float_feet">%1$.2f πόδια</string>
<string name="value_float_kilometer">%1$.2f χιλιόμετρα</string>
<string name="value_float_meter">%1$.2f μέτρα</string>
<string name="value_float_mile">%1$.2f μίλια</string>
<string name="value_float_mile_poor_gps">%1$.1f μίλια (χαμηλό σήμα GPS)</string>
<string name="value_integer_feet">%1$d πόδια</string>
<string name="value_integer_feet_excellent_gps">%1$d πόδια (τέλειο σήμα GPS)</string>
<string name="value_integer_feet_recommended">%1$d ft (συνιστάται)</string>
-9
View File
@@ -412,11 +412,6 @@ Si el dispositivo GPS reporta datos inexactos (por ejemplo, ubicación, velocida
<string name="value_adapt_accuracy">Adaptar para obtener valores exactos</string>
<string name="value_adapt_battery_life">Adaptar a la duración de la batería</string>
<string name="value_always">Siempre</string>
<string name="value_float_feet">%1$.2f pies</string>
<string name="value_float_kilometer">%1$.2f km</string>
<string name="value_float_meter">%1$.2f m</string>
<string name="value_float_mile">%1$.2f millas</string>
<string name="value_float_mile_poor_gps">%1$.1f millas (señal de GPS débil)</string>
<string name="value_integer_feet">%1$d pies</string>
<string name="value_integer_feet_excellent_gps">%1$d pies (señal de GPS excelente)</string>
<string name="value_integer_feet_recommended">%1$d pies (recomendado)</string>
@@ -595,12 +590,8 @@ Si el dispositivo GPS reporta datos inexactos (por ejemplo, ubicación, velocida
<string name="import_progress_summary_errors_msg">Errores</string>
<string name="import_progress_summary_ok_msg">Archivos importados</string>
<string name="description_altitude_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 km/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>
-9
View File
@@ -411,11 +411,6 @@ Kui GPS-seade teatab ebatäpsetest andmetest (nt asukoht, kiirus, kõrgus), ei s
<string name="value_adapt_accuracy">Täpsuse kohandamine</string>
<string name="value_adapt_battery_life">Kohandage aku elueale</string>
<string name="value_always">Alati</string>
<string name="value_float_feet">%1$.2f jalga</string>
<string name="value_float_kilometer">%1$.2f km</string>
<string name="value_float_meter">%1$.2f m</string>
<string name="value_float_mile">%1$.2f miili</string>
<string name="value_float_mile_poor_gps">%1$.1f miili (nõrk GPS-signaal)</string>
<string name="value_integer_feet">%1$d jalga</string>
<string name="value_integer_feet_excellent_gps">%1$d jalga (tugev GPS-signaal)</string>
<string name="value_integer_feet_recommended">%1$d jalga (soovitatav)</string>
@@ -579,10 +574,6 @@ Kui GPS-seade teatab ebatäpsetest andmetest (nt asukoht, kiirus, kõrgus), ei s
<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>
-9
View File
@@ -412,11 +412,6 @@ GPS gailuak datu okerrak salatzen baditu (adibidez, kokapena, abiadura, kota), O
<string name="value_adapt_accuracy">Doitu zehaztasuna</string>
<string name="value_adapt_battery_life">Doitu bateriaren iraupenera</string>
<string name="value_always">Beti</string>
<string name="value_float_feet">%1$.2f oin</string>
<string name="value_float_kilometer">%1$.2f km</string>
<string name="value_float_meter">%1$.2f m</string>
<string name="value_float_mile">%1$.2f mi</string>
<string name="value_float_mile_poor_gps">%1$.1f mi (GPS ahula)</string>
<string name="value_integer_feet">%1$d ft</string>
<string name="value_integer_feet_excellent_gps">%1$d ft (GPS bikaina)</string>
<string name="value_integer_feet_recommended">%1$d oin (gomendatua)</string>
@@ -543,13 +538,9 @@ GPS gailuak datu okerrak salatzen baditu (adibidez, kokapena, abiadura, kota), O
<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>
-5
View File
@@ -339,11 +339,6 @@ limitations under the License.
<string name="value_adapt_accuracy">تعدیل به خاطر دقت</string>
<string name="value_adapt_battery_life">تعدیل به خاطر عمر باتری</string>
<string name="value_always">همیشه</string>
<string name="value_float_feet">%1$.2f فوت</string>
<string name="value_float_kilometer">%1$.2f کیلومتر</string>
<string name="value_float_meter">%1$.2f متر</string>
<string name="value_float_mile">%1$.2f مایل</string>
<string name="value_float_mile_poor_gps">%1$.1f مایل (GPS ضعیف)</string>
<string name="value_integer_feet">%1$d فوت</string>
<string name="value_integer_feet_excellent_gps">%1$d فوت (GPS عالی)</string>
<string name="value_integer_feet_recommended">%1$d فوت (توصیه می‌شود)</string>
-9
View File
@@ -422,11 +422,6 @@ jotta ne voidaan tuoda kohteeseen %1$s.</string>
<string name="value_adapt_accuracy">Säädä tarkkuutta</string>
<string name="value_adapt_battery_life">Sovita akun kestoon</string>
<string name="value_always">Aina</string>
<string name="value_float_feet">%1$.2f jalkaa</string>
<string name="value_float_kilometer">%1$.2f km</string>
<string name="value_float_meter">%1$.2f m</string>
<string name="value_float_mile">%1$.2f mailia</string>
<string name="value_float_mile_poor_gps">%1$.1f mailia (heikko GPS)</string>
<string name="value_integer_feet">%1$d jalkaa</string>
<string name="value_integer_feet_excellent_gps">%1$d jalkaa (erinomainen GPS)</string>
<string name="value_integer_feet_recommended">%1$d jalkaa (suositus)</string>
@@ -560,7 +555,6 @@ jotta ne voidaan tuoda kohteeseen %1$s.</string>
<string name="settings_default_export_uri_title">Seuraa vientihakemistoa</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 ympärysmitta (mm)</string>
<string name="stats_intervals">Välein</string>
<string name="import_thread_interrupted">Lanka keskeytetty</string>
@@ -605,12 +599,9 @@ jotta ne voidaan tuoda kohteeseen %1$s.</string>
<string name="description_altitude_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">Avattu reitti</string>
<string name="track_distance_notification">Etäisyys: %1$s</string>
<string name="track_pace_notification">Vauhti: %1$s</string>
-5
View File
@@ -290,11 +290,6 @@
<string name="value_adapt_accuracy">Adapter pour la précision</string>
<string name="value_adapt_battery_life">Optimiser pour économiser la pile</string>
<string name="value_always">Toujours</string>
<string name="value_float_feet">%1$.2f pi</string>
<string name="value_float_kilometer">%1$.2f km</string>
<string name="value_float_meter">%1$.2f m</string>
<string name="value_float_mile">%1$.2f mi</string>
<string name="value_float_mile_poor_gps">%1$.1f mi (mauvaise réception GPS)</string>
<string name="value_integer_feet">%1$d pi</string>
<string name="value_integer_feet_excellent_gps">%1$d pi(s) (réception GPS excellente)</string>
<string name="value_integer_feet_recommended">%1$d pi (recommandé)</string>
+1 -6
View File
@@ -291,12 +291,7 @@
<string name="value_adapt_accuracy">Adapter pour la précision</string>
<string name="value_adapt_battery_life">Économiser la batterie</string>
<string name="value_always">Toujours</string>
<string name="value_float_feet">%1$.2f pi</string>
<string name="value_float_kilometer">%1$.2f km</string>
<string name="value_float_meter">%1$.2f m</string>
<string name="value_float_mile">%1$.2f mile(s)</string>
<string name="value_float_mile_poor_gps">%1$.1f mile(s) (mauvaise réception GPS)</string>
<string name="value_integer_feet">%1$d pied(s)</string>
<string name="value_integer_feet">%1$d pied(s)</string>
<string name="value_integer_feet_excellent_gps">%1$d pied(s) (réception GPS excellente)</string>
<string name="value_integer_feet_recommended">%1$d pied(s) (recommandé)</string>
<string name="value_integer_kilometer">%1$d km</string>
+21 -30
View File
@@ -32,11 +32,11 @@ limitations under the License.
<string name="help_track_map_title">Comment faire apparaître une trace sur une carte ?</string>
<string name="help_track_map_content"> Utilisez la petite icône de carte.
\n a) (mises à jour en direct) Installez <a href="https://github.com/OpenTracksApp/OSMDashboard">OSMDashboard</a>. Il fournit des cartes en ligne ou hors ligne via <a href="https://www.openstreetmap.org ">OpenStreetMap</a>.
\n En fait, OSMDashboard est une application séparée qui ne peut accéder (temporairement) qu aux données des piste(s) qui vont être affichées.
\n En fait, OSMDashboard est une application séparée qui ne peut accéder (temporairement) qu aux données des trace(s) qui vont être affichées.
\n b) Via une application cartographique normale (par exemple) , <a href="http://osmand.net/">OsmAnd</a> et <a href="https://maps.me/">MAPS.ME</a> Ces applications doivent prendre en charge le format de fichier KMZ (uniquement les lieux et les horodatages). En fait, une application doit prendre en charge <i>ACTION_VIEW</i> avec MIME <i>application/vnd.google-earth.kmz</i>. </string>
<string name="help_storage_pictures_title">Comment OpenTracks stocke-t-il les photos qui ont été prises comme marqueurs ?</string>
<string name="help_storage_pictures_content">Elles sont stockées dans le stockage interne de lapplication (c est-à-dire uniquement accessible dans OpenTracks).</string>
<string name="help_track_export_title">Comment puis-je exporter/partager des pistes enregistrées ?</string>
<string name="help_track_export_title">Comment puis-je exporter/partager des traces enregistrées \?</string>
<string name="help_track_export_content"> Les traces peuvent être exportées à partir des paramètres en tant que KMZ, KML ou GPX. KMZ/KML toutes les données ; GPX seulement les emplacements. Les traces partagées via l\'icône <i>partager</i> sont partagées au format KMZ. </string>
<string name="help_data_google_play_services_title">OpenTracks nécessite-t-il/utilise-t-il le service Google Play ?</string>
<string name="help_recording_title">Comment OpenTracks enregistre-t-il les données ?</string>
@@ -136,7 +136,7 @@ Si le dispositif GPS signale des données inexactes (par exemple, la localisatio
<string name="description_average_moving_speed">Vitesse moyenne de déplacement : %1$.2f km/h (%2$.1f mi/h)</string>
<string name="description_average_pace_in_minute">Vitesse moyenne : %1$s min/km (%2$s min/mi)</string>
<string name="description_average_speed">Vitesse moyenne : %1$.2f km/h (%2$.1f mi/h)</string>
<string name="description_altitude_gain">Dénivelé : %1$d m (%2$d pi)</string>
<string name="description_altitude_gain">Dénivelé positif : %1$d m (%2$d pi)</string>
<string name="description_altitude_imperial">Altitude (pi)</string>
<string name="description_altitude_metric">Altitude (m)</string>
<string name="description_fastest_pace_in_minute">Vitesse maximale : %1$s min/km (%2$s min/mi)</string>
@@ -276,7 +276,7 @@ Si le dispositif GPS signale des données inexactes (par exemple, la localisatio
<string name="menu_stop_gps">Arrêter le GPS</string>
<string name="menu_view_photo">Afficher la photo</string>
<string name="menu_voice_frequency">Fréquence vocale</string>
<string name="menu_resume_track">Reprendre la piste</string>
<string name="menu_resume_track">Reprendre la trace</string>
<!-- OpenTracks -->
<string name="recording_service">Service enregistrement</string>
<!-- Permission -->
@@ -284,7 +284,7 @@ Si le dispositif GPS signale des données inexactes (par exemple, la localisatio
<!-- Search -->
<string name="search_global_description">Parcours et repères</string>
<string name="search_list_empty_message">Aucun parcours ni repère ne correspond à votre recherche</string>
<string name="search_list_marker_track_location">Dans la piste « %1$s ».</string>
<string name="search_list_marker_track_location">Dans la trace « %1$s ».</string>
<!-- Sensor State -->
<string name="sensor_state_battery">Niveau de la batterie</string>
<string name="sensor_state_cadence">Cadences</string>
@@ -358,7 +358,7 @@ Si le dispositif GPS signale des données inexactes (par exemple, la localisatio
<string name="stats_average_speed">Vitesse moyenne</string>
<string name="stats_altitude">Altitude</string>
<string name="stats_fastest_pace">Allure la plus rapide</string>
<string name="stats_gain">Gagner</string>
<string name="stats_gain">Dénivelé positif</string>
<string name="stats_grade">Inclinaison</string>
<string name="stats_latitude">Latitude</string>
<string name="stats_longitude">Longitude</string>
@@ -384,7 +384,7 @@ Si le dispositif GPS signale des données inexactes (par exemple, la localisatio
<string name="track_detail_chart_tab">Graphique</string>
<string name="track_detail_stats_tab">Statistiques</string>
<string name="track_detail_record_success">Enregistrement d\'un nouveau parcours en cours</string>
<string name="track_detail_resume_success">Reprenons maintenant la piste</string>
<string name="track_detail_resume_success">Reprenons maintenant la trace</string>
<!-- Track Edit -->
<string name="track_edit_activity_type">Type d\'activité (exemple : randonnée)</string>
<string name="track_edit_activity_type_hint">Type d\'activité</string>
@@ -412,11 +412,6 @@ Si le dispositif GPS signale des données inexactes (par exemple, la localisatio
<string name="value_adapt_accuracy">Adapter pour la précision</string>
<string name="value_adapt_battery_life">Économiser la batterie</string>
<string name="value_always">Toujours</string>
<string name="value_float_feet">%1$.2f pi</string>
<string name="value_float_kilometer">%1$.2f km</string>
<string name="value_float_meter">%1$.2f m</string>
<string name="value_float_mile">%1$.2f mile(s)</string>
<string name="value_float_mile_poor_gps">%1$.1f mile(s) (mauvaise réception GPS)</string>
<string name="value_integer_feet">%1$d pied(s)</string>
<string name="value_integer_feet_excellent_gps">%1$d pied(s) (réception GPS excellente)</string>
<string name="value_integer_feet_recommended">%1$d pied(s) (recommandé)</string>
@@ -541,27 +536,23 @@ Si le dispositif GPS signale des données inexactes (par exemple, la localisatio
<string name="track_speed_notification">Vitesse : %1$s</string>
<string name="track_detail_intervals_tab">Intervalles</string>
<string name="interval_list_empty_message">Il n\'y a pas encore d\'intervalle</string>
<string name="aggregated_stats_empty_message">Enregistrez votre première piste pour voir les statistiques agrégées</string>
<string name="value_float_kilometer_hour">%1$.2f km/h</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="aggregated_stats_empty_message">Enregistrez votre première trace pour voir les statistiques agrégées</string>
<string name="voice_pace_lap">Temps au tour de %1$s</string>
<string name="voice_speed_lap">Vitesse du tour de %1$s</string>
<string name="toast_message_instant_track_was_exported">Dernière piste exportée</string>
<string name="settings_default_export_uri_title">Répertoire d\'exportation des pistes</string>
<string name="toast_message_instant_track_was_exported">Dernière trace exportée</string>
<string name="settings_default_export_uri_title">Répertoire d\'exportation des traces</string>
<string name="instant_export_enabled_title">Exportation instantanée après l\'entraînement</string>
<string name="stats_intervals">Intervalles</string>
<string name="stats_loss">Perte</string>
<string name="settings_prevent_reimport_tracks_title">Empêcher la réimportation de pistes</string>
<string name="stats_loss">Dénivelé négatif</string>
<string name="settings_prevent_reimport_tracks_title">Empêcher la réimportation de traces</string>
<string name="settings_night_mode_option_night">Nuit</string>
<string name="settings_night_mode_option_day">Jour</string>
<string name="settings_night_mode_option_system">Système</string>
<string name="stats_split_interval">Intervalle partagé</string>
<string name="instant_export_enabled_summary">Exportation de la piste vers le stockage après la fin de l\'enregistrement</string>
<string name="instant_export_enabled_summary">Exportation de la trace vers le stockage après la fin de l\'enregistrement</string>
<string name="hold_to_pause">Tenir pour faire une pause</string>
<string name="hold_to_stop">Maintenir pour arrêter</string>
<string name="track_distance_notification">Distanciation : %1$s</string>
<string name="track_distance_notification">Distance : %1$s</string>
<string name="open_track_as_KMZ">Ouvert comme KMZ</string>
<string name="open_track_as_GPX">Ouvert en tant que GPX</string>
<string name="open_track_as_trackfileformat">Ouvrir comme %1$s</string>
@@ -570,7 +561,7 @@ Si le dispositif GPS signale des données inexactes (par exemple, la localisatio
<string name="settings_sensor_wheel_circumference">Circonférence de roue (millimètre)</string>
<string name="export_error_post_workout">L\'exportation du post-travail a échoué, veuillez vérifier le répertoire d\'exportation.</string>
<string name="export_all_do_it_for_all">le faire pour tous les conflits</string>
<string name="export_track_errors">Pistes non exportées</string>
<string name="export_track_errors">Traces non exportées</string>
<string name="import_unable_to_import_file">Impossible d\'importer le fichier : %1$s</string>
<string name="import_no_kml_file_found">Pas de doc.fichier kml trouvé dans KMZ</string>
<string name="import_error_info">%1$s : %2$s</string>
@@ -585,8 +576,8 @@ Si le dispositif GPS signale des données inexactes (par exemple, la localisatio
<string name="sensor_state_heart_rate_avg">Rythme cardiaque moyen</string>
<string name="sensor_state_cadence_avg">Cadence moyenne</string>
<string name="generic_click_twice_cancel">Cliquez sur retour pour annuler</string>
<string name="export_progress_summary_new_msg">Nouvelles pistes exportées</string>
<string name="export_progress_summary_skip_msg">Pistes sautées</string>
<string name="export_progress_summary_new_msg">Nouvelles traces exportées</string>
<string name="export_progress_summary_skip_msg">Traces ignorées</string>
<string name="export_progress_summary_errors_msg">Erreurs</string>
<string name="generic_overwrite">Écraser</string>
<string name="generic_show_errors">Afficher les erreurs</string>
@@ -599,7 +590,7 @@ Si le dispositif GPS signale des données inexactes (par exemple, la localisatio
<string name="import_unsupported_format">Format de fichier non pris en charge</string>
<string name="import_parser_error">Erreur de l\'analyseur : %1$s</string>
<string name="import_file_imported">Fichier %1$s importé</string>
<string name="import_prevent_reimport">Vous avez défini empêcher la réimportation de la piste</string>
<string name="import_prevent_reimport">Vous avez défini empêcher la réimportation de la trace</string>
<string name="import_progress_summary_ok_msg">Les fichiers importés</string>
<string name="import_progress_summary_exists_msg">Il existe déjà</string>
<string name="import_progress_summary_errors_msg">Erreur</string>
@@ -608,9 +599,9 @@ Si le dispositif GPS signale des données inexactes (par exemple, la localisatio
<item quantity="other">Terminé avec des erreurs %1$d</item>
</plurals>
<string name="settings_default_trackfileformat">Format de fichier d\'exportation / partage</string>
<string name="description_altitude_loss">Perte d\'altitude : %1$d m (%2$d ft)</string>
<string name="export_track_already_exists_msg">La piste %1$s existe déjà dans le répertoire de destination.</string>
<string name="export_progress_summary_overwrite_msg">Pistes écrasées</string>
<string name="description_altitude_loss">Dénivelé négatif : %1$d m (%2$d ft)</string>
<string name="export_track_already_exists_msg">La trace %1$s existe déjà dans le répertoire de destination.</string>
<string name="export_progress_summary_overwrite_msg">Traces écrasées</string>
<string name="about_third_party_data">Données de tiers</string>
<string name="track_delete_progress">%1$d/%2$d</string>
<string name="delete_service">Service de suppression</string>
+3 -12
View File
@@ -34,7 +34,7 @@ limitations under the License.
<string name="help_track_map_title">¿Como mostrar a ruta nun mapa?</string>
<string name="help_track_map_content"> Usa a icona do mapa pequeno.
\n a) (actualizacións en directo) Instala <a href="https://github.com/OpenTracksApp/OSMDashboard"> OSMDashboard </a>. Ofrece mapas en liña ou fóra de liña a través de <a href="https://www.openstreetmap.org"> OpenStreetMap </a>.
\n \u0020En realidade, OSMDashboard é unha aplicación independente que só pode acceder (temporalmente) aos datos das pistas que se amosarán.
\n En realidade, OSMDashboard é unha aplicación independente que só pode acceder (temporalmente) aos datos das pistas que se amosarán.
\n b) A través dunha aplicación de mapa normal (por exemplo, <a href="http://osmand.net/">OsmAnd</a> e <a href="https://maps.me/">MAPS.ME</a>). Estas aplicacións precisan admitir o formato de ficheiro KMZ (só localizacións e marcas de tempo). De feito, unha aplicación ten que admitir <i> ACTION_VIEW </i> con MIME <i>application/vnd.google-earth.kmz</i>. </string>
<string name="help_storage_pictures_title">¿De que xeito garda OpenTracks as fotos tomadas como marcadores?</string>
<string name="help_storage_pictures_content">Gárdanse no almacenaxe interno da app (só accesible desde OpenTracks).</string>
@@ -236,7 +236,7 @@ Se te detés ou estás en interiores, non se gardarán datos do sensor (mais ser
<string name="image_stop">Deter</string>
<string name="image_track">Traxecto</string>
<!-- Import -->
<string name="import_error">Importados: %1$d de %2$s de %3$s</string>
<string name="import_error">Importados: %1$d de %2$s desde %3$s</string>
<string name="import_no_directory">%1$s non existe</string>
<string name="import_no_file">Non hai ficheiros para importar. Conecta o teu dispositivo ao ordenador e coloca os ficheiros para importar en %1$s.</string>
<string name="import_no_file_title">Importar ficheiros</string>
@@ -422,18 +422,13 @@ Se te detés ou estás en interiores, non se gardarán datos do sensor (mais ser
<string name="unit_kilometer_per_hour">km/h</string>
<string name="unit_meter">m</string>
<string name="unit_mile">km</string>
<string name="unit_mile_per_hour">km/h</string>
<string name="unit_mile_per_hour">mph</string>
<string name="unit_minute_per_kilometer">min/km</string>
<string name="unit_minute_per_mile">min/km</string>
<!-- Value -->
<string name="value_adapt_accuracy">Adaptar para obter precisión</string>
<string name="value_adapt_battery_life">Adaptar á duración restante da batería</string>
<string name="value_always">Sempre</string>
<string name="value_float_feet">%1$.2f pés</string>
<string name="value_float_kilometer">%1$.2f km</string>
<string name="value_float_meter">%1$.2f m</string>
<string name="value_float_mile">%1$.2f km</string>
<string name="value_float_mile_poor_gps">%1$.1f km (GPS deficiente)</string>
<string name="value_integer_feet">%1$d m</string>
<string name="value_integer_feet_excellent_gps">%1$d m (GPS excelente)</string>
<string name="value_integer_feet_recommended">%1$d m (recomendado)</string>
@@ -593,12 +588,8 @@ Se te detés ou estás en interiores, non se gardarán datos do sensor (mais ser
<string name="export_progress_summary_new_msg">Exportadas as novas rutas</string>
<string name="import_no_kml_file_found">Non se atopou ficheiro doc.kml en KMZ</string>
<string name="stats_loss">Perda</string>
<string name="value_float_kilometer_hour">%1$.2f km/h</string>
<string name="value_float_mile_hour">%1$.2f mph</string>
<string name="description_altitude_loss">Perda de altitude: %1$d m (%2$d ft)</string>
<string name="export_error_post_workout">Fallou a exportación, comproba o directorio de exportación.</string>
<string name="value_pace_mile">%1$d:%2$02d min/mi</string>
<string name="value_pace_kilometer">%1$d:%2$02d min/km</string>
<string name="settings_prevent_reimport_tracks_title">Previr a reimportación de rutas</string>
<string name="export_progress_summary_overwrite_msg">Rutas sobrescritas</string>
<string name="export_progress_summary_skip_msg">Rutas omitidas</string>
-5
View File
@@ -339,11 +339,6 @@ limitations under the License.
<string name="value_adapt_accuracy">सटीकता के लिए ग्रहण करें</string>
<string name="value_adapt_battery_life">बैटरी जीवन के लिए ग्रहण करें</string>
<string name="value_always">हमेशा</string>
<string name="value_float_feet">%1$.2f फुट</string>
<string name="value_float_kilometer">%1$.2f किमी</string>
<string name="value_float_meter">%1$.2f मी</string>
<string name="value_float_mile">%1$.2f मी</string>
<string name="value_float_mile_poor_gps">%1$.1f मी (खराब GPS)</string>
<string name="value_integer_feet">%1$d फुट</string>
<string name="value_integer_feet_excellent_gps">%1$d फुट (उत्कृष्ट GPS)</string>
<string name="value_integer_feet_recommended">%1$d फ़ुट (अनुशंसित)</string>
-5
View File
@@ -340,11 +340,6 @@ limitations under the License.
<string name="value_adapt_accuracy">Prilagodba radi točnosti</string>
<string name="value_adapt_battery_life">Prilagodi vijeku trajanja baterije</string>
<string name="value_always">Uvijek</string>
<string name="value_float_feet">%1$.2f ft</string>
<string name="value_float_kilometer">%1$.2f km</string>
<string name="value_float_meter">%1$.2f m</string>
<string name="value_float_mile">%1$.2f mi</string>
<string name="value_float_mile_poor_gps">%1$.1f mi (loš GPS)</string>
<string name="value_integer_feet">%1$d ft</string>
<string name="value_integer_feet_excellent_gps">%1$d ft (izvrstan GPS)</string>
<string name="value_integer_feet_recommended">%1$d ft (preporučeno)</string>
-9
View File
@@ -341,11 +341,6 @@ limitations under the License.
<string name="value_adapt_accuracy">Beállítás pontossághoz</string>
<string name="value_adapt_battery_life">Akkumulátor élettartama szerint</string>
<string name="value_always">Mindig</string>
<string name="value_float_feet">%1$.2f láb</string>
<string name="value_float_kilometer">%1$.2f km</string>
<string name="value_float_meter">%1$.2f m</string>
<string name="value_float_mile">%1$.2f mérföld</string>
<string name="value_float_mile_poor_gps">%1$.1f mérföld (gyenge GPS-jel)</string>
<string name="value_integer_feet">%1$d láb</string>
<string name="value_integer_feet_excellent_gps">%1$d láb (kiváló GPS-jel)</string>
<string name="value_integer_feet_recommended">%1$d láb (ajánlott)</string>
@@ -499,15 +494,12 @@ limitations under the License.
<string name="track_detail_record_success">Most egy új szám felvétele</string>
<string name="track_detail_resume_success">Most folytatódik a pálya</string>
<string name="interval_list_empty_message">Még nincs intervallum</string>
<string name="value_float_mile_hour">%1$.2f mph</string>
<string name="value_pace_mile">%1$d:%2$02d min/mi</string>
<string name="settings_default_export_uri_title">Pálya export könyvtár</string>
<string name="instant_export_enabled_summary">Sáv exportálása a tárolóba a felvétel befejezése után</string>
<string name="toast_message_instant_track_was_exported">Utolsó exportált zeneszám</string>
<string name="hold_to_pause">Tartsa a szünethez</string>
<string name="hold_to_stop">Tartsa lenyomva a leállításhoz</string>
<string name="track_distance_notification">Távolság: %1$s</string>
<string name="value_pace_kilometer">%1$d:%2$02d min/km</string>
<string name="instant_export_enabled_title">Azonnali edzés utáni export</string>
<string name="track_pace_notification">Tempó: %1$s</string>
<string name="export_with_photos">fotókkal</string>
@@ -519,7 +511,6 @@ limitations under the License.
<string name="import_unsupported_format">Nem támogatott fájlformátum</string>
<string name="description_default_speed_or_pace">Tevékenységtípus szerint</string>
<string name="export_without_photos">fotók nélkül</string>
<string name="value_float_kilometer_hour">%1$.2f km/h</string>
<string name="voice_speed_lap">%1$s körsebessége</string>
<string name="help_gps_troubleshooting_content"> Mit tehetsz:
\n a) ellenőrizze, hogy a GPS engedélyezve van-e,
+21 -12
View File
@@ -151,8 +151,8 @@
<string name="image_track">Lacak</string>
<string name="import_error">Mengimpor %1$d dari %2$s dari %3$s</string>
<string name="import_no_directory">%1$s tidak ada.</string>
<string name="import_no_file">Tidak ada file untuk diimpor. Hubungkan perangkat ke komputer Anda dan tempatkan file yang akan diimpor di %1$s.</string>
<string name="import_no_file_title">Impor file</string>
<string name="import_no_file">Tidak ada berkas untuk diimpor. Hubungkan perangkat ke komputer Anda dan tempatkan file yang akan diimpor di %1$s.</string>
<string name="import_no_file_title">Impor berkas</string>
<string name="import_progress_message">Mengimpor dari %1$s…</string>
<string name="import_selection_option">Semua %1$s</string>
<string name="import_selection_title">Impor dari penyimpanan external</string>
@@ -160,10 +160,10 @@
<string name="marker_add_error">Tidak dapat memasukkan penanda. Tidak ada sinyal GPS. Coba lagi.</string>
<string name="marker_add_success">Penanda disisipkan.</string>
<string name="marker_add_canceled">Batal, penanda tidak ditambahkan</string>
<string name="marker_delete_all_confirm_message">Semua penanda untuk lintasan ini akan dihapus permanen dari perangkat</string>
<string name="marker_delete_one_confirm_message">Penanda akan dihapus permanen dari perangkat</string>
<string name="marker_delete_all_confirm_message">Semua penanda untuk lintasan ini akan dihapus secara permanen dari perangkat</string>
<string name="marker_delete_one_confirm_message">Penanda akan dihapus secara permanen dari perangkat</string>
<string name="marker_delete_one_confirm_title">Hapus penanda?</string>
<string name="marker_delete_multiple_confirm_message">Penanda yang dipilih akan dihapus permanen dari perangkat</string>
<string name="marker_delete_multiple_confirm_message">Penanda yang dipilih akan dihapus secara permanen dari perangkat</string>
<string name="marker_label_end">%1$s (Akhir)</string>
<string name="marker_label_start">%1$s (Awal)</string>
<string name="marker_name_format">Penanda %1$d</string>
@@ -229,7 +229,7 @@
<string name="settings_recording_track_name_number_option">Angka</string>
<string name="settings_recording_track_name_title">Nama trek default</string>
<string name="settings_reset">Setel ulang</string>
<string name="settings_reset_confirm_message">Semua setelan akan diubah ke nilai default.</string>
<string name="settings_reset_confirm_message">Semua setelan akan diubah ke nilai default. Tiada lintasan yang akan dihapus dari perangkat.</string>
<string name="settings_reset_confirm_title">Setel ulang sekarang?</string>
<string name="settings_reset_done">Semua setelan telah dikembalikan ke nilai default</string>
<string name="settings_sensor">Sensor</string>
@@ -291,11 +291,6 @@
<string name="value_adapt_accuracy">Sesuaikan dengan akurasi</string>
<string name="value_adapt_battery_life">Sesuaikan untuk masa pakai baterai</string>
<string name="value_always">Selalu</string>
<string name="value_float_feet">%1$.2f kaki</string>
<string name="value_float_kilometer">%1$.2f km</string>
<string name="value_float_meter">%1$.2f m</string>
<string name="value_float_mile">%1$.2f mil</string>
<string name="value_float_mile_poor_gps">%1$.1f mil (GPS buruk)</string>
<string name="value_integer_feet">%1$d kaki</string>
<string name="value_integer_feet_excellent_gps">%1$d kaki (GPS sangat baik)</string>
<string name="value_integer_feet_recommended">%1$d kaki (direkomendasikan)</string>
@@ -411,4 +406,18 @@
<string name="waypoint_type_waterfall">air terjun</string>
<string name="waypoint_type_water_fountain">air mancur</string>
<string name="waypoint_type_waypoint">titik jalan</string>
</resources>
<string name="settings_night_mode_option_night">Malam</string>
<string name="open_track_as_KMZ">Buka sebagai KMZ</string>
<string name="import_error_list_dialog_title">Berkas tidak diimpor</string>
<string name="import_file_imported">Berkas %1$s telah diimpor</string>
<string name="settings_night_mode_option_day">Siang</string>
<string name="help_support_title">Bagaimana mendukung OpenTracks\?</string>
<string name="import_progress_summary_errors_msg">Kesalahan</string>
<string name="open_track_as_GPX">Buka sebagai GPX</string>
<string name="settings_error_initial_values_restored">Maaf, kesalahan terjadi. Semua setelan telah dikembalikan ke nilai default</string>
<string name="track_speed_notification">Kecepatan: %1$s</string>
<string name="import_unable_to_import_file">Tidak bisa mengimpor berkas: %1$s</string>
<string name="open_track_as_trackfileformat">Buka sebagai %1$s</string>
<string name="track_distance_notification">Jarak: %1$s</string>
<string name="settings_recording_show_on_lockscreen_while_recording_summary">Selama perekaman,</string>
</resources>
-9
View File
@@ -385,11 +385,6 @@ Infatti, un\'applicazione deve supportare <i>ACTION_VIEW</i> con MIME <i>applica
<string name="value_adapt_accuracy">Adatta per la precisione</string>
<string name="value_adapt_battery_life">Adatta per la massima durata della batteria</string>
<string name="value_always">Sempre</string>
<string name="value_float_feet">%1$.2f ft</string>
<string name="value_float_kilometer">%1$.2f km</string>
<string name="value_float_meter">%1$.2f m</string>
<string name="value_float_mile">%1$.2f mi</string>
<string name="value_float_mile_poor_gps">%1$.1f mi (GPS debole)</string>
<string name="value_integer_feet">%1$d pd</string>
<string name="value_integer_feet_excellent_gps">%1$d pd (GPS forte)</string>
<string name="value_integer_feet_recommended">%1$d pd (consigliato)</string>
@@ -605,16 +600,12 @@ Infatti, un\'applicazione deve supportare <i>ACTION_VIEW</i> con MIME <i>applica
<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">Discesa</string>
<string name="settings_sensor_wheel_circumference">Circonferenza della ruota (mm)</string>
<string name="description_altitude_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>
<string name="track_distance_notification">Distanza: %1$s</string>
<string name="track_speed_notification">Velocità: %1$s</string>
<string name="track_pace_notification">Ritmo: %1$s</string>
-5
View File
@@ -291,11 +291,6 @@
<string name="value_adapt_accuracy">התאם למען הדיוק</string>
<string name="value_adapt_battery_life">התאם לחיי הסוללה</string>
<string name="value_always">תמיד</string>
<string name="value_float_feet">%1$.2f רגל</string>
<string name="value_float_kilometer">%1$.2f ק\"מ</string>
<string name="value_float_meter">%1$.2f מ\'</string>
<string name="value_float_mile">%1$.2f מייל</string>
<string name="value_float_mile_poor_gps">%1$.1f מייל (GPS חלש)</string>
<string name="value_integer_feet">%1$d רגל</string>
<string name="value_integer_feet_excellent_gps">%1$d רגל (GPS מעולה)</string>
<string name="value_integer_feet_recommended">%1$d רגל (מומלץ)</string>
-5
View File
@@ -341,11 +341,6 @@ limitations under the License.
<string name="value_adapt_accuracy">精度を重視</string>
<string name="value_adapt_battery_life">電池残量を重視</string>
<string name="value_always">常に使用する</string>
<string name="value_float_feet">%1$.2fフィート</string>
<string name="value_float_kilometer">%1$.2fkm</string>
<string name="value_float_meter">%1$.2fm</string>
<string name="value_float_mile">%1$.2fmi</string>
<string name="value_float_mile_poor_gps">%1$.1fmi(GPS信号: 弱)</string>
<string name="value_integer_feet">%1$dft</string>
<string name="value_integer_feet_excellent_gps">%1$dft(GPS信号: 強)</string>
<string name="value_integer_feet_recommended">%1$dft(推奨)</string>
-5
View File
@@ -340,11 +340,6 @@ limitations under the License.
<string name="value_adapt_accuracy">정확도를 위해 조정</string>
<string name="value_adapt_battery_life">배터리 수명을 위해 조정</string>
<string name="value_always">항상</string>
<string name="value_float_feet">%1$.2f피트</string>
<string name="value_float_kilometer">%1$.2fkm</string>
<string name="value_float_meter">%1$.2fm</string>
<string name="value_float_mile">%1$.2f마일</string>
<string name="value_float_mile_poor_gps">%1$.1f마일(GPS 수신 불량)</string>
<string name="value_integer_feet">%1$d피트</string>
<string name="value_integer_feet_excellent_gps">%1$d피트(GPS 수신 원활)</string>
<string name="value_integer_feet_recommended">%1$d피트(권장)</string>
-5
View File
@@ -342,11 +342,6 @@ limitations under the License.
<string name="value_adapt_accuracy">Pritaikyti siekiant tikslumo</string>
<string name="value_adapt_battery_life">Pritaik. taus. akum.</string>
<string name="value_always">Visada</string>
<string name="value_float_feet">%1$.2f pėd.</string>
<string name="value_float_kilometer">%1$.2f km</string>
<string name="value_float_meter">%1$.2f m</string>
<string name="value_float_mile">%1$.2f myl.</string>
<string name="value_float_mile_poor_gps">%1$.1f myl. (prastas GPS)</string>
<string name="value_integer_feet">%1$d pėd.</string>
<string name="value_integer_feet_excellent_gps">%1$d pėd. (puikus GPS)</string>
<string name="value_integer_feet_recommended">%1$d pėd. (rekomenduojama)</string>
-5
View File
@@ -340,11 +340,6 @@ limitations under the License.
<string name="value_adapt_accuracy">Pielāgot precizitātei</string>
<string name="value_adapt_battery_life">Pielāgot akumulatora darbības ilgumam</string>
<string name="value_always">Vienmēr</string>
<string name="value_float_feet">%1$.2f pēda(-as)</string>
<string name="value_float_kilometer">%1$.2f km</string>
<string name="value_float_meter">%1$.2f m</string>
<string name="value_float_mile">%1$.2f jūdze(-es)</string>
<string name="value_float_mile_poor_gps">%1$.1f jūdze(-es) (vājš GPS)</string>
<string name="value_integer_feet">%1$d pēda(-as)</string>
<string name="value_integer_feet_excellent_gps">%1$d pēda(-as) (izcilas kvalitātes GPS)</string>
<string name="value_integer_feet_recommended">%1$d pēdas (ieteicams)</string>
-5
View File
@@ -291,11 +291,6 @@
<string name="value_adapt_accuracy">Adaptaţi pentru acurateţe</string>
<string name="value_adapt_battery_life">Economisiţi bateria</string>
<string name="value_always">Întotdeauna</string>
<string name="value_float_feet">%1$.2f ft</string>
<string name="value_float_kilometer">%1$.2f km</string>
<string name="value_float_meter">%1$.2f m</string>
<string name="value_float_mile">%1$.2f mi</string>
<string name="value_float_mile_poor_gps">%1$.1f mi (semnal GPS slab)</string>
<string name="value_integer_feet">%1$d ft</string>
<string name="value_integer_feet_excellent_gps">%1$d ft (semnal GPS excelent)</string>
<string name="value_integer_feet_recommended">%1$d ft (recomandat)</string>
-5
View File
@@ -341,11 +341,6 @@ limitations under the License.
<string name="value_adapt_accuracy">Sesuaikan untuk ketepatan</string>
<string name="value_adapt_battery_life">Sesuaikan untuk hayat bateri</string>
<string name="value_always">Sentiasa</string>
<string name="value_float_feet">%1$.2f kaki</string>
<string name="value_float_kilometer">%1$.2f km</string>
<string name="value_float_meter">%1$.2f m</string>
<string name="value_float_mile">%1$.2f batu</string>
<string name="value_float_mile_poor_gps">%1$.1f batu (GPS lemah)</string>
<string name="value_integer_feet">%1$d kaki</string>
<string name="value_integer_feet_excellent_gps">%1$d kaki (GPS bagus)</string>
<string name="value_integer_feet_recommended">%1$d kaki (disyorkan)</string>
+2 -11
View File
@@ -335,18 +335,13 @@ limitations under the License.
<string name="unit_kilometer_per_hour">km/t</string>
<string name="unit_meter">m</string>
<string name="unit_mile">mi</string>
<string name="unit_mile_per_hour">mi/h</string>
<string name="unit_mile_per_hour">mph</string>
<string name="unit_minute_per_kilometer">min/km</string>
<string name="unit_minute_per_mile">min/mi</string>
<!-- Value -->
<string name="value_adapt_accuracy">Tilpass for nøyaktighet</string>
<string name="value_adapt_battery_life">Tilpass for batterilevetid</string>
<string name="value_always">Alltid</string>
<string name="value_float_feet">%1$.2f fot</string>
<string name="value_float_kilometer">%1$.2f km</string>
<string name="value_float_meter">%1$.2f m</string>
<string name="value_float_mile">%1$.2f mi</string>
<string name="value_float_mile_poor_gps">%1$.1f mi (dårlig GPS)</string>
<string name="value_integer_feet">%1$d fot</string>
<string name="value_integer_feet_excellent_gps">%1$d fot (utmerket GPS)</string>
<string name="value_integer_feet_recommended">%1$d fot (anbefales)</string>
@@ -398,7 +393,7 @@ limitations under the License.
<string name="voice_speed_miles_per_hour_other">%1$.1f engelske mil i timen</string>
<string name="voice_speed_miles_per_hour_two">2 engelske mil i timen</string>
<string name="voice_speed_miles_per_hour_zero">0 engelske mil i timen</string>
<string name="voice_template">Spor %1$s om %2$s ved %3$s</string>
<string name="voice_template">OpenTracks %1$s på %2$s med %3$s</string>
<string name="voice_total_distance_kilometers_few">total distanse %1$.2f kilometer</string>
<string name="voice_total_distance_kilometers_many">total distanse %1$.2f kilometer</string>
<string name="voice_total_distance_kilometers_one">total distanse 1 kilometer</string>
@@ -579,10 +574,6 @@ limitations under the License.
\n e) kjøpe 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) gjøre hva du tror hjelper OpenTracks. </string>
<string name="value_float_kilometer_hour">%1$.2f km/t</string>
<string name="value_pace_kilometer">%1$d:%2$02d min/km</string>
<string name="value_float_mile_hour">%1$.2f mph</string>
<string name="value_pace_mile">%1$d:%2$02d min/mi</string>
<string name="description_altitude_loss">Total nedstigning: %1$d m (%2$d ft)</string>
<string name="stats_loss">Total nedstigning</string>
<string name="import_prevent_reimport">Du har stilt inn å forhindre at spor importeres om igjen</string>
-9
View File
@@ -411,11 +411,6 @@ Als het GPS-apparaat onnauwkeurige gegevens rapporteert (bijv. locatie, snelheid
<string name="value_adapt_accuracy">Aanpassen voor nauwkeurigheid</string>
<string name="value_adapt_battery_life">Aanpassen voor resterende accutijd</string>
<string name="value_always">Altijd</string>
<string name="value_float_feet">%1$.2f ft</string>
<string name="value_float_kilometer">%1$.2f km</string>
<string name="value_float_meter">%1$.2f m</string>
<string name="value_float_mile">%1$.2f mi</string>
<string name="value_float_mile_poor_gps">%1$.1f mi (slecht GPS-signaal)</string>
<string name="value_integer_feet">%1$d ft</string>
<string name="value_integer_feet_excellent_gps">%1$d ft (uitstekend GPS-signaal)</string>
<string name="value_integer_feet_recommended">%1$d ft (aanbevolen)</string>
@@ -553,7 +548,6 @@ Als het GPS-apparaat onnauwkeurige gegevens rapporteert (bijv. locatie, snelheid
<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>
@@ -574,12 +568,9 @@ Als het GPS-apparaat onnauwkeurige gegevens rapporteert (bijv. locatie, snelheid
<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>
-9
View File
@@ -378,11 +378,6 @@ limitations under the License.
<string name="value_adapt_accuracy">Dostosuj pod kątem dokładności</string>
<string name="value_adapt_battery_life">Dostosuj pod kątem żywotności baterii</string>
<string name="value_always">Zawsze</string>
<string name="value_float_feet">%1$.2f ft</string>
<string name="value_float_kilometer">%1$.2f km</string>
<string name="value_float_meter">%1$.2f m</string>
<string name="value_float_mile">%1$.2f mi</string>
<string name="value_float_mile_poor_gps">%1$.1f mi (słaby sygnał GPS)</string>
<string name="value_integer_feet">%1$d ft</string>
<string name="value_integer_feet_excellent_gps">%1$d ft (doskonały sygnał GPS)</string>
<string name="value_integer_feet_recommended">%1$d ft (zalecane)</string>
@@ -609,10 +604,6 @@ limitations under the License.
<string name="track_pace_notification">Prędkość: %1$s</string>
<string name="track_speed_notification">Szybkość: %1$s</string>
<string name="track_distance_notification">Odległość: %1$s</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="about_third_party_data">Dane osób trzecich</string>
<string name="delete_service">Usługa usuwania</string>
<string name="track_delete_progress">%1$d/%2$d</string>
-9
View File
@@ -294,11 +294,6 @@
<string name="value_adapt_accuracy">Adaptar para fins de precisão</string>
<string name="value_adapt_battery_life">Adaptar à vida da bateria</string>
<string name="value_always">Sempre</string>
<string name="value_float_feet">%1$.2f pés</string>
<string name="value_float_kilometer">%1$.2f km</string>
<string name="value_float_meter">%1$.2f m</string>
<string name="value_float_mile">%1$.2f milhas</string>
<string name="value_float_mile_poor_gps">%1$.1f milhas (GPS com sinal baixo)</string>
<string name="value_integer_feet">%1$d pés</string>
<string name="value_integer_feet_excellent_gps">%1$d pés (GPS excelente)</string>
<string name="value_integer_feet_recommended">%1$d pés (recomendado)</string>
@@ -530,16 +525,12 @@
<string name="voice_pace_lap">Tempo da volta de %1$s</string>
<string name="share_image_subject">Eu gostaria de compartilhar uma imagem com você</string>
<string name="instant_export_enabled_summary">Exportar trilha para armazenamento depois que a gravação for concluída</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="import_prevent_reimport">Você definiu evitar reimportar a trilha</string>
<string name="stats_loss">Perda</string>
<string name="export_error_post_workout">A exportação pós-treino falhou, verifique o diretório de exportação.</string>
<string name="value_float_kilometer_hour">%1$.2f km/h</string>
<string name="description_altitude_loss">Perda de elevação: %1$d m (%2$d ft)</string>
<string name="export_progress_summary_new_msg">Novas trilhas exportadas</string>
<string name="import_no_kml_file_found">Nenhum arquivo doc.kml encontrado no KMZ</string>
<string name="value_pace_mile">%1$d:%2$02d min/mi</string>
<string name="settings_prevent_reimport_tracks_title">Impedir a reimportação de trilhas</string>
<string name="export_progress_summary_overwrite_msg">Trilhas sobrescritas</string>
<string name="export_progress_summary_skip_msg">Trilhas puladas</string>
-5
View File
@@ -290,11 +290,6 @@
<string name="value_adapt_accuracy">Adaptar para precisão</string>
<string name="value_adapt_battery_life">Adaptar para a vida da bateria</string>
<string name="value_always">Sempre</string>
<string name="value_float_feet">%1$.2f pés</string>
<string name="value_float_kilometer">%1$.2f km</string>
<string name="value_float_meter">%1$.2f m</string>
<string name="value_float_mile">%1$.2f mi</string>
<string name="value_float_mile_poor_gps">%1$.1f mi (GPS fraco)</string>
<string name="value_integer_feet">%1$d pés</string>
<string name="value_integer_feet_excellent_gps">%1$d pés (GPS excelente)</string>
<string name="value_integer_feet_recommended">%1$d pés (recomendado)</string>
-5
View File
@@ -342,11 +342,6 @@ limitations under the License.
<string name="value_adapt_accuracy">Adaptar para fins de precisão</string>
<string name="value_adapt_battery_life">Adaptar à vida da bateria</string>
<string name="value_always">Sempre</string>
<string name="value_float_feet">%1$.2f pés</string>
<string name="value_float_kilometer">%1$.2f km</string>
<string name="value_float_meter">%1$.2f m</string>
<string name="value_float_mile">%1$.2f milhas</string>
<string name="value_float_mile_poor_gps">%1$.1f milhas (GPS com sinal baixo)</string>
<string name="value_integer_feet">%1$d pés</string>
<string name="value_integer_feet_excellent_gps">%1$d pés (GPS excelente)</string>
<string name="value_integer_feet_recommended">%1$d pés (recomendado)</string>
-5
View File
@@ -340,11 +340,6 @@ limitations under the License.
<string name="value_adapt_accuracy">Adaptaţi pentru acurateţe</string>
<string name="value_adapt_battery_life">Economisiţi bateria</string>
<string name="value_always">Întotdeauna</string>
<string name="value_float_feet">%1$.2f ft</string>
<string name="value_float_kilometer">%1$.2f km</string>
<string name="value_float_meter">%1$.2f m</string>
<string name="value_float_mile">%1$.2f mi</string>
<string name="value_float_mile_poor_gps">%1$.1f mi (semnal GPS slab)</string>
<string name="value_integer_feet">%1$d ft</string>
<string name="value_integer_feet_excellent_gps">%1$d ft (semnal GPS excelent)</string>
<string name="value_integer_feet_recommended">%1$d ft (recomandat)</string>
-9
View File
@@ -340,11 +340,6 @@ limitations under the License.
<string name="value_adapt_accuracy">Макс. точность</string>
<string name="value_adapt_battery_life">Мин. расход батареи</string>
<string name="value_always">Всегда</string>
<string name="value_float_feet">%1$.2f фт</string>
<string name="value_float_kilometer">%1$.2f км</string>
<string name="value_float_meter">%1$.2f м</string>
<string name="value_float_mile">%1$.2f мил</string>
<string name="value_float_mile_poor_gps">%1$.1f мил. (слабый сигнал)</string>
<string name="value_integer_feet">%1$d фт</string>
<string name="value_integer_feet_excellent_gps">%1$d фт (отличный сигнал)</string>
<string name="value_integer_feet_recommended">%1$d фт (рекомендуется)</string>
@@ -569,10 +564,6 @@ limitations under the License.
<string name="track_pace_notification">Темп: %1$s</string>
<string name="track_speed_notification">Скорость: %1$s</string>
<string name="track_distance_notification">Расстояние: %1$s</string>
<string name="value_pace_kilometer">%1$d:%2$02d мин/км</string>
<string name="value_pace_mile">%1$d:%2$02d мин/миль</string>
<string name="value_float_mile_hour">%1$.2f mph</string>
<string name="value_float_kilometer_hour">%1$.2f км/ч</string>
<string name="interval_list_empty_message">Интервалов пока нет</string>
<string name="aggregated_stats_empty_message">Запишите свой первый трек, чтобы увидеть агрегированную статистику</string>
<string name="track_detail_resume_success">Теперь возобновляем путь</string>
-5
View File
@@ -339,11 +339,6 @@ limitations under the License.
<string name="value_adapt_accuracy">Prispôsobiť na presnosť</string>
<string name="value_adapt_battery_life">Prispôsobiť na výdrž batérie</string>
<string name="value_always">Vždy</string>
<string name="value_float_feet">%1$.2f stopy</string>
<string name="value_float_kilometer">%1$.2f km</string>
<string name="value_float_meter">%1$.2f m</string>
<string name="value_float_mile">%1$.2f mi.</string>
<string name="value_float_mile_poor_gps">%1$.1f mi. (slabý signál GPS)</string>
<string name="value_integer_feet">%1$d st.</string>
<string name="value_integer_feet_excellent_gps">%1$d st. (výborný signál GPS)</string>
<string name="value_integer_feet_recommended">%1$d st. (odporúčané)</string>
-5
View File
@@ -341,11 +341,6 @@ limitations under the License.
<string name="value_adapt_accuracy">Prilagodi za natančnost</string>
<string name="value_adapt_battery_life">Prilagodi za življenjsko dobo baterije</string>
<string name="value_always">Vedno</string>
<string name="value_float_feet">%1$.2f čevljev</string>
<string name="value_float_kilometer">%1$.2f km</string>
<string name="value_float_meter">%1$.2f m</string>
<string name="value_float_mile">%1$.2f mi</string>
<string name="value_float_mile_poor_gps">%1$.1f mi (slab signal GPS)</string>
<string name="value_integer_feet">%1$d čevljev</string>
<string name="value_integer_feet_excellent_gps">%1$d čevljev (odličen signal GPS)</string>
<string name="value_integer_feet_recommended">%1$d čevljev (priporočeno)</string>
-5
View File
@@ -339,11 +339,6 @@ limitations under the License.
<string name="value_adapt_accuracy">Прилагоди за прецизност</string>
<string name="value_adapt_battery_life">Прилагоди трајању батерије</string>
<string name="value_always">Увек</string>
<string name="value_float_feet">%1$.2f ст</string>
<string name="value_float_kilometer">%1$.2f км</string>
<string name="value_float_meter">%1$.2f м</string>
<string name="value_float_mile">%1$.2f ми</string>
<string name="value_float_mile_poor_gps">%1$.1f ми (слаб GPS)</string>
<string name="value_integer_feet">%1$d ст</string>
<string name="value_integer_feet_excellent_gps">%1$d ст (одличан GPS)</string>
<string name="value_integer_feet_recommended">%1$d ст (препоручено)</string>
-5
View File
@@ -339,11 +339,6 @@ limitations under the License.
<string name="value_adapt_accuracy">Anpassa för noggrannhet</string>
<string name="value_adapt_battery_life">Anpassa för batterilivslängd</string>
<string name="value_always">Alltid</string>
<string name="value_float_feet">%1$.2f fot</string>
<string name="value_float_kilometer">%1$.2f km</string>
<string name="value_float_meter">%1$.2f m</string>
<string name="value_float_mile">%1$.2f mile</string>
<string name="value_float_mile_poor_gps">%1$.1f mile (dålig GPS-mottagning)</string>
<string name="value_integer_feet">%1$d fot</string>
<string name="value_integer_feet_excellent_gps">%1$d fot (utmärkt GPS-mottagning)</string>
<string name="value_integer_feet_recommended">%1$d fot (rekommenderas)</string>
-5
View File
@@ -339,11 +339,6 @@ limitations under the License.
<string name="value_adapt_accuracy">ปรับความแม่นยำ</string>
<string name="value_adapt_battery_life">ปรับเพื่อแบตเตอรี่</string>
<string name="value_always">เปิดใช้งานเสมอ</string>
<string name="value_float_feet">%1$.2f ฟุต</string>
<string name="value_float_kilometer">%1$.2f กม.</string>
<string name="value_float_meter">%1$.2f ม.</string>
<string name="value_float_mile">%1$.2f ไมล์</string>
<string name="value_float_mile_poor_gps">%1$.1f ไมล์ (สัญญาณ GPS ไม่ดี)</string>
<string name="value_integer_feet">%1$d ฟุต</string>
<string name="value_integer_feet_excellent_gps">%1$d ฟุต (สัญญาณ GPS ดี)</string>
<string name="value_integer_feet_recommended">%1$d ฟุต (แนะนำ)</string>
-5
View File
@@ -339,11 +339,6 @@ limitations under the License.
<string name="value_adapt_accuracy">Iangkop para sa katumpakan</string>
<string name="value_adapt_battery_life">Iangkop para sa antas ng baterya</string>
<string name="value_always">Palagi</string>
<string name="value_float_feet">%1$.2f ft</string>
<string name="value_float_kilometer">%1$.2f km</string>
<string name="value_float_meter">%1$.2f m</string>
<string name="value_float_mile">%1$.2f mi</string>
<string name="value_float_mile_poor_gps">%1$.1f mi (mahina ang GPS)</string>
<string name="value_integer_feet">%1$d ft</string>
<string name="value_integer_feet_excellent_gps">%1$d ft (malinaw na GPS)</string>
<string name="value_integer_feet_recommended">%1$d ft (inirerekomenda)</string>
-5
View File
@@ -340,11 +340,6 @@ limitations under the License.
<string name="value_adapt_accuracy">Doğruluk için uyarla</string>
<string name="value_adapt_battery_life">Pil ömrü için uyarla</string>
<string name="value_always">Her zaman</string>
<string name="value_float_feet">%1$.2f ft</string>
<string name="value_float_kilometer">%1$.2f km</string>
<string name="value_float_meter">%1$.2f m</string>
<string name="value_float_mile">%1$.2f mil</string>
<string name="value_float_mile_poor_gps">%1$.1f mil (zayıf GPS)</string>
<string name="value_integer_feet">%1$d ft</string>
<string name="value_integer_feet_excellent_gps">%1$d ft (mükemmel GPS)</string>
<string name="value_integer_feet_recommended">%1$d ft (önerilir)</string>
-9
View File
@@ -341,11 +341,6 @@ limitations under the License.
<string name="value_adapt_accuracy">Адаптувати для точності</string>
<string name="value_adapt_battery_life">Адаптувати до часу роботи батареї</string>
<string name="value_always">Завжди</string>
<string name="value_float_feet">%1$.2f фт</string>
<string name="value_float_kilometer">%1$.2f км</string>
<string name="value_float_meter">%1$.2f м</string>
<string name="value_float_mile">Миль: %1$.2f</string>
<string name="value_float_mile_poor_gps">Миль: %1$.1f (слабкий сигнал GPS)</string>
<string name="value_integer_feet">%1$d фут</string>
<string name="value_integer_feet_excellent_gps">%1$d фут. (відмінний сигнал GPS)</string>
<string name="value_integer_feet_recommended">%1$d фут. (рекомендовано)</string>
@@ -474,14 +469,11 @@ limitations under the License.
<string name="stats_split_interval">Розділити на</string>
<string name="track_recording_notification_accuracy">Точність місцеположення: %1$s</string>
<string name="track_detail_intervals_tab">Інтервали</string>
<string name="value_pace_mile">%1$d:%2$02d хв/милі</string>
<string name="voice_speed_lap">Швидкість кола %1$s</string>
<string name="voice_pace_lap">Час кола %1$s</string>
<string name="track_detail_record_success">Зараз записується новий трек</string>
<string name="track_detail_resume_success">Відновлюємо запис треку</string>
<string name="interval_list_empty_message">Ще немає інтервалу</string>
<string name="value_float_kilometer_hour">%1$.2f км/г</string>
<string name="value_float_mile_hour">%1$.2f миль/г</string>
<string name="share_image_body">Думаю, вам може бути цікаво це зображення.</string>
<string name="hold_to_pause">Затисніть</string>
<string name="instant_export_enabled_summary">Експорт треку в сховище після завершення запису</string>
@@ -582,7 +574,6 @@ limitations under the License.
<string name="settings_recording_show_on_lockscreen_while_recording_summary">Під час запису показувати статистику без розблокування пристрою.</string>
<string name="stats_loss">Втрачено</string>
<string name="stats_intervals">Інтервали</string>
<string name="value_pace_kilometer">%1$d:%2$02d хв/км</string>
<string name="share_image_subject">Я хотів би поділитися з вами зображенням</string>
<string name="toast_message_instant_track_was_exported">Останній трек експортовано</string>
<string name="settings_default_export_uri_title">Каталог експорту треків</string>
-5
View File
@@ -341,11 +341,6 @@ limitations under the License.
<string name="value_adapt_accuracy">Thích ứng với độ chính xác</string>
<string name="value_adapt_battery_life">Thích ứng với tuổi thọ pin</string>
<string name="value_always">Luôn luôn</string>
<string name="value_float_feet">%1$.2f ft</string>
<string name="value_float_kilometer">%1$.2f km</string>
<string name="value_float_meter">%1$.2f m</string>
<string name="value_float_mile">%1$.2f dặm</string>
<string name="value_float_mile_poor_gps">%1$.1f dặm (tín hiệu GPS xấu)</string>
<string name="value_integer_feet">%1$d ft</string>
<string name="value_integer_feet_excellent_gps">%1$d ft (tín hiệu GPS tốt)</string>
<string name="value_integer_feet_recommended">%1$d ft (được đề nghị)</string>
-5
View File
@@ -292,11 +292,6 @@
<string name="value_adapt_accuracy">根據精度調整</string>
<string name="value_adapt_battery_life">根據電池壽命調整</string>
<string name="value_always">一律使用</string>
<string name="value_float_feet">%1$.2f 英尺</string>
<string name="value_float_kilometer">%1$.2f 公里</string>
<string name="value_float_meter">%1$.2f 公尺</string>
<string name="value_float_mile">%1$.2f 英里</string>
<string name="value_float_mile_poor_gps">%1$.1f 英里 (GPS 訊號差)</string>
<string name="value_integer_feet">%1$d 英尺</string>
<string name="value_integer_feet_excellent_gps">%1$d 英尺 (GPS 訊號佳)</string>
<string name="value_integer_feet_recommended">%1$d 英尺 (建議值)</string>
-5
View File
@@ -292,11 +292,6 @@
<string name="value_adapt_accuracy">根據精度調整</string>
<string name="value_adapt_battery_life">根據電池壽命調整</string>
<string name="value_always">一律使用</string>
<string name="value_float_feet">%1$.2f 英尺</string>
<string name="value_float_kilometer">%1$.2f 公里</string>
<string name="value_float_meter">%1$.2f 公尺</string>
<string name="value_float_mile">%1$.2f 英里</string>
<string name="value_float_mile_poor_gps">%1$.1f 英里 (GPS 訊號差)</string>
<string name="value_integer_feet">%1$d 英尺</string>
<string name="value_integer_feet_excellent_gps">%1$d 英尺 (GPS 訊號佳)</string>
<string name="value_integer_feet_recommended">%1$d 英尺 (建議值)</string>
-5
View File
@@ -342,11 +342,6 @@ limitations under the License.
<string name="value_adapt_accuracy">适应精度</string>
<string name="value_adapt_battery_life">视电池电量而定</string>
<string name="value_always">一律启用</string>
<string name="value_float_feet">%1$.2f 英尺</string>
<string name="value_float_kilometer">%1$.2f 公里</string>
<string name="value_float_meter">%1$.2f 米</string>
<string name="value_float_mile">%1$.2f 英里</string>
<string name="value_float_mile_poor_gps">%1$.1f 英里(GPS 信号差)</string>
<string name="value_integer_feet">%1$d 英尺</string>
<string name="value_integer_feet_excellent_gps">%1$d 英尺(GPS 信号极佳)</string>
<string name="value_integer_feet_recommended">%1$d 英尺(推荐)</string>
+6
View File
@@ -18,12 +18,18 @@ limitations under the License.
<string name="app_name" translatable="false">OpenTracks</string>
<string name="egm2008" translatable="false">EGM2008</string>
<string name="wgs84" translatable="false">WGS84</string>
<string name="third_party_data" translatable="false">
<a href="https://earth-info.nga.mil/">National Geospatial-Intelligence Agency (NGA)\'s EGM2008 model</a> 5\' minute resolution.
Data provided by <a href="https://geographiclib.sourceforge.io/html/geoid.html">GeographicLib</a>.
</string>
<!-- If the order depends on the language, we might need to make this translatable -->
<string name="distance_with_unit" translatable="false">%1$s %2$s</string>
<string name="speed_with_unit" translatable="false">%1$s %2$s</string>
<string name="time" translatable="false">%1$d:%2$02d</string>
<!-- List here all used libraries (will be shown in the about activity -->
<string name="third_party_libraries" translatable="false">
<a href="https://developer.android.com/topic/libraries/support-library/">Google\'s androidX support</a> (Apache License, Version 2.0) \n
+11
View File
@@ -238,6 +238,17 @@
<item>-50</item>
<item>-100</item>
</string-array>
<string name="voice_speed_rate_key" translatable="false">voiceSpeedRate</string>
<string name="voice_speed_rate_default" translatable="false">0.9</string>
<string-array name="voice_speed_rate_values">
<item>0.7</item>
<item>0.8</item>
<item>@string/voice_speed_rate_default</item>
<item>1.0</item>
<item>1.1</item>
<item>1.2</item>
<item>1.3</item>
</string-array>
<string name="export_trackfileformat_key" translatable="false">exportTrackFileFormat</string>
<!-- See TrackFileFormat -->
+2 -10
View File
@@ -349,6 +349,7 @@ limitations under the License.
<string name="menu_stop_gps">Stop GPS</string>
<string name="menu_view_photo">View photo</string>
<string name="menu_voice_frequency">Voice frequency</string>
<string name="menu_voice_rate">Voice speed</string>
<string name="menu_resume_track">Resume track</string>
<!-- OpenTracks -->
<string name="recording_service">Recording service</string>
@@ -507,21 +508,14 @@ limitations under the License.
<string name="unit_kilometer_per_hour">km/h</string>
<string name="unit_meter">m</string>
<string name="unit_mile">mi</string>
<string name="unit_mile_per_hour">mi/h</string>
<string name="unit_mile_per_hour">mph</string>
<string name="unit_minute_per_kilometer">min/km</string>
<string name="unit_minute_per_mile">min/mi</string>
<!-- Value -->
<string name="value_adapt_accuracy">Adapt for accuracy</string>
<string name="value_adapt_battery_life">Adapt for battery life</string>
<string name="value_always">Always</string>
<string name="value_float_feet">%1$.2f ft</string>
<string name="value_float_kilometer">%1$.2f km</string>
<string name="value_float_kilometer_hour">%1$.2f km/h</string>
<string name="value_float_meter">%1$.2f m</string>
<string name="value_float_mile">%1$.2f mi</string>
<string name="value_float_mile_hour">%1$.2f mph</string>
<string name="value_float_mile_poor_gps">%1$.1f mi (poor GPS)</string>
<string name="value_integer_feet">%1$d ft</string>
<string name="value_integer_feet_excellent_gps">%1$d ft (excellent GPS)</string>
<string name="value_integer_feet_recommended">%1$d ft (recommended)</string>
@@ -536,8 +530,6 @@ limitations under the License.
<string name="value_integer_minute">%1$d min</string>
<string name="value_integer_percent">%1$d %%</string>
<string name="value_integer_second">%1$d 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="value_none">None</string>
<string name="value_off">Off</string>
<string name="value_smallest">Smallest</string>
+7
View File
@@ -85,6 +85,13 @@ limitations under the License.
android:key="@string/voice_frequency_key"
android:title="@string/menu_voice_frequency"
app:useSimpleSummaryProvider="true" />
<ListPreference
android:defaultValue="@string/voice_speed_rate_default"
android:entryValues="@array/voice_speed_rate_values"
android:entries="@array/voice_speed_rate_values"
android:key="@string/voice_speed_rate_key"
android:title="@string/menu_voice_rate"
app:useSimpleSummaryProvider="true" />
<de.dennisguse.opentracks.settings.ActivityTypePreference
android:defaultValue="@string/default_activity_default"
android:key="@string/default_activity_key"