Compare commits

...

29 Commits

Author SHA1 Message Date
Dennis Guse c7738b1cb2 Release: v4.27.2 2026-06-24 06:24:33 +02:00
Dennis Guse 713f5c7708 Cleanup. 2026-06-24 06:10:12 +02:00
Dennis Guse b9a36d1b3c Bugfix: storing idle and sensor-based trackpoints might happen in the wrong order (race condition).
Fixes #2356.
2026-06-21 18:50:26 +02:00
Dennis Guse 34e89ea3f9 TimeZoneOffsets now supports minutes.
Fixes #2357.
2026-06-21 13:54:59 +02:00
CI/CD Bot by pstorch 50a0e37086 Update actions/checkout action to v7 2026-06-19 05:44:07 +02:00
CI/CD Bot by pstorch 11ac6b7279 Update dependency gradle to v9.6.0 2026-06-19 00:34:17 +00:00
CI/CD Bot by pstorch aadb79bf7d Update dependency androidx.core:core-location-altitude to v1.0.0 2026-06-17 17:34:17 +00:00
ovl-005 da6b26644e Translated using Weblate (Norwegian Bokmål)
Currently translated at 98.7% (539 of 546 strings)

Translation: OpenTracks/opentracks.src-main-res-values-strings-xml--main
Translate-URL: https://translate.codeberg.org/projects/opentracks/opentracks-src-main-res-values-strings-xml-main/nb_NO/
2026-06-12 21:41:16 +00:00
qwjyh b264655d75 Translated using Weblate (Japanese)
Currently translated at 52.1% (285 of 546 strings)

Translation: OpenTracks/opentracks.src-main-res-values-strings-xml--main
Translate-URL: https://translate.codeberg.org/projects/opentracks/opentracks-src-main-res-values-strings-xml-main/ja/
2026-06-09 14:16:51 +00:00
Outbreak2096 1ea9c0b216 Translated using Weblate (Chinese (Simplified Han script))
Currently translated at 100.0% (546 of 546 strings)

Translation: OpenTracks/opentracks.src-main-res-values-strings-xml--main
Translate-URL: https://translate.codeberg.org/projects/opentracks/opentracks-src-main-res-values-strings-xml-main/zh_Hans/
2026-06-09 14:16:51 +00:00
SomeTr 8040edac7a Translated using Weblate (Ukrainian)
Currently translated at 100.0% (546 of 546 strings)

Translation: OpenTracks/opentracks.src-main-res-values-strings-xml--main
Translate-URL: https://translate.codeberg.org/projects/opentracks/opentracks-src-main-res-values-strings-xml-main/uk/
2026-06-09 14:16:51 +00:00
ghose 037c79a1c5 Translated using Weblate (Galician)
Currently translated at 100.0% (546 of 546 strings)

Translation: OpenTracks/opentracks.src-main-res-values-strings-xml--main
Translate-URL: https://translate.codeberg.org/projects/opentracks/opentracks-src-main-res-values-strings-xml-main/gl/
2026-06-09 14:16:51 +00:00
Priit Jõerüüt bc46f7447e Translated using Weblate (Estonian)
Currently translated at 100.0% (546 of 546 strings)

Translation: OpenTracks/opentracks.src-main-res-values-strings-xml--main
Translate-URL: https://translate.codeberg.org/projects/opentracks/opentracks-src-main-res-values-strings-xml-main/et/
2026-06-09 14:16:51 +00:00
Dennis Guse 6e88a448b1 Release: v4.27.1 2026-06-07 12:24:52 +02:00
jimkats 2dac8d3ea6 Translated using Weblate (Greek)
Currently translated at 71.7% (391 of 545 strings)

Translation: OpenTracks/opentracks.src-main-res-values-strings-xml--main
Translate-URL: https://translate.codeberg.org/projects/opentracks/opentracks-src-main-res-values-strings-xml-main/el/
2026-06-04 19:41:11 +02:00
CI/CD Bot by pstorch 0844c48d25 Update dependency androidx.core:core to v1.19.0 2026-06-03 21:25:51 +02:00
Dennis Guse 9607fda962 Android17: upgrade target sdk to 37.
Part of #2340.
2026-06-03 21:25:20 +02:00
Dennis Guse 77ed28a8e2 Revert "Remove StrictMode warnings."
This reverts commit f3e8d033cc.
2026-06-03 21:25:20 +02:00
Dennis Guse 4283e6d316 Bugfix: TrackRecordingService must handle restarts while recording.
We now store the Track.Id in a sharedPreferences.

Functionality was removed in e09b04308c

Fixes #2333
2026-05-29 16:36:29 +02:00
Dennis Guse ee030f32f5 Bugfix: resetting settings should restore data. 2026-05-29 16:36:29 +02:00
Dennis Guse fb25055f3e Activity: add gravel biking. 2026-05-27 12:23:32 +02:00
Dennis Guse 9cdf87b998 Test for announcement without speed/distance units.
Part of #2323
2026-05-27 11:29:18 +02:00
gallegonovato e9217b5327 Translated using Weblate (Spanish)
Currently translated at 7.5% (14 of 185 strings)

Translation: OpenTracks/App store metadata files
Translate-URL: https://translate.codeberg.org/projects/opentracks/app-store-metadata-files/es/
2026-05-27 06:28:40 +00:00
claudio001 b4cb606499 Translated using Weblate (Italian)
Currently translated at 4.8% (9 of 184 strings)

Translation: OpenTracks/App store metadata files
Translate-URL: https://translate.codeberg.org/projects/opentracks/app-store-metadata-files/it/
2026-05-24 22:06:57 +02:00
CI/CD Bot by pstorch d1975685bc Update dependency com.google.android.material:material to v1.14.0 2026-05-13 15:34:36 +00:00
CI/CD Bot by pstorch 8cd9dda1f5 Update dependency gradle to v9.5.1 2026-05-12 14:36:37 +00:00
Dennis Guse c072967dd1 PlayStore: data retention seems to be a thing now. 2026-05-10 11:32:02 +02:00
Dennis Guse 94e91d2063 PlayStore: add existing privacy policy. 2026-05-10 11:32:02 +02:00
Kilian Lackhove 796fa216f6 added bosch cadence sensor 2026-05-09 21:46:44 +02:00
33 changed files with 511 additions and 69 deletions
+2 -2
View File
@@ -13,7 +13,7 @@ jobs:
image: 'ubuntu:android-24.04'
steps:
- name: Checkout sources
uses: actions/checkout@v6
uses: actions/checkout@v7
with:
fetch-depth: 1
@@ -27,7 +27,7 @@ jobs:
image: 'ubuntu:android-24.04'
steps:
- name: Checkout sources
uses: actions/checkout@v6
uses: actions/checkout@v7
with:
fetch-depth: 1
+2 -1
View File
@@ -123,4 +123,5 @@ Tested with:
Currently supported measurements:
* Human power
* Human power
* Cadence
+7 -7
View File
@@ -33,7 +33,7 @@ android {
targetCompatibility JavaVersion.VERSION_17
}
compileSdk = 36
compileSdk = 37
buildFeatures {
viewBinding = true
@@ -47,13 +47,13 @@ android {
defaultConfig {
applicationId 'de.dennisguse.opentracks'
versionCode 6653
versionName "v4.27.0"
versionCode 6682
versionName "v4.27.2"
buildConfigField "String", "VERSION_NAME_FULL", "\"${getVersionName()}\""
minSdk = 26
targetSdk = 36
targetSdk = 37
testInstrumentationRunner 'de.dennisguse.opentracks.TestRunner'
testInstrumentationRunnerArguments clearPackageData: 'true'
@@ -149,12 +149,12 @@ dependencies {
implementation 'androidx.preference:preference:1.2.1'
implementation 'androidx.documentfile:documentfile:1.1.0'
implementation 'androidx.gridlayout:gridlayout:1.1.0'
implementation 'com.google.android.material:material:1.13.0'
implementation 'com.google.android.material:material:1.14.0'
implementation 'androidx.constraintlayout:constraintlayout:2.2.1'
implementation 'androidx.core:core:1.18.0'
implementation 'androidx.core:core:1.19.0'
implementation 'androidx.core:core-splashscreen:1.2.0'
implementation 'androidx.mediarouter:mediarouter:1.8.1'
implementation 'androidx.core:core-location-altitude:1.0.0-rc01'
implementation 'androidx.core:core-location-altitude:1.0.0'
implementation 'androidx.work:work-runtime:2.11.2'
androidTestImplementation 'androidx.test:core:1.7.0'
@@ -0,0 +1,11 @@
v4.27.1: OpenTracks
Changes:
- Support for Bosch Smart System eBikes (cadence, human power)
- Add activity "gravel biking"
Bugfix:
- While recording: TrackRecordingService would not restart correctly if killed by Android
Developer:
- TargetSDK 37
@@ -0,0 +1,5 @@
v4.27.2: OpenTracks
Bugfix:
- Not all valid timezone offsets could be set
- Fixed race condition for IDLE trackpoints
@@ -0,0 +1,17 @@
v3.0.1: OpenTracks
Cambios:
- Compartir marcadores
- Configuración reestructurada
- Se ha eliminado el botón «Atrás» de la barra de acciones
- Exportación: se ha eliminado la opción de configuración del tamaño de las fotos
- Exportación: ahora se puede seleccionar el formato KML
Correcciones de errores:
- El directorio de exportación es ahora /OpenTracks/
- El modo horizontal ya no provoca fallos
- Se ha corregido la exportación a KML/KMZ
Desarrollador:
- Las pruebas son ejecutables (pero muchas fallan)
- Se ha habilitado la integración continua con TravisCI (sin pruebas)
@@ -0,0 +1,14 @@
v4.27.0: OpenTracks
Cambios:
- Sensor: potencia de la bicicleta eléctrica Bosch
- Sensores: se incluyen los dispositivos Bluetooth ya emparejados (como la bicicleta eléctrica Bosch)
- Barómetro: se aplica la frecuencia de muestreo
Corrección de errores:
- API del panel de control: no se proporcionaba la actualización
- No se podía activar el GPS con el Bluetooth desactivado
Desarrollador:
- Se ha eliminado Mockito
- Pruebas de la interfaz de usuario: menos inestables
@@ -0,0 +1,5 @@
v4.26.2: OpenTracks
Correzioni:
- Il GPS via Bluetooth non è (ancora) supportato
- Il tipo di altitudine (EGM2008) non viene mostrato
+1 -1
View File
@@ -1,6 +1,6 @@
distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
distributionUrl=https\://services.gradle.org/distributions/gradle-9.5.0-bin.zip
distributionUrl=https\://services.gradle.org/distributions/gradle-9.6.0-bin.zip
networkTimeout=10000
validateDistributionUrl=true
zipStoreBase=GRADLE_USER_HOME
+14
View File
@@ -0,0 +1,14 @@
<html>
<body>
<h1>OpenTracks: Privacy Policy</h1>
<p>
OpenTracks does only store data on the local device that is relevant for tracking your sport exercise.
Stored data is not transmitted from the app itself to a third party.
Data is stored by OpenTracks until:
1. the user deletes the recorded track or
2. the user uninstalls OpenTracks
</p>
</body>
</html>
@@ -96,6 +96,25 @@ public class SensorDataCyclingTest {
assertNull(current.getAggregatedValue(Instant.MIN));
}
@Test
public void compute_cadence_directCadence_withoutPrevious() {
AggregatorCyclingCadence current = new AggregatorCyclingCadence("", "");
current.add(Instant.MIN, new CyclingCadenceBluetooth.DirectCadenceData(Cadence.of(92)));
assertEquals(92, current.getAggregatedValue(Instant.MIN).getRPM(), 0.01);
}
@Test
public void compute_cadence_directCadence_winsOverCrankData() {
AggregatorCyclingCadence current = new AggregatorCyclingCadence("", "");
current.add(Instant.MIN, new CyclingCadenceBluetooth.CrankData(1L, 1024));
current.add(Instant.MIN, new CyclingCadenceBluetooth.DirectCadenceData(Cadence.of(92)));
assertEquals(92, current.getAggregatedValue(Instant.MIN).getRPM(), 0.01);
}
@Test
public void compute_speed() {
AggregatorCyclingDistanceSpeed current = new AggregatorCyclingDistanceSpeed("", "");
@@ -2,6 +2,7 @@ package de.dennisguse.opentracks.services;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;
import android.content.Context;
import android.content.Intent;
@@ -93,6 +94,41 @@ public class TrackRecordingServiceRecordingTest {
service = startService();
}
@MediumTest
@Test
public void reCreate_not_recording() {
// given
TrackIdStorage.unset(context);
assertFalse(service.isRecording());
// when
service.onStartCommand(null, 0, 1);
// then
assertFalse(service.isRecording());
}
@MediumTest
@Test
public void reCreate_recording() {
// given
assertFalse(service.isRecording());
Track.Id trackId = service.startNewTrack();
assertTrue(service.isRecording());
// when (crash and restart)
service.onDestroy();
service.onCreate();
service.onStartCommand(null, 0, 1);
// then
assertTrue(service.isRecording());
assertEquals(trackId, service.getRecordingStatusObservable().getValue().trackId());
service.endCurrentTrack();
}
@MediumTest
@Test
public void recording_startStop() {
@@ -52,6 +52,7 @@ public class VoiceAnnouncementUtilsTest {
@Before
public void setUp() {
PreferencesUtils.setVoiceAnnounceUnit(true);
PreferencesUtils.setVoiceAnnounceHeartRateCurrent(false);
PreferencesUtils.setVoiceAnnounceLapHeartRate(false);
PreferencesUtils.setVoiceAnnounceAverageHeartRate(false);
@@ -221,6 +222,48 @@ public class VoiceAnnouncementUtilsTest {
assertEquals("12:00 AM. Total distance 19.9 kilometers. 1 hour. Average moving speed 19.9 kilometers per hour.", announcement);
}
@Test
public void getAnnouncement_metric_distance_without_unit() {
PreferencesUtils.setVoiceAnnounceUnit(false);
Track track = new Track(
null,
null,
"",
"",
"",
ActivityType.AIRPLANE,
ZoneOffset.UTC,
new Statistics(
Instant.EPOCH,
Instant.EPOCH,
Duration.ofHours(2).plusMinutes(5).plusSeconds(10),
Duration.ofHours(1),
Distance.of(19900),
Speed.of(100),
null,
new AltitudeGainLoss(6000, 6000),
null,
null
));
SensorDataSet dataSet = new SensorDataSet(
null,
null,
null,
new SensorData<>(HeartRate.of(60), "unused"),
null, null,
null,
null
);
// when
String announcement = VoiceAnnouncementUtils.createStatistics(context, track, dataSet, UnitSystem.METRIC, true, null, null).toString();
// then
assertEquals("12:00 AM. Total distance 19.9. 1 hour. Average moving speed 19.9.", announcement);
}
@Test
public void getAnnouncement_withInterval_metric_speed() {
// given
@@ -4,6 +4,7 @@ import android.annotation.SuppressLint;
import android.app.Application;
import android.content.Context;
import android.os.Build;
import android.os.StrictMode;
import android.util.Log;
import java.lang.reflect.Method;
@@ -27,6 +28,11 @@ public class Startup extends Application {
// Include version information into stack traces.
Log.i(TAG, BuildConfig.APPLICATION_ID + "; BuildType: " + BuildConfig.BUILD_TYPE + "; VersionName: " + BuildConfig.VERSION_NAME + "/" + BuildConfig.VERSION_NAME_FULL + " VersionCode: " + BuildConfig.VERSION_CODE);
// In debug builds: show thread and VM warnings.
if (BuildConfig.DEBUG) {
Log.d(TAG, "Enabling strict mode");
StrictMode.enableDefaults();
}
PreferencesUtils.initPreferences(this);
// Set default values of preferences on first start.
@@ -30,6 +30,7 @@ public enum ActivityType {
FERRY("ferry", R.string.activity_type_ferry, R.drawable.ic_activity_boat_24dp, true),
FRISBEE("frisbee", R.string.activity_type_frisbee, ActivityIcon.ICON_UNKNOWN, true),
GLIDING("gliding", R.string.activity_type_gliding, ActivityIcon.ICON_UNKNOWN, true),
GRAVEL_BIKING("gravel biking", R.string.activity_type_biking, R.drawable.ic_activity_bike_24dp, true),
HANG_GLIDING("hang gliding", R.string.activity_type_hang_gliding, ActivityIcon.ICON_UNKNOWN, true),
HELICOPTER("helicopter", R.string.activity_type_helicopter, ActivityIcon.ICON_UNKNOWN, true),
HIKING("hiking", R.string.activity_type_hiking, R.drawable.ic_activity_walk_24dp, false),
@@ -9,7 +9,7 @@ import de.dennisguse.opentracks.sensors.driver.CyclingCadenceBluetooth;
import de.dennisguse.opentracks.sensors.driver.Driver;
import de.dennisguse.opentracks.sensors.sensorData.AggregatorCyclingCadence;
public class CyclingCadenceHandler extends SensorHandler<CyclingCadenceBluetooth.CrankData, Cadence> {
public class CyclingCadenceHandler extends SensorHandler<CyclingCadenceBluetooth.CyclingCadenceMeasurement, Cadence> {
protected CyclingCadenceHandler(SensorManager sensorManager) {
super(sensorManager);
@@ -6,23 +6,25 @@ import android.util.Pair;
import java.util.List;
import de.dennisguse.opentracks.data.models.Cadence;
import de.dennisguse.opentracks.sensors.ServiceMeasurementUUID;
public class CyclingCadenceBluetooth implements BluetoothDriver.BluetoothParser<CyclingCadenceBluetooth.CrankData> {
public class CyclingCadenceBluetooth implements BluetoothDriver.BluetoothParser<CyclingCadenceBluetooth.CyclingCadenceMeasurement> {
private static final String TAG = CyclingCadenceBluetooth.class.getSimpleName();
public static final List<ServiceMeasurementUUID> CYCLING_CADENCE = List.of(
public static final List<ServiceMeasurementUUID> SUPPORTED_SERVICES = List.of(
CyclingPowerBluetooth.CYCLING_POWER,
CyclingDistanceSpeedBluetooth.CYCLING_SPEED_CADENCE
CyclingDistanceSpeedBluetooth.CYCLING_SPEED_CADENCE,
BoschEbikeParser.BOSCH_EBIKE
);
@Override
public List<ServiceMeasurementUUID> getServices() {
return CYCLING_CADENCE;
return SUPPORTED_SERVICES;
}
@Override
public CrankData parsePayload(ServiceMeasurementUUID serviceMeasurementUUID, String sensorName, BluetoothGattCharacteristic characteristic) {
public CyclingCadenceMeasurement parsePayload(ServiceMeasurementUUID serviceMeasurementUUID, String sensorName, BluetoothGattCharacteristic characteristic) {
if (serviceMeasurementUUID.equals(CyclingPowerBluetooth.CYCLING_POWER)) {
CyclingPowerBluetooth.Data data = CyclingPowerBluetooth.parseCyclingPower(characteristic);
if (data != null && data.crank() != null) {
@@ -38,12 +40,28 @@ public class CyclingCadenceBluetooth implements BluetoothDriver.BluetoothParser<
}
}
if (serviceMeasurementUUID.equals(BoschEbikeParser.BOSCH_EBIKE)) {
BoschEbikeParser.Data boschData = BoschEbikeParser.parse(characteristic.getValue());
if (boschData != null && boschData.cadence() != null) {
return new DirectCadenceData(boschData.cadence());
}
}
Log.e(TAG, "Don't know how to decode this payload.");
return null;
}
public sealed interface CyclingCadenceMeasurement permits CrankData, DirectCadenceData {
}
public record CrankData(
long crankRevolutionsCount, // UINT32
int crankRevolutionsTime // UINT16; 1/1024s
) {}
) implements CyclingCadenceMeasurement {
}
public record DirectCadenceData(
Cadence cadence
) implements CyclingCadenceMeasurement {
}
}
@@ -10,7 +10,7 @@ import de.dennisguse.opentracks.data.models.Cadence;
import de.dennisguse.opentracks.sensors.UintUtils;
import de.dennisguse.opentracks.sensors.driver.CyclingCadenceBluetooth;
public class AggregatorCyclingCadence extends Aggregator<CyclingCadenceBluetooth.CrankData, Cadence> {
public class AggregatorCyclingCadence extends Aggregator<CyclingCadenceBluetooth.CyclingCadenceMeasurement, Cadence> {
private final String TAG = AggregatorCyclingCadence.class.getSimpleName();
@@ -19,12 +19,20 @@ public class AggregatorCyclingCadence extends Aggregator<CyclingCadenceBluetooth
}
@Override
protected void computeValue(@NonNull Raw<CyclingCadenceBluetooth.CrankData> current) {
if (previous == null) {
protected void computeValue(@NonNull Raw<CyclingCadenceBluetooth.CyclingCadenceMeasurement> current) {
if (current.value() instanceof CyclingCadenceBluetooth.DirectCadenceData directCadenceData) {
output = directCadenceData.cadence();
return;
}
float timeDiff_ms = UintUtils.diff(current.value().crankRevolutionsTime(), previous.value().crankRevolutionsTime(), UintUtils.UINT16_MAX) / 1024f * 1000;
computeRawCadence((CyclingCadenceBluetooth.CrankData) current.value());
}
private void computeRawCadence(@NonNull CyclingCadenceBluetooth.CrankData currentRaw) {
CyclingCadenceBluetooth.CrankData previousRaw = getPreviousRaw();
if (previousRaw == null) return;
float timeDiff_ms = UintUtils.diff(currentRaw.crankRevolutionsTime(), previousRaw.crankRevolutionsTime(), UintUtils.UINT16_MAX) / 1024f * 1000;
Duration timeDiff = Duration.ofMillis((long) timeDiff_ms);
if (timeDiff.isZero()) {
@@ -37,13 +45,21 @@ public class AggregatorCyclingCadence extends Aggregator<CyclingCadenceBluetooth
}
// TODO We have to treat with overflow according to the documentation: read https://codeberg.org/OpenTracksApp/OpenTracks/pulls/953#issuecomment-6466930
if (current.value().crankRevolutionsCount() < previous.value().crankRevolutionsCount()) {
if (currentRaw.crankRevolutionsCount() < previousRaw.crankRevolutionsCount()) {
Log.e(TAG, "Crank revolutions count difference is invalid: cannot compute cadence.");
output = null;
return;
}
long crankDiff = UintUtils.diff(current.value().crankRevolutionsCount(), previous.value().crankRevolutionsCount(), UintUtils.UINT32_MAX);
long crankDiff = UintUtils.diff(currentRaw.crankRevolutionsCount(), previousRaw.crankRevolutionsCount(), UintUtils.UINT32_MAX);
output = Cadence.of(crankDiff, timeDiff);
}
private CyclingCadenceBluetooth.CrankData getPreviousRaw() {
if (previous == null) {
return null;
}
return previous.value() instanceof CyclingCadenceBluetooth.CrankData raw ? raw : null;
}
}
@@ -0,0 +1,56 @@
package de.dennisguse.opentracks.services;
import android.content.Context;
import android.content.SharedPreferences;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import de.dennisguse.opentracks.data.models.Track;
/**
* For handling restarts of TrackRecordingService: while recording, stores the {@link de.dennisguse.opentracks.data.models.Track.Id}.
*/
class TrackIdStorage {
private final static String SHARED_PREFERENCE_NAME = "TrackRecordingService_pref";
private final static String KEY = "trackId_key";
private static SharedPreferences sharedPreferences;
private static void init(Context context) {
if (sharedPreferences == null) {
sharedPreferences = context.getSharedPreferences(SHARED_PREFERENCE_NAME, Context.MODE_PRIVATE);
}
}
@Nullable
public static Track.Id get(Context context) {
TrackIdStorage.init(context);
long value = sharedPreferences.getLong(KEY, -1);
if (value < 0) {
return null;
}
return new Track.Id(value);
}
public static void set(Context context, @NonNull Track.Id trackId) {
TrackIdStorage.init(context);
SharedPreferences.Editor editor = sharedPreferences.edit();
editor.putLong(KEY, trackId.id());
editor.commit();
}
public static void unset(Context context) {
TrackIdStorage.init(context);
SharedPreferences.Editor editor = sharedPreferences.edit();
editor.remove(KEY);
editor.commit();
}
}
@@ -67,7 +67,7 @@ public class TrackRecordingManager implements SharedPreferences.OnSharedPreferen
contentProviderUtils = new ContentProviderUtils(context);
}
Track.Id startNewTrack() {
synchronized Track.Id startNewTrack() {
TrackPoint segmentStartTrackPoint = trackPointCreator.createSegmentStartManual();
ZoneOffset zoneOffset = ZoneOffset.systemDefault().getRules().getOffset(segmentStartTrackPoint.getTime());
@@ -103,7 +103,7 @@ public class TrackRecordingManager implements SharedPreferences.OnSharedPreferen
/**
* @return if the recording could be started.
*/
boolean resumeExistingTrack(@NonNull Track.Id resumeTrackId) {
synchronized boolean resumeExistingTrack(@NonNull Track.Id resumeTrackId) {
trackId = resumeTrackId;
Track track = contentProviderUtils.getTrack(trackId);
if (track == null) {
@@ -119,7 +119,7 @@ public class TrackRecordingManager implements SharedPreferences.OnSharedPreferen
return true;
}
void endCurrentTrack() {
synchronized void endCurrentTrack() {
TrackPoint segmentEnd = trackPointCreator.createSegmentEnd();
insertTrackPoint(segmentEnd, true);
@@ -149,7 +149,7 @@ public class TrackRecordingManager implements SharedPreferences.OnSharedPreferen
}
@VisibleForTesting(otherwise = VisibleForTesting.PACKAGE_PRIVATE)
public void onIdle() {
public synchronized void onIdle() {
Log.d(TAG, "Becoming idle");
onNewTrackPoint(trackPointCreator.createIdle());
@@ -86,7 +86,6 @@ public class TrackRecordingService extends Service implements TrackPointCreator.
private MutableLiveData<GpsStatusValue> gpsStatusObservable;
private MutableLiveData<RecordingData> recordingDataObservable;
// The following variables are set when recording:
private Handler handler;
private TrackPointCreator trackPointCreator;
@@ -119,9 +118,6 @@ public class TrackRecordingService extends Service implements TrackPointCreator.
@Override
public void onDestroy() {
Log.d(TAG, "Destroying");
if (isRecording()) {
endCurrentTrack();
}
if (trackPointCreator != null && trackPointCreator.isStarted()) {
stopSensors();
}
@@ -150,6 +146,16 @@ public class TrackRecordingService extends Service implements TrackPointCreator.
@Override
public int onStartCommand(Intent intent, int flags, int startId) {
if (intent == null) {
Log.i(TAG, "Service got killed and is restarting");
//Best guess: do we need to resume a recording?
Track.Id trackId = TrackIdStorage.get(this);
if (trackId != null) {
Log.i(TAG, "After restart: resuming recording for " + trackId);
resumeTrack(trackId);
}
}
return START_STICKY;
}
@@ -167,9 +173,7 @@ public class TrackRecordingService extends Service implements TrackPointCreator.
// Set recording status
Track.Id trackId = trackRecordingManager.startNewTrack();
updateRecordingStatus(RecordingStatus.record(trackId));
startRecording();
startRecording(trackId);
return trackId;
}
@@ -180,12 +184,13 @@ public class TrackRecordingService extends Service implements TrackPointCreator.
}
Log.i(TAG, "resumeTrack");
updateRecordingStatus(RecordingStatus.record(trackId));
startRecording();
startRecording(trackId);
}
private void startRecording() {
private void startRecording(Track.Id trackId) {
updateRecordingStatus(RecordingStatus.record(trackId));
TrackIdStorage.set(this, trackId);
startSensors();
voiceAnnouncementManager.start(trackRecordingManager.getTrackStatistics());
@@ -226,6 +231,7 @@ public class TrackRecordingService extends Service implements TrackPointCreator.
updateRecordingStatus(STATUS_DEFAULT);
trackRecordingManager.endCurrentTrack();
TrackIdStorage.unset(this);
stopUpdateRecordingData();
@@ -328,6 +328,11 @@ public class PreferencesUtils {
return getBoolean(R.string.voice_announce_unit_key, true);
}
@VisibleForTesting
public static void setVoiceAnnounceUnit(boolean value) {
setBoolean(R.string.voice_announce_unit_key, value);
}
public static Duration getVoiceAnnouncementFrequency() {
final int DEFAULT = Integer.parseInt(resources.getString(R.string.voice_announcement_frequency_default));
int value = getInt(R.string.voice_announcement_frequency_key, DEFAULT);
@@ -748,11 +753,13 @@ public class PreferencesUtils {
sharedPreferences.edit().clear().commit();
}
// Each setting file must be mentioned here.
// Each setting file must be mentioned here: see xml/settings.xml
PreferenceManager.setDefaultValues(context, R.xml.settings, readAgain);
PreferenceManager.setDefaultValues(context, R.xml.settings_defaults, readAgain);
PreferenceManager.setDefaultValues(context, R.xml.settings_user_interface, readAgain);
PreferenceManager.setDefaultValues(context, R.xml.settings_recording, readAgain);
PreferenceManager.setDefaultValues(context, R.xml.settings_sensors, readAgain);
PreferenceManager.setDefaultValues(context, R.xml.settings_announcements, readAgain);
PreferenceManager.setDefaultValues(context, R.xml.settings_import_export, readAgain);
PreferenceManager.setDefaultValues(context, R.xml.settings_public_api, readAgain);
}
@@ -27,6 +27,6 @@ public class BluetoothLeCyclingCadencePreference extends BluetoothLeSensorPrefer
@Override
public PreferenceDialogFragmentCompat createInstance() {
return BluetoothLeSensorPreferenceDialog.newInstance(getKey(), CyclingCadenceBluetooth.CYCLING_CADENCE);
return BluetoothLeSensorPreferenceDialog.newInstance(getKey(), CyclingCadenceBluetooth.SUPPORTED_SERVICES);
}
}
@@ -7,13 +7,51 @@ import android.widget.ArrayAdapter;
import androidx.annotation.NonNull;
import java.time.ZoneOffset;
import java.util.stream.IntStream;
/**
* <a href="Data source for UTC Offsets">https://en.wikipedia.org/wiki/List_of_UTC_offsets</a>
*/
public class ZoneOffsetAdapter extends ArrayAdapter<ZoneOffset> {
public ZoneOffsetAdapter(@NonNull Context context, int resource) {
super(context, resource, IntStream.range(-12, +12)
.boxed()
.map(ZoneOffset::ofHours)
.toList());
super(context, resource);
addAll(
ZoneOffset.ofHoursMinutes(-12, 00),
ZoneOffset.ofHoursMinutes(-11, 00),
ZoneOffset.ofHoursMinutes(-10, 00),
ZoneOffset.ofHoursMinutes(-9, -30),
ZoneOffset.ofHoursMinutes(-8, 00),
ZoneOffset.ofHoursMinutes(-7, 00),
ZoneOffset.ofHoursMinutes(-6, 00),
ZoneOffset.ofHoursMinutes(-5, 00),
ZoneOffset.ofHoursMinutes(-4, 00),
ZoneOffset.ofHoursMinutes(-3, -30),
ZoneOffset.ofHoursMinutes(-3, 00),
ZoneOffset.ofHoursMinutes(-2, 00),
ZoneOffset.ofHoursMinutes(-1, 00),
ZoneOffset.ofHoursMinutes(0, 00),
ZoneOffset.ofHoursMinutes(1, 00),
ZoneOffset.ofHoursMinutes(2, 00),
ZoneOffset.ofHoursMinutes(3, 00),
ZoneOffset.ofHoursMinutes(3, 30),
ZoneOffset.ofHoursMinutes(4, 00),
ZoneOffset.ofHoursMinutes(4, 30),
ZoneOffset.ofHoursMinutes(5, 30),
ZoneOffset.ofHoursMinutes(5, 45),
ZoneOffset.ofHoursMinutes(6, 00),
ZoneOffset.ofHoursMinutes(6, 30),
ZoneOffset.ofHoursMinutes(7, 00),
ZoneOffset.ofHoursMinutes(8, 00),
ZoneOffset.ofHoursMinutes(8, 45),
ZoneOffset.ofHoursMinutes(9, 00),
ZoneOffset.ofHoursMinutes(9, 30),
ZoneOffset.ofHoursMinutes(10, 00),
ZoneOffset.ofHoursMinutes(10, 30),
ZoneOffset.ofHoursMinutes(11, 00),
ZoneOffset.ofHoursMinutes(12, 00),
ZoneOffset.ofHoursMinutes(12, 45),
ZoneOffset.ofHoursMinutes(13, 00),
ZoneOffset.ofHoursMinutes(14, 00)
);
}
}
@@ -1,16 +0,0 @@
package de.dennisguse.opentracks.ui.customRecordingLayout;
public enum CustomLayoutFieldType {
GENERIC(1),
CLOCK(2);
private final int value;
CustomLayoutFieldType(int value) {
this.value = value;
}
public int value() {
return this.value;
}
}
+121 -5
View File
@@ -94,7 +94,7 @@ limitations under the License.
<string name="description_max_altitude">Μέγιστη ανύψωση: %1$d μέτρα (%2$d πόδια)</string>
<string name="description_max_speed">Μέγιστη ταχύτητα: %1$.2f χλμ/ώρα (%2$.1f μίλια/ώρα)</string>
<string name="description_min_altitude">Ελάχιστη ανύψωση: %1$d μέτρα (%2$d πόδια)</string>
<string name="description_moving_time">Χρόνος μεταφοράς: %1$s</string>
<string name="description_moving_time">Χρόνος κίνησης: %1$s</string>
<string name="description_pace_imperial">Ρυθμός (λεπτά/μίλι)</string>
<string name="description_pace_metric">Ρυθμός (λεπτά/χλμ)</string>
<string name="description_recorded_time">Εγγραφή: %1$s</string>
@@ -230,22 +230,22 @@ limitations under the License.
<!-- Tracks -->
<!-- Unit -->
<string name="unit_feet">πόδια</string>
<string name="unit_kilometer">χλμ.</string>
<string name="unit_kilometer">χλμ</string>
<string name="unit_kilometer_per_hour">χλμ/ώρα</string>
<string name="unit_meter">μέτρα</string>
<string name="unit_mile">μίλι</string>
<string name="unit_mile_per_hour">μίλια/ώρα</string>
<string name="unit_minute_per_kilometer">λεπτά / χιλ.</string>
<string name="unit_minute_per_kilometer">λεπτά/χλμ</string>
<string name="unit_minute_per_mile">λεπτά/μίλι</string>
<!-- Value -->
<string name="value_integer_feet_excellent_gps">%1$d πόδια (τέλειο σήμα GPS)</string>
<string name="value_integer_feet_recommended">%1$d ft (συνιστάται)</string>
<string name="value_integer_feet_recommended">%1$d πόδια (συνιστάται)</string>
<string name="value_integer_kilometer_hour">%1$d χλμ/ώρα</string>
<string name="value_integer_meter_excellent_gps">%1$d μέτρα (τέλειο σήμα GPS)</string>
<string name="value_integer_meter_poor_gps">%1$d μέτρα (χαμηλό σήμα GPS)</string>
<string name="value_integer_meter_recommended">%1$d m (συνιστάται)</string>
<string name="value_integer_mile_hour">%1$d μίλια/ώρα</string>
<string name="value_integer_minute">%1$d λεπ.</string>
<string name="value_integer_minute">%1$d λεπ</string>
<string name="value_integer_second">%1$d δευτ</string>
<string name="value_none">Καμία</string>
<string name="value_off">Απενεργοποιημένο</string>
@@ -325,4 +325,120 @@ limitations under the License.
<string name="settings_recording_min_sampling_interval_title">Χρονικό μεσοδιάστημα δειγματοληψίας</string>
<string name="settings_recording_location_distance_summary">%1$s μεταξύ καταγεγραμμένων τοποθεσιών</string>
<string name="field_remove_from_layout">Αφαίρεση</string>
<string name="value_float_kilometer_hour">%1$.1f χλμ/ώρα</string>
<string name="value_float_kilometer_hour_recommended">%1$.1f χλμ/ώρα (συνιστάται)</string>
<string name="value_integer_knots">%1$d κόμβοι</string>
<string name="value_float_mile_hour">%1$.1f μίλια/ώρα</string>
<string name="value_float_knots">%1$.1f κόμβοι</string>
<string name="value_float_mile_hour_recommended">%1$.1f μίλια/ώρα (συνιστάται)</string>
<string name="value_float_knots_recommended">%1$.1f κόμβοι (συνιστάται)</string>
<string name="value_internal_sensor">Εσωτερικός αισθητήρας</string>
<string name="value_int_seconds">%1$d δευτ (συνιστάται)</string>
<string name="voice_per_kilometer">ανά χιλιόμετρο</string>
<string name="voice_per_mile">ανά μίλι</string>
<string name="voice_per_nautical_mile">ανά ναυτικό μίλι</string>
<string name="lap_time">Χρόνος γύρου</string>
<string name="pace">Ρυθμός</string>
<string name="lap_speed">Ταχύτητα γύρου</string>
<string name="speed">Μέση ταχύτητα κίνησης</string>
<string name="total_distance">Συνολική απόσταση</string>
<string name="voiceDistanceKilometersPlural">{n, plural, =1 {1 χιλιόμετρο} other {{n,number,#.0} χιλιόμετρα} }</string>
<string name="voiceDistanceMilesPlural">{n, plural, =1 {1 μίλι} other {{n,number,#.0} μίλια} }</string>
<string name="voiceDistanceNauticalMilesPlural">{n, plural, =1 {1 ναυτικό μίλι} other {{n,number,#.0} ναυτικά μίλια} }</string>
<string name="voiceDistance">{n, plural, =1 {1 } other {{n,number,#.0}} }</string>
<string name="voiceHoursPlural">{n, plural, =1 {1 ώρα} other {{n,number} ώρες} }</string>
<string name="settings_import_section">Εισαγωγή</string>
<string name="settings_export_section">Εξαγωγή</string>
<string name="settings_api_title">Δημόσιο API</string>
<string name="settings_public_api_enabled_title">Δημόσιο API</string>
<string name="settings_public_api_package_title">Όνομα πακέτου για Δημόσιο API</string>
<string name="settings_api_summary">API απομακρυσμένου ελέγχου και στατιστικών για άλλες εφαρμογές</string>
<string name="voiceMinutesPlural">{n, plural, =1 {1 λεπτό} other {{n,number} λεπτά} }</string>
<string name="voiceSecondsPlural">{n, plural, =1 {1 δευτερόλεπτο} other {{n,number} δευτερόλεπτα} }</string>
<string name="voiceSpeedKilometersPerHourPlural">{n, plural, =1 {1 χιλιόμετρο ανά ώρα} other {{n,number,#.0} χιλιόμετρα ανά ώρα} }</string>
<string name="voiceSpeedMilesPerHourPlural">{n, plural, =1 {1 μίλι ανά ώρα} other {{n,number,#.0} μίλια ανά ώρα} }</string>
<string name="voiceSpeedMKnotsPlural">{n, plural, =1 {1 κόμβος} other {{n,number,#.0} κόμβοι} }</string>
<string name="voiceSpeed">{n, plural, =1 {1} other {{n,number,#.0}} }</string>
<string name="description_speed_nautical">Ταχύτητα (κόμβοι)</string>
<string name="description_sensor_temperature">Θερμοκρασία (Κελσίου)</string>
<string name="export_with_photos">με φωτογραφίες</string>
<string name="export_without_photos">χωρίς φωτογραφίες</string>
<string name="export_progress_summary_errors_msg">Σφάλματα</string>
<string name="import_progress_summary_errors_msg">Σφάλματα</string>
<string name="import_progress_summary_exists_msg">Υπάρχει ήδη</string>
<string name="import_error_info">%1$s: %2$s</string>
<string name="gps_recording_status">%1$s: %2$s</string>
<string name="generic_undo">Αναίρεση</string>
<string name="generic_filter">Φίλτρο</string>
<string name="menu_filter">Φίλτρο</string>
<string name="menu_voice_rate">Ταχύτητα φωνής</string>
<string name="sensor_state_cadence_avg">Μέσο πεταλάρισμα</string>
<string name="sensor_state_cadence_max">Μέγιστο πεταλάρισμα</string>
<string name="sensor_state_temperature">Θερμόμετρο</string>
<string name="sensor_state_pressure">Βαρόμετρο</string>
<string name="bluetooth_sensor_summary">%1$s (%2$s)</string>
<string name="bluetooth_disabled">Παρακαλούμε ενεργοποιήστε το Bluetooth.</string>
<string name="settings_ui_title">Διεπαφή Χρήστη</string>
<string name="settings_ui_summary">Εμφάνιση, Διάταξη Εγγραφής, Θέμα</string>
<string name="settings_gps_sensor">GPS</string>
<string name="description_speed_source_gps">GPS</string>
<string name="settings_sensors_title">Αισθητήρες</string>
<string name="settings_sensors_summary">Αισθητήρες Ποδηλασίας, Περιφέρεια Ρόδας, Αισθητήρες Τρεξίματος</string>
<string name="settings_defaults_title">Προεπιλεγμένες Μονάδες και Δραστηριότητα</string>
<string name="settings_announcements_summary">Χρόνος, Απόσταση, Ταχύτητα Φωνής</string>
<string name="settings_import_export_title">Εισαγωγή και Εξαγωγή</string>
<string name="settings_import_export_summary">Εισαγωγή, Εξαγωγή, Αυτόματη Εξαγωγή</string>
<string name="settings_units_category">Μονάδες</string>
<string name="settings_activity_defaults_category">Προεπιλογές Δραστηριότητας</string>
<string name="settings_reset_summary">Επαναφορά Ρυθμίσεων στις Προεπιλεγμένες Τιμές</string>
<string name="generic_from">Από</string>
<string name="generic_to">Μέχρι</string>
<string name="generic_today">Σήμερα</string>
<string name="generic_yesterday">Χθες</string>
<string name="generic_dismiss">Παράβλεψη</string>
<string name="generic_completed">Ολοκληρώθηκε</string>
<string name="generic_click_twice_cancel">Πατήστε ξανά πίσω για ακύρωση</string>
<string name="generic_choose_an_option">Επιλέξτε μια επιλογή</string>
<string name="generic_skip">Παράλειψη</string>
<string name="generic_separator_done_total">/</string>
<string name="generic_show_errors">Εμφάνιση σφαλμάτων</string>
<plurals name="generic_completed_with_errors">
<item quantity="one">Ολοκληρώθηκε με ένα σφάλμα</item>
<item quantity="other">Ολοκληρώθηκε με %1$d σφάλματα</item>
</plurals>
<string name="generic_overwrite">Αντικατάσταση</string>
<string name="gps_disabled_msg">GPS απενεργοποιημένο</string>
<string name="gps_fixed_and_ready">GPS σταθερό και έτοιμο</string>
<string name="image_discard">Απόρριψη</string>
<string name="menu_select_layout">Επιλογή διάταξης</string>
<string name="custom_layout_select_layout">Επιλογή διάταξης</string>
<string name="settings_announcements_distance_frequency">Μήκος γύρου</string>
<string name="settings_locale_title">Γλώσσα</string>
<string name="settings_locale_system_default">Προεπιλογή συστήματος</string>
<string name="settings_night_mode_option_system">Σύστημα</string>
<string name="settings_night_mode_option_day">Μέρα</string>
<string name="settings_night_mode_option_night">Νύχτα</string>
<string name="custom_layout_list_title">Διατάξεις</string>
<string name="stats_custom_layout_default_layout">Προεπιλεγμένη διάταξη</string>
<string name="settings_all_reset">Επαναφορά</string>
<string name="settings_recording_fullscreen_on_while_recording_title">Πλήρης οθόνη</string>
<string name="settings_recording_show_gps_disabled_warning_title">Προειδοποίηση απενεργοποιημένου GPS</string>
<string name="settings_recording_keep_screen_on_while_recording_title">Διατήρηση της οθόνης ενεργοποιημένης</string>
<string name="settings_recording_show_on_lockscreen_while_recording_title">Εμφάνιση στατιστικών στο κλείδωμα οθόνης</string>
<string name="settings_ui_dynamic_colors_title">Δυναμικά Χρώματα</string>
<string name="settings_recording_customize_layout_title">Προσαρμόστε τη διάταξη εγγραφής</string>
<string name="settings_sensor_cycling_cadence">Πεταλάρισμα</string>
<string name="stats_sensors_cadence">Πεταλάρισμα</string>
<string name="stats_sensors_temperature">Θερμοκρασία</string>
<string name="stats_clock">Ρολόι</string>
<string name="track_recording_notification_accuracy">Ακρίβεια τοποθεσίας: %1$s</string>
<string name="track_distance_notification">Απόσταση: %1$s</string>
<string name="track_speed_notification">Ταχύτητα: %1$s</string>
<string name="track_pace_notification">Ρυθμός: %1$s</string>
<string name="stats_coordinates">Γεωγραφικό πλάτος/Γεωγραφικό μήκος</string>
<string name="settings_sensor_running_speed_and_cadence">Απόσταση, Ταχύτητα και Πεταλάρισμα</string>
<string name="settings_sensor_cycling_distance_speed">Απόσταση και Ταχύτητα</string>
<string name="settings_sensor_bluetooth_service_filter_title">Φιλτράτισμα Συσκευών Bluetooth</string>
<string name="settings_sensor_wheel_circumference">Περιφέρεια Ρόδας (mm)</string>
<string name="settings_public_api_dashboard_enabled_title">Αυτόματη μεταφορά δεδομένων</string>
</resources>
+1
View File
@@ -599,4 +599,5 @@ limitations under the License.
<string name="description_sensor_temperature">Temperatuur (Celsius)</string>
<string name="sensor_state_temperature">Termomeeter</string>
<string name="stats_sensors_temperature">Temperatuur</string>
<string name="activity_type_gravel_biking">rattasõit kruusateedel</string>
</resources>
+1
View File
@@ -607,4 +607,5 @@ limitations under the License.
<string name="stats_sensors_temperature">Temperatura</string>
<string name="description_sensor_temperature">Temperatura (Celsius)</string>
<string name="sensor_state_temperature">Termómetro</string>
<string name="activity_type_gravel_biking">ciclismo por grava</string>
</resources>
+21 -1
View File
@@ -150,7 +150,7 @@ limitations under the License.
<string name="menu_aggregated_statistics">集計統計</string>
<string name="menu_delete">削除</string>
<string name="menu_edit">編集</string>
<string name="menu_export_all">すべてエクスポート</string>
<string name="menu_export_all">すべてエクスポート(トラックごとに別ファイル)</string>
<string name="menu_help">ヘルプ</string>
<string name="menu_import_all">すべてインポート</string>
<string name="menu_insert_marker">マーカーを追加</string>
@@ -313,4 +313,24 @@ limitations under the License.
<string name="import_progress_summary_ok_msg">インポート完了</string>
<string name="import_progress_summary_exists_msg">既に存在しています</string>
<string name="help_track_map_content">小さな地図のアイコンを使います.\n a) (live updates) <a href="https://codeberg.org/OpenTracksApp/OSMDashboard">OSMDashboard</a>をインストールして下さい。 <a href="https://www.openstreetmap.org">OpenStreetMap</a> を利用したオンラインまたはオフラインの地図を使用できるようになります。\n実際、OSMDashbordは (一時的に保存された) 表示するトラッキングデータにだけアクセスする独立したアプリケーションです。\n b) 一般的な地図アプリケーションを使用 (e.g., <a href="http://osmand.net/">OsmAnd</a> and <a href="https://maps.me/">MAPS.ME</a>) 。 KMZフォーマットをサポートしている必要があります (位置及びタイムスタンプのみを含む)。 加えて、 MIME <i>application/vnd.google-earth.kmz</i> の <i>ACTION_VIEW</i> をサポートしている必要もあります。</string>
<string name="help_storage_pictures_title">マーカーとして撮影した写真の保存場所</string>
<string name="help_storage_pictures_content">アプリケーションの内部ストレージ(つまり、OpenTracks内部からのみアクセス可能な場所)に保存されます。</string>
<string name="help_track_export_title">記録したトラックのエクスポート/共有方法</string>
<string name="stats_sensors_temperature">温度</string>
<string name="settings_announcements_average_heart_rate">平均心拍数</string>
<string name="description_speed_source_gps">GPS</string>
<string name="export_dir_not_writable">" (書き込み失敗!)"</string>
<string name="export_progress_summary_overwrite_msg">上書きされたファイル</string>
<string name="export_progress_summary_skip_msg">スキップされたファイル</string>
<string name="export_progress_summary_errors_msg">エラー</string>
<string name="generic_separator_done_total">/</string>
<string name="generic_today">今日</string>
<string name="generic_yesterday">昨日</string>
<string name="gps_disabled_msg">GPSが無効化されています</string>
<string name="import_no_kml_file_found">KMZ中にkmlファイルが見つかりませんでした</string>
<string name="import_error_info">%1$s: %2$s</string>
<string name="create_marker_error">マーカーを作成することができませんでした。もう一度やり直して下さい。</string>
<string name="menu_export_all_one_file">すべてエクスポート(全てのトラックを1つのファイルに)</string>
<string name="menu_select_layout">レイアウトを選択</string>
<string name="permission_gps_failed">OpenTracksはGPSにアクセスする権限が必要です。</string>
</resources>
+4
View File
@@ -588,4 +588,8 @@ limitations under the License.
<string name="stats_sensors_temperature">Temperatur</string>
<string name="sensor_state_temperature">Termometer</string>
<string name="description_sensor_temperature">Temperatur (Celsius)</string>
<string name="activity_type_gravel_biking">gravelsykling</string>
<string name="settings_night_mode_option_day_dynamic">Dag (DynamicColor)</string>
<string name="settings_night_mode_option_night_dynamic">Natt (DynamicColor)</string>
<string name="settings_night_mode_option_night_oled_dynamic">OLED-vennlig (DynamicColor)</string>
</resources>
+1
View File
@@ -603,4 +603,5 @@ limitations under the License.
<string name="description_sensor_temperature">Температура (Цельсій)</string>
<string name="sensor_state_temperature">Термометр</string>
<string name="stats_sensors_temperature">Температура</string>
<string name="activity_type_gravel_biking">гравійний велосипед</string>
</resources>
+2 -1
View File
@@ -17,7 +17,7 @@ limitations under the License.
<!-- Activity Type -->
<string name="activity_type_airplane">坐飞机</string>
<string name="activity_type_atv">全地形越野</string>
<string name="activity_type_biking">自行车</string>
<string name="activity_type_biking">骑行</string>
<string name="activity_type_blimp">小型飞艇游览</string>
<string name="activity_type_boat">船</string>
<string name="activity_type_climbing">攀岩</string>
@@ -633,4 +633,5 @@ limitations under the License.
<string name="description_sensor_temperature">温度(摄氏度)</string>
<string name="sensor_state_temperature">温度计</string>
<string name="stats_sensors_temperature">温度</string>
<string name="activity_type_gravel_biking">砾石骑行</string>
</resources>
+1
View File
@@ -83,6 +83,7 @@ limitations under the License.
<string name="activity_type_airplane">airplane</string>
<string name="activity_type_atv">ATV</string>
<string name="activity_type_biking">biking</string>
<string name="activity_type_gravel_biking">gravel biking</string>
<string name="activity_type_blimp">blimp</string>
<string name="activity_type_boat">boat</string>
<string name="activity_type_climbing">climbing</string>