forked from upstream-mirrors/OpenTracks
Compare commits
32 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| d043842901 | |||
| cefb6b3abf | |||
| 3ee6d43e3f | |||
| 2edd9a1f05 | |||
| f9a4fcfa0d | |||
| 6bed9c5ddf | |||
| 4e400c60f6 | |||
| ca6f973360 | |||
| edb0e7f7bb | |||
| 876b013c2e | |||
| 7fb27cbe95 | |||
| 6d519e2fc1 | |||
| c2f3b50767 | |||
| a95c6a1857 | |||
| f45dad167b | |||
| 88c75d31a8 | |||
| d49e7749f3 | |||
| 8d3e953220 | |||
| 6566904906 | |||
| 1d0e8f59b7 | |||
| be7987f88a | |||
| a92a1e7838 | |||
| 8cba53cfe4 | |||
| fcedb9c444 | |||
| 2ec82af8f1 | |||
| 447d1bb7a3 | |||
| 2f6765e45f | |||
| 90bf2e6ab0 | |||
| d12423522e | |||
| 24f052491b | |||
| 6b4e7d7d9b | |||
| 00fcbb070a |
+6
-6
@@ -6,7 +6,7 @@ buildscript {
|
||||
mavenCentral()
|
||||
}
|
||||
dependencies {
|
||||
classpath 'com.android.tools.build:gradle:9.0.0'
|
||||
classpath 'com.android.tools.build:gradle:9.1.0'
|
||||
}
|
||||
}
|
||||
|
||||
@@ -47,8 +47,8 @@ android {
|
||||
|
||||
defaultConfig {
|
||||
applicationId 'de.dennisguse.opentracks'
|
||||
versionCode 6564
|
||||
versionName "v4.25.2"
|
||||
versionCode 6627
|
||||
versionName "v4.26.2"
|
||||
|
||||
buildConfigField "String", "VERSION_NAME_FULL", "\"${getVersionName()}\""
|
||||
|
||||
@@ -154,10 +154,10 @@ dependencies {
|
||||
implementation 'androidx.gridlayout:gridlayout:1.1.0'
|
||||
implementation 'com.google.android.material:material:1.13.0'
|
||||
implementation 'androidx.constraintlayout:constraintlayout:2.2.1'
|
||||
implementation 'androidx.core:core:1.17.0'
|
||||
implementation 'androidx.core:core:1.18.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-beta01'
|
||||
implementation 'androidx.core:core-location-altitude:1.0.0-rc01'
|
||||
implementation 'androidx.work:work-runtime:2.11.1'
|
||||
|
||||
androidTestImplementation 'androidx.test:core:1.7.0'
|
||||
@@ -165,7 +165,7 @@ dependencies {
|
||||
androidTestImplementation 'androidx.test:rules:1.7.0'
|
||||
androidTestImplementation 'androidx.test:runner:1.7.0'
|
||||
androidTestImplementation 'androidx.test.espresso:espresso-core:3.7.0'
|
||||
androidTestImplementation 'org.mockito:mockito-android:5.21.0'
|
||||
androidTestImplementation 'org.mockito:mockito-android:5.23.0'
|
||||
|
||||
androidTestUtil 'androidx.test:orchestrator:1.6.1'
|
||||
}
|
||||
|
||||
@@ -0,0 +1,10 @@
|
||||
(versionName): OpenTracks
|
||||
|
||||
Ændringer:
|
||||
-
|
||||
|
||||
Fejlrettelser:
|
||||
-
|
||||
|
||||
Udvikler:
|
||||
-
|
||||
@@ -0,0 +1,12 @@
|
||||
v4.26.0: OpenTracks
|
||||
|
||||
Changes:
|
||||
- GPS is optional: configurable in the settings
|
||||
- BLE sensor address/name is only shown after successful connection
|
||||
|
||||
Bugfix:
|
||||
- Altitude is now shown again in EGM2008 (introduced in v4.25.0)
|
||||
- Altitude and speed were shown as 0.0 briefly when data was stored
|
||||
|
||||
Developer:
|
||||
- Refactored sensor subsystem
|
||||
@@ -0,0 +1,6 @@
|
||||
v4.26.1: OpenTracks
|
||||
|
||||
Bugfix:
|
||||
- Crash when deleting a UI layout
|
||||
- Crash when heartrate sensor data could not be decoded
|
||||
- Crash when exporting zero tracks
|
||||
@@ -0,0 +1,5 @@
|
||||
v4.26.2: OpenTracks
|
||||
|
||||
Bugfix:
|
||||
- GPS via Bluetooth is not (yet) supported
|
||||
- Altitude type (EGM2008) wasn't shown
|
||||
@@ -0,0 +1,22 @@
|
||||
Un tracker sportivo che rispetta la tua privacy.
|
||||
|
||||
Sport e attività all'aria aperta portano a felicità, allenamento e fiducia
|
||||
|
||||
Valorizza la tua salute tenendo traccia dei tuoi progressi.
|
||||
Registra quando stai correndo o passeggiando e regalati un monitor più grande quando fai ciclismo.
|
||||
Segna con fotografie i posti interessanti incrociati durante il tragitto.
|
||||
Tieni traccia delle statistiche in dettaglio per analizzarle.
|
||||
Condividi solo i dati che vuoi.
|
||||
|
||||
* Annunci vocali.
|
||||
* Supporto a sensori Bluetooth LE: battito cardiaco, velocità e distanza (ciclismo), cadenza (ciclismo), rilevamento potenza (ciclismo).
|
||||
* Altitudine positiva o negativa tramite sensore barometrico.
|
||||
* Altitudine mostrata in EGM2008 (sopra il livello del mare) ed esportata come WGS84.
|
||||
* Esporta tracce come KMZ (con foto incluse), KML o GPX.
|
||||
* Non richiede accesso a internet o altri permessi.
|
||||
* Temi chiaro e scuro, o seguendo la impostazioni di sistema.
|
||||
* Nessuna pubblicità.
|
||||
|
||||
Libre software / Free Software / Open Source
|
||||
cioè puoi usare, studiare, cambiare e condividere il codice sorgente.
|
||||
Licenza: Apache 2.0
|
||||
+1
-1
@@ -1,6 +1,6 @@
|
||||
distributionBase=GRADLE_USER_HOME
|
||||
distributionPath=wrapper/dists
|
||||
distributionUrl=https\://services.gradle.org/distributions/gradle-9.3.1-bin.zip
|
||||
distributionUrl=https\://services.gradle.org/distributions/gradle-9.4.0-bin.zip
|
||||
networkTimeout=10000
|
||||
validateDistributionUrl=true
|
||||
zipStoreBase=GRADLE_USER_HOME
|
||||
|
||||
+29
-7
@@ -89,12 +89,12 @@ import de.dennisguse.opentracks.settings.PreferencesUtils;
|
||||
public class ExportImportTest {
|
||||
|
||||
@Rule
|
||||
public final ServiceTestRule mServiceRule = ServiceTestRule.withTimeout(50, TimeUnit.SECONDS); //TODO 5s
|
||||
public final ServiceTestRule mServiceRule = ServiceTestRule.withTimeout(5, TimeUnit.SECONDS);
|
||||
|
||||
@Rule
|
||||
public GrantPermissionRule mGrantPermissionRule = TestUtil.createGrantPermissionRule();
|
||||
|
||||
//For csv_export_only() as we the timezone is hardcoded in the expectation.
|
||||
//For csv_export_only() as the timezone is hardcoded in the expectation.
|
||||
@Rule
|
||||
public TimezoneRule timezoneRule = new TimezoneRule(TimeZone.getTimeZone("Europe/Berlin"));
|
||||
|
||||
@@ -199,7 +199,6 @@ public class ExportImportTest {
|
||||
));
|
||||
|
||||
trackPointCreator.setClock("2020-02-02T02:02:18Z");
|
||||
trackPointCreator.getSensorManager().clear();
|
||||
service.endCurrentTrack();
|
||||
|
||||
trackPointCreator.setClock("2020-02-02T02:03:20Z");
|
||||
@@ -214,7 +213,6 @@ public class ExportImportTest {
|
||||
|
||||
sendLocation(trackPointCreator, "2020-02-02T02:03:50Z", 3.1234567, 16.001, 10, 27, 15, 999.123, 0f);
|
||||
|
||||
trackPointCreator.getSensorManager().clear();
|
||||
trackPointCreator.setClock("2020-02-02T02:04:00Z");
|
||||
service.endCurrentTrack();
|
||||
|
||||
@@ -298,7 +296,22 @@ public class ExportImportTest {
|
||||
Power.of(50),
|
||||
new AltitudeGainLoss(0, 0)
|
||||
),
|
||||
new TrackPoint(TrackPoint.Type.SEGMENT_END_MANUAL, Instant.parse("2020-02-02T02:02:18Z")),
|
||||
new TrackPoint(
|
||||
null,
|
||||
TrackPoint.Type.SEGMENT_END_MANUAL,
|
||||
new Position(
|
||||
Instant.parse("2020-02-02T02:02:18Z"),
|
||||
3.123456, 14.001456, Distance.of(10),
|
||||
Altitude.WGS84.of(1020.25), null,
|
||||
null,
|
||||
Speed.of(5)),
|
||||
Distance.of(0),
|
||||
HeartRate.of(69),
|
||||
null,
|
||||
Cadence.of(3),
|
||||
Power.of(50),
|
||||
new AltitudeGainLoss(0, 0)
|
||||
),
|
||||
new TrackPoint(TrackPoint.Type.SEGMENT_START_MANUAL, Instant.parse("2020-02-02T02:03:20Z")),
|
||||
new TrackPoint(
|
||||
null,
|
||||
@@ -356,7 +369,16 @@ public class ExportImportTest {
|
||||
null,
|
||||
null,
|
||||
new AltitudeGainLoss(0, 0)),
|
||||
new TrackPoint(TrackPoint.Type.SEGMENT_END_MANUAL, Instant.parse("2020-02-02T02:04:00Z"))
|
||||
new TrackPoint(
|
||||
null,
|
||||
TrackPoint.Type.SEGMENT_END_MANUAL,
|
||||
Position.of(Instant.parse("2020-02-02T02:04:00Z")),
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
new AltitudeGainLoss(0, 0))
|
||||
), actual);
|
||||
|
||||
assertEquals(new Statistics(
|
||||
@@ -747,7 +769,7 @@ public class ExportImportTest {
|
||||
sensorManager.getAltitudeChangeHandler().setAggregator(aggregatorBarometer);
|
||||
}
|
||||
|
||||
private void sendLocation(TrackPointCreator trackPointCreator, String time, double latitude, double longitude, float accuracy, float verticalAccuracy, float speed, double altitude, Float altitudeGain) {
|
||||
public static void sendLocation(TrackPointCreator trackPointCreator, String time, double latitude, double longitude, float accuracy, float verticalAccuracy, float speed, double altitude, Float altitudeGain) {
|
||||
Location location = new Location("mock");
|
||||
location.setLatitude(latitude);
|
||||
location.setLongitude(longitude);
|
||||
|
||||
@@ -26,6 +26,7 @@ public class TrackPointAssert {
|
||||
|
||||
Assert.assertEquals("has altitude", expected.position().hasAltitude(), actual.position().hasAltitude());
|
||||
if (expected.position().hasAltitude()) {
|
||||
Assert.assertEquals("altitude", expected.position().altitude().getClass(), actual.position().altitude().getClass());
|
||||
Assert.assertEquals("altitude", expected.position().altitude().toM(), actual.position().altitude().toM(), delta);
|
||||
}
|
||||
|
||||
|
||||
@@ -14,7 +14,7 @@ import androidx.test.ext.junit.runners.AndroidJUnit4;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
|
||||
import de.dennisguse.opentracks.data.ContentProviderUtils;
|
||||
import de.dennisguse.opentracks.data.CustomContentProvider;
|
||||
|
||||
@RunWith(AndroidJUnit4.class)
|
||||
public class DataProviderTest {
|
||||
@@ -24,7 +24,7 @@ public class DataProviderTest {
|
||||
@Test
|
||||
public void supportsVersion2_tracks() {
|
||||
// given
|
||||
Uri uri = Uri.parse(ContentProviderUtils.CONTENT_BASE_URI + "/dashboard/tracks/1");
|
||||
Uri uri = Uri.parse(CustomContentProvider.CONTENT_BASE_URI + "/dashboard/tracks/1");
|
||||
String[] expectedTracksColumns = new String[]{
|
||||
"_id",
|
||||
"name",
|
||||
@@ -64,7 +64,7 @@ public class DataProviderTest {
|
||||
@Test
|
||||
public void supportsVersion2_trackpoints() {
|
||||
// given
|
||||
Uri uri = Uri.parse(ContentProviderUtils.CONTENT_BASE_URI + "/dashboard/trackpoints/1");
|
||||
Uri uri = Uri.parse(CustomContentProvider.CONTENT_BASE_URI + "/dashboard/trackpoints/1");
|
||||
String[] expectedTrackpointsColumns = {
|
||||
"_id",
|
||||
"trackid",
|
||||
@@ -94,7 +94,7 @@ public class DataProviderTest {
|
||||
@Test
|
||||
public void supportsVersion2_markers() {
|
||||
// given
|
||||
Uri uri = Uri.parse(ContentProviderUtils.CONTENT_BASE_URI + "/dashboard/markers/1");
|
||||
Uri uri = Uri.parse(CustomContentProvider.CONTENT_BASE_URI + "/dashboard/markers/1");
|
||||
String[] expectedMarkersColumns = {
|
||||
"_id",
|
||||
"name",
|
||||
@@ -126,7 +126,7 @@ public class DataProviderTest {
|
||||
@Test
|
||||
public void supportsVersion3_tracks() {
|
||||
// given
|
||||
Uri uri = Uri.parse(ContentProviderUtils.CONTENT_BASE_URI + "/dashboard/tracks/1");
|
||||
Uri uri = Uri.parse(CustomContentProvider.CONTENT_BASE_URI + "/dashboard/tracks/1");
|
||||
String[] expectedTracksColumns = {
|
||||
"_id",
|
||||
"name",
|
||||
@@ -164,7 +164,7 @@ public class DataProviderTest {
|
||||
@Test
|
||||
public void supportsVersion3_trackpoints() {
|
||||
//given
|
||||
Uri uri = Uri.parse(ContentProviderUtils.CONTENT_BASE_URI + "/dashboard/trackpoints/1");
|
||||
Uri uri = Uri.parse(CustomContentProvider.CONTENT_BASE_URI + "/dashboard/trackpoints/1");
|
||||
String[] expectedTrackpointsColumns = {
|
||||
"_id",
|
||||
"trackid",
|
||||
@@ -194,7 +194,7 @@ public class DataProviderTest {
|
||||
@Test
|
||||
public void supportsVersion3_markers() {
|
||||
// given
|
||||
Uri uri = Uri.parse(ContentProviderUtils.CONTENT_BASE_URI + "/dashboard/markers/1");
|
||||
Uri uri = Uri.parse(CustomContentProvider.CONTENT_BASE_URI + "/dashboard/markers/1");
|
||||
String[] expectedMarkersColumns = {
|
||||
"_id",
|
||||
"name",
|
||||
|
||||
+1
-1
@@ -135,7 +135,7 @@ public class TrackRecordingServiceStateMachineTest {
|
||||
// then
|
||||
assertEquals(TrackRecordingService.STATUS_DEFAULT, service.getRecordingStatusObservable().getValue());
|
||||
assertEquals(RecordingData.NOT_RECORDING, service.getRecordingDataObservable().getValue());
|
||||
// assertEquals(GpsStatusValue.GPS_NONE, service.getGpsStatusObservable().getValue()); TODO BUG: GPS can only be stopped if a service is recording.
|
||||
assertEquals(GpsStatusValue.GPS_NONE, service.getGpsStatusObservable().getValue());
|
||||
}
|
||||
|
||||
@MediumTest
|
||||
|
||||
+112
@@ -0,0 +1,112 @@
|
||||
package de.dennisguse.opentracks.services;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static de.dennisguse.opentracks.services.TrackRecordingService.RECORDING_DATA_UPDATE_INTERVAL;
|
||||
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.os.Looper;
|
||||
|
||||
import androidx.core.location.LocationManagerCompat;
|
||||
import androidx.test.core.app.ApplicationProvider;
|
||||
import androidx.test.ext.junit.runners.AndroidJUnit4;
|
||||
import androidx.test.rule.GrantPermissionRule;
|
||||
import androidx.test.rule.ServiceTestRule;
|
||||
|
||||
import org.junit.BeforeClass;
|
||||
import org.junit.Rule;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
|
||||
import java.time.Instant;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import java.util.concurrent.TimeoutException;
|
||||
|
||||
import de.dennisguse.opentracks.TestUtil;
|
||||
import de.dennisguse.opentracks.data.models.Altitude;
|
||||
import de.dennisguse.opentracks.data.models.AltitudeGainLoss;
|
||||
import de.dennisguse.opentracks.data.models.Position;
|
||||
import de.dennisguse.opentracks.data.models.Speed;
|
||||
import de.dennisguse.opentracks.data.models.TrackPoint;
|
||||
import de.dennisguse.opentracks.io.file.importer.ExportImportTest;
|
||||
import de.dennisguse.opentracks.io.file.importer.TrackPointAssert;
|
||||
import de.dennisguse.opentracks.sensors.driver.GpsInternal;
|
||||
import de.dennisguse.opentracks.services.handlers.TrackPointCreator;
|
||||
|
||||
@RunWith(AndroidJUnit4.class)
|
||||
public class TrackRecordingServiceUITest {
|
||||
|
||||
@Rule
|
||||
public final ServiceTestRule mServiceRule = ServiceTestRule.withTimeout(5, TimeUnit.SECONDS);
|
||||
|
||||
@Rule
|
||||
public GrantPermissionRule mGrantPermissionRule = TestUtil.createGrantPermissionRule();
|
||||
|
||||
private static final Context context = ApplicationProvider.getApplicationContext();
|
||||
|
||||
@BeforeClass
|
||||
public static void preSetUp() {
|
||||
// Prepare looper for Android's message queue
|
||||
if (Looper.myLooper() == null) Looper.prepare();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void getDataForUI_gps() throws TimeoutException, InterruptedException {
|
||||
TrackRecordingService service = ((TrackRecordingService.Binder) mServiceRule.bindService(new Intent(context, TrackRecordingService.class)))
|
||||
.getService();
|
||||
RecordingData recordingData;
|
||||
|
||||
// given
|
||||
TrackPointCreator trackPointCreator = service.getTrackPointCreator();
|
||||
|
||||
trackPointCreator.setClock("2020-02-02T02:02:02Z");
|
||||
|
||||
// when
|
||||
service.startNewTrack();
|
||||
//We do not want the real GPS to interfere.
|
||||
GpsInternal gpsInternal = (GpsInternal)trackPointCreator.getSensorManager().getGpsHandler().driver;
|
||||
LocationManagerCompat.removeUpdates(gpsInternal.locationManager, gpsInternal.locationListenerCompat);
|
||||
Thread.sleep(RECORDING_DATA_UPDATE_INTERVAL.toMillis());
|
||||
|
||||
// then
|
||||
assertEquals(new TrackPoint(
|
||||
null,
|
||||
TrackPoint.Type.TRACKPOINT,
|
||||
Position.of( Instant.parse("2020-02-02T02:02:02Z")),
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
null
|
||||
),
|
||||
service.getRecordingDataObservable().getValue().latestTrackPoint());
|
||||
|
||||
|
||||
// when
|
||||
// In this step, the data is saved (and Aggregator.resetAggregated called), but data should still be shown in the UI.
|
||||
trackPointCreator.setClock("2020-02-02T02:02:03Z");
|
||||
ExportImportTest.sendLocation(trackPointCreator, "2020-02-02T02:02:03Z", 3.1234567, 14.0014567, 10, 13, 15, 1020.25, 1f);
|
||||
|
||||
// then
|
||||
Thread.sleep(RECORDING_DATA_UPDATE_INTERVAL.toMillis());
|
||||
recordingData = service.getRecordingDataObservable().getValue();
|
||||
new TrackPointAssert().assertEquals(new TrackPoint(
|
||||
null,
|
||||
TrackPoint.Type.TRACKPOINT,
|
||||
new Position(
|
||||
Instant.parse("2020-02-02T02:02:03Z"),
|
||||
3.1234567, 14.0014567, null,
|
||||
Altitude.EGM2008.of(1013.05), null,
|
||||
null,
|
||||
Speed.of(15)),
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
new AltitudeGainLoss(1, 1)
|
||||
),
|
||||
recordingData.latestTrackPoint());
|
||||
}
|
||||
}
|
||||
@@ -4,10 +4,10 @@
|
||||
"2020-02-02T03:02:04+01:00","TRACKPOINT",,,,,,54,1,1,10,66,3,50
|
||||
"2020-02-02T03:02:15+01:00","TRACKPOINT",,,,,,,,,,68,3,50
|
||||
"2020-02-02T03:02:17+01:00","TRACKPOINT",3.123456,14.001456,1020.2,10,,18,0,0,2,69,3,50
|
||||
"2020-02-02T03:02:18+01:00","SEGMENT_END_MANUAL",,,,,,,,,,,,
|
||||
"2020-02-02T03:02:18+01:00","SEGMENT_END_MANUAL",3.123456,14.001456,1020.2,10,,18,0,0,0,69,3,50
|
||||
"2020-02-02T03:03:20+01:00","SEGMENT_START_MANUAL",,,,,,,,,,,,
|
||||
"2020-02-02T03:03:21+01:00","TRACKPOINT",3.123456,14.002456,999.1,10,,54,0,0,,,,
|
||||
"2020-02-02T03:03:22+01:00","SEGMENT_START_AUTOMATIC",3.123456,16,999.1,10,,54,0,0,,,,
|
||||
"2020-02-02T03:03:30+01:00","IDLE",,,,,,,0,0,,,,
|
||||
"2020-02-02T03:03:50+01:00","TRACKPOINT",3.123456,16.001,999.1,10,,54,0,0,,,,
|
||||
"2020-02-02T03:04:00+01:00","SEGMENT_END_MANUAL",,,,,,,,,,,,
|
||||
"2020-02-02T03:04:00+01:00","SEGMENT_END_MANUAL",,,,,,,0,0,,,,
|
||||
|
@@ -36,7 +36,6 @@ import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.UUID;
|
||||
|
||||
import de.dennisguse.opentracks.BuildConfig;
|
||||
import de.dennisguse.opentracks.data.models.ActivityType;
|
||||
import de.dennisguse.opentracks.data.models.Altitude;
|
||||
import de.dennisguse.opentracks.data.models.AltitudeExtremities;
|
||||
@@ -69,23 +68,10 @@ public class ContentProviderUtils {
|
||||
|
||||
private static final String TAG = ContentProviderUtils.class.getSimpleName();
|
||||
|
||||
//TODO Move to CustomContentProvider
|
||||
// The authority (the first part of the URI) for the app's content provider.
|
||||
static final String AUTHORITY_PACKAGE = BuildConfig.APPLICATION_ID + ".content";
|
||||
|
||||
//TODO Move to CustomContentProvider
|
||||
// The base URI for the app's content provider.
|
||||
public static final String CONTENT_BASE_URI = "content://" + AUTHORITY_PACKAGE;
|
||||
|
||||
private static final String ID_SEPARATOR = ",";
|
||||
|
||||
private final ContentResolver contentResolver;
|
||||
|
||||
//TODO Check if this can be used in more than one place. If not remove.
|
||||
public interface ContentProviderSelectionInterface {
|
||||
SelectionData buildSelection();
|
||||
}
|
||||
|
||||
public ContentProviderUtils(Context context) {
|
||||
contentResolver = context.getContentResolver();
|
||||
}
|
||||
@@ -755,7 +741,7 @@ public class ContentProviderUtils {
|
||||
}
|
||||
|
||||
@NonNull
|
||||
public List<AggregatedStatistic> getAggregatedStatisticsForTracks(ContentProviderSelectionInterface selection) {
|
||||
public List<AggregatedStatistic> getAggregatedStatisticsForTracks(TrackSelection selection) {
|
||||
SelectionData data = selection.buildSelection();
|
||||
try (Cursor cursor = contentResolver.query(TracksColumns.CONTENT_URI_AGGREGATED_STATISTICS, null, data.selection(), data.getSelectionArgs(), null)) {
|
||||
ArrayList<AggregatedStatistic> aggregatedStatistics = new ArrayList<>(cursor.getCount());
|
||||
|
||||
@@ -34,6 +34,7 @@ import androidx.annotation.NonNull;
|
||||
|
||||
import java.util.Arrays;
|
||||
|
||||
import de.dennisguse.opentracks.BuildConfig;
|
||||
import de.dennisguse.opentracks.data.models.TrackPoint;
|
||||
import de.dennisguse.opentracks.data.tables.MarkerColumns;
|
||||
import de.dennisguse.opentracks.data.tables.TrackPointsColumns;
|
||||
@@ -50,6 +51,11 @@ import de.dennisguse.opentracks.settings.PreferencesUtils;
|
||||
*/
|
||||
public class CustomContentProvider extends ContentProvider {
|
||||
|
||||
// The authority (the first part of the URI) for the app's content provider.
|
||||
static final String AUTHORITY_PACKAGE = BuildConfig.APPLICATION_ID + ".content";
|
||||
|
||||
// The base URI for the app's content provider.
|
||||
public static final String CONTENT_BASE_URI = "content://" + AUTHORITY_PACKAGE;
|
||||
private static final String TAG = CustomContentProvider.class.getSimpleName();
|
||||
|
||||
private static final String SQL_LIST_DELIMITER = ",";
|
||||
@@ -96,22 +102,22 @@ public class CustomContentProvider extends ContentProvider {
|
||||
|
||||
public CustomContentProvider() {
|
||||
uriMatcher = new UriMatcher(UriMatcher.NO_MATCH);
|
||||
uriMatcher.addURI(ContentProviderUtils.AUTHORITY_PACKAGE, TrackPointsColumns.CONTENT_URI_BY_ID.getPath(), UrlType.TRACKPOINTS.ordinal());
|
||||
uriMatcher.addURI(ContentProviderUtils.AUTHORITY_PACKAGE, TrackPointsColumns.CONTENT_URI_BY_ID.getPath() + "/#", UrlType.TRACKPOINTS_BY_ID.ordinal());
|
||||
uriMatcher.addURI(ContentProviderUtils.AUTHORITY_PACKAGE, TrackPointsColumns.CONTENT_URI_BY_TRACKID.getPath() + "/*", UrlType.TRACKPOINTS_BY_TRACKID.ordinal());
|
||||
uriMatcher.addURI(AUTHORITY_PACKAGE, TrackPointsColumns.CONTENT_URI_BY_ID.getPath(), UrlType.TRACKPOINTS.ordinal());
|
||||
uriMatcher.addURI(AUTHORITY_PACKAGE, TrackPointsColumns.CONTENT_URI_BY_ID.getPath() + "/#", UrlType.TRACKPOINTS_BY_ID.ordinal());
|
||||
uriMatcher.addURI(AUTHORITY_PACKAGE, TrackPointsColumns.CONTENT_URI_BY_TRACKID.getPath() + "/*", UrlType.TRACKPOINTS_BY_TRACKID.ordinal());
|
||||
|
||||
uriMatcher.addURI(ContentProviderUtils.AUTHORITY_PACKAGE, TracksColumns.CONTENT_URI.getPath(), UrlType.TRACKS.ordinal());
|
||||
uriMatcher.addURI(ContentProviderUtils.AUTHORITY_PACKAGE, TracksColumns.CONTENT_URI_SENSOR_STATS.getPath() + "/#", UrlType.TRACKS_SENSOR_STATS.ordinal());
|
||||
uriMatcher.addURI(ContentProviderUtils.AUTHORITY_PACKAGE, TracksColumns.CONTENT_URI_AGGREGATED_STATISTICS.getPath(), UrlType.TRACKS_AGGREGATED_STATISTICS.ordinal());
|
||||
uriMatcher.addURI(ContentProviderUtils.AUTHORITY_PACKAGE, TracksColumns.CONTENT_URI.getPath() + "/*", UrlType.TRACKS_BY_ID.ordinal());
|
||||
uriMatcher.addURI(AUTHORITY_PACKAGE, TracksColumns.CONTENT_URI.getPath(), UrlType.TRACKS.ordinal());
|
||||
uriMatcher.addURI(AUTHORITY_PACKAGE, TracksColumns.CONTENT_URI_SENSOR_STATS.getPath() + "/#", UrlType.TRACKS_SENSOR_STATS.ordinal());
|
||||
uriMatcher.addURI(AUTHORITY_PACKAGE, TracksColumns.CONTENT_URI_AGGREGATED_STATISTICS.getPath(), UrlType.TRACKS_AGGREGATED_STATISTICS.ordinal());
|
||||
uriMatcher.addURI(AUTHORITY_PACKAGE, TracksColumns.CONTENT_URI.getPath() + "/*", UrlType.TRACKS_BY_ID.ordinal());
|
||||
|
||||
uriMatcher.addURI(ContentProviderUtils.AUTHORITY_PACKAGE, MarkerColumns.CONTENT_URI.getPath(), UrlType.MARKERS.ordinal());
|
||||
uriMatcher.addURI(ContentProviderUtils.AUTHORITY_PACKAGE, MarkerColumns.CONTENT_URI.getPath() + "/#", UrlType.MARKERS_BY_ID.ordinal());
|
||||
uriMatcher.addURI(ContentProviderUtils.AUTHORITY_PACKAGE, MarkerColumns.CONTENT_URI_BY_TRACKID.getPath() + "/*", UrlType.MARKERS_BY_TRACKID.ordinal());
|
||||
uriMatcher.addURI(AUTHORITY_PACKAGE, MarkerColumns.CONTENT_URI.getPath(), UrlType.MARKERS.ordinal());
|
||||
uriMatcher.addURI(AUTHORITY_PACKAGE, MarkerColumns.CONTENT_URI.getPath() + "/#", UrlType.MARKERS_BY_ID.ordinal());
|
||||
uriMatcher.addURI(AUTHORITY_PACKAGE, MarkerColumns.CONTENT_URI_BY_TRACKID.getPath() + "/*", UrlType.MARKERS_BY_TRACKID.ordinal());
|
||||
|
||||
uriMatcher.addURI(ContentProviderUtils.AUTHORITY_PACKAGE, UrlType.DASHBOARD_TRACKS_BY_IDS.getPath() + "*", UrlType.DASHBOARD_TRACKS_BY_IDS.ordinal());
|
||||
uriMatcher.addURI(ContentProviderUtils.AUTHORITY_PACKAGE, UrlType.DASHBOARD_TRACKPOINTS_BY_TRACKIDS.getPath() + "*", UrlType.DASHBOARD_TRACKPOINTS_BY_TRACKIDS.ordinal());
|
||||
uriMatcher.addURI(ContentProviderUtils.AUTHORITY_PACKAGE, UrlType.DASHBOARD_MARKERS_BY_TRACKIDS.getPath() + "*", UrlType.DASHBOARD_MARKERS_BY_TRACKIDS.ordinal());
|
||||
uriMatcher.addURI(AUTHORITY_PACKAGE, UrlType.DASHBOARD_TRACKS_BY_IDS.getPath() + "*", UrlType.DASHBOARD_TRACKS_BY_IDS.ordinal());
|
||||
uriMatcher.addURI(AUTHORITY_PACKAGE, UrlType.DASHBOARD_TRACKPOINTS_BY_TRACKIDS.getPath() + "*", UrlType.DASHBOARD_TRACKPOINTS_BY_TRACKIDS.ordinal());
|
||||
uriMatcher.addURI(AUTHORITY_PACKAGE, UrlType.DASHBOARD_MARKERS_BY_TRACKIDS.getPath() + "*", UrlType.DASHBOARD_MARKERS_BY_TRACKIDS.ordinal());
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -416,9 +422,9 @@ public class CustomContentProvider extends ContentProvider {
|
||||
@Deprecated //TODO Should not be used anymore
|
||||
MARKERS_BY_TRACKID,
|
||||
|
||||
DASHBOARD_TRACKS_BY_IDS(Uri.parse(ContentProviderUtils.CONTENT_BASE_URI + "/dashboard/tracks/"), TracksColumns.CONTENT_TYPE),
|
||||
DASHBOARD_TRACKPOINTS_BY_TRACKIDS(Uri.parse(ContentProviderUtils.CONTENT_BASE_URI + "/dashboard/trackpoints/"), TrackPointsColumns.CONTENT_TYPE),
|
||||
DASHBOARD_MARKERS_BY_TRACKIDS(Uri.parse(ContentProviderUtils.CONTENT_BASE_URI + "/dashboard/markers/"), MarkerColumns.CONTENT_TYPE);
|
||||
DASHBOARD_TRACKS_BY_IDS(Uri.parse(CONTENT_BASE_URI + "/dashboard/tracks/"), TracksColumns.CONTENT_TYPE),
|
||||
DASHBOARD_TRACKPOINTS_BY_TRACKIDS(Uri.parse(CONTENT_BASE_URI + "/dashboard/trackpoints/"), TrackPointsColumns.CONTENT_TYPE),
|
||||
DASHBOARD_MARKERS_BY_TRACKIDS(Uri.parse(CONTENT_BASE_URI + "/dashboard/markers/"), MarkerColumns.CONTENT_TYPE);
|
||||
|
||||
private final Uri uri;
|
||||
|
||||
|
||||
@@ -59,12 +59,12 @@ public class ShareContentProvider extends CustomContentProvider {
|
||||
private static final String TRACKID_DELIMITER = "_";
|
||||
|
||||
static {
|
||||
uriMatcher.addURI(ContentProviderUtils.AUTHORITY_PACKAGE, TracksColumns.TABLE_NAME + "/" + TrackFileFormat.GPX.getPreferenceId() + "/*/*", URI_GPX);
|
||||
uriMatcher.addURI(CustomContentProvider.AUTHORITY_PACKAGE, TracksColumns.TABLE_NAME + "/" + TrackFileFormat.GPX.getPreferenceId() + "/*/*", URI_GPX);
|
||||
|
||||
uriMatcher.addURI(ContentProviderUtils.AUTHORITY_PACKAGE, TracksColumns.TABLE_NAME + "/" + TrackFileFormat.KML_WITH_TRACKDETAIL_AND_SENSORDATA.getPreferenceId() + "/*/*", URI_KML_WITH_TRACKDETAIL_SENSORDATA);
|
||||
uriMatcher.addURI(CustomContentProvider.AUTHORITY_PACKAGE, TracksColumns.TABLE_NAME + "/" + TrackFileFormat.KML_WITH_TRACKDETAIL_AND_SENSORDATA.getPreferenceId() + "/*/*", URI_KML_WITH_TRACKDETAIL_SENSORDATA);
|
||||
|
||||
uriMatcher.addURI(ContentProviderUtils.AUTHORITY_PACKAGE, TracksColumns.TABLE_NAME + "/" + TrackFileFormat.KMZ_WITH_TRACKDETAIL_AND_SENSORDATA_AND_PICTURES.getPreferenceId() + "/*/*", URI_KMZ_WITH_TRACKDETAIL_SENSORDATA_AND_PICTURES);
|
||||
uriMatcher.addURI(ContentProviderUtils.AUTHORITY_PACKAGE, TracksColumns.TABLE_NAME + "/" + TrackFileFormat.CSV.getPreferenceId() + "/*/*", URI_CSV);
|
||||
uriMatcher.addURI(CustomContentProvider.AUTHORITY_PACKAGE, TracksColumns.TABLE_NAME + "/" + TrackFileFormat.KMZ_WITH_TRACKDETAIL_AND_SENSORDATA_AND_PICTURES.getPreferenceId() + "/*/*", URI_KMZ_WITH_TRACKDETAIL_SENSORDATA_AND_PICTURES);
|
||||
uriMatcher.addURI(CustomContentProvider.AUTHORITY_PACKAGE, TracksColumns.TABLE_NAME + "/" + TrackFileFormat.CSV.getPreferenceId() + "/*/*", URI_CSV);
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -10,7 +10,7 @@ import java.util.List;
|
||||
import de.dennisguse.opentracks.data.models.Track;
|
||||
import de.dennisguse.opentracks.data.tables.TracksColumns;
|
||||
|
||||
public class TrackSelection implements ContentProviderUtils.ContentProviderSelectionInterface {
|
||||
public class TrackSelection {
|
||||
private final List<Track.Id> trackIds = new ArrayList<>();
|
||||
private final List<String> categories = new ArrayList<>();
|
||||
private Instant from;
|
||||
@@ -40,7 +40,6 @@ public class TrackSelection implements ContentProviderUtils.ContentProviderSelec
|
||||
return trackIds.isEmpty() && categories.isEmpty() && from == null && to == null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ContentProviderUtils.SelectionData buildSelection() {
|
||||
String selection = "";
|
||||
List<String> selectionArgs = new ArrayList<>();
|
||||
|
||||
@@ -28,7 +28,6 @@ import androidx.annotation.VisibleForTesting;
|
||||
* @author Leif Hendrik Wilden
|
||||
* @author Rodrigo Damazio
|
||||
*/
|
||||
//TODO All data should be final; no default values.
|
||||
public final class MarkerBuilder {
|
||||
|
||||
private Marker.Id id;
|
||||
@@ -57,14 +56,6 @@ public final class MarkerBuilder {
|
||||
this.position = trackPoint.position();
|
||||
}
|
||||
|
||||
public MarkerBuilder(@Nullable Track.Id trackId, @NonNull TrackPoint trackPoint, String name, String description, String typeLocalized, Uri photoUrl) {
|
||||
this(trackId, trackPoint);
|
||||
this.name = name;
|
||||
this.description = description;
|
||||
this.typeLocalized = typeLocalized;
|
||||
this.photoUrl = photoUrl;
|
||||
}
|
||||
|
||||
public MarkerBuilder(Marker marker) {
|
||||
this.id = marker.id();
|
||||
this.trackId = marker.trackId();
|
||||
|
||||
@@ -8,7 +8,7 @@ import androidx.annotation.Nullable;
|
||||
import java.time.Instant;
|
||||
|
||||
public record Position(
|
||||
@Nullable Instant time,
|
||||
@Nullable Instant time, //TODO It may be a good idea to move time out of Position
|
||||
@Nullable Double latitude,
|
||||
@Nullable Double longitude,
|
||||
@Nullable Distance horizontalAccuracy,
|
||||
@@ -17,10 +17,6 @@ public record Position(
|
||||
@Nullable Float bearing,
|
||||
@Nullable Speed speed
|
||||
) {
|
||||
@Deprecated
|
||||
public static Position empty() {
|
||||
return of(null);
|
||||
}
|
||||
|
||||
public static Position of(@NonNull Instant time) {
|
||||
return new Position(
|
||||
|
||||
@@ -20,6 +20,7 @@ import android.os.Parcelable;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.annotation.Nullable;
|
||||
import androidx.annotation.VisibleForTesting;
|
||||
|
||||
import java.time.Instant;
|
||||
|
||||
@@ -63,7 +64,7 @@ public record TrackPoint(
|
||||
);
|
||||
}
|
||||
|
||||
@Deprecated //TOOD Private?
|
||||
@VisibleForTesting
|
||||
public TrackPoint(Type type, Instant now) {
|
||||
this(
|
||||
null,
|
||||
|
||||
@@ -22,7 +22,7 @@ import android.provider.BaseColumns;
|
||||
import android.widget.ArrayAdapter;
|
||||
|
||||
import de.dennisguse.opentracks.R;
|
||||
import de.dennisguse.opentracks.data.ContentProviderUtils;
|
||||
import de.dennisguse.opentracks.data.CustomContentProvider;
|
||||
|
||||
/**
|
||||
* Constants for markers table.
|
||||
@@ -32,8 +32,8 @@ import de.dennisguse.opentracks.data.ContentProviderUtils;
|
||||
public interface MarkerColumns extends BaseColumns {
|
||||
|
||||
String TABLE_NAME = "markers";
|
||||
Uri CONTENT_URI = Uri.parse(ContentProviderUtils.CONTENT_BASE_URI + "/" + TABLE_NAME);
|
||||
Uri CONTENT_URI_BY_TRACKID = Uri.parse(ContentProviderUtils.CONTENT_BASE_URI + "/" + TABLE_NAME + "/trackid");
|
||||
Uri CONTENT_URI = Uri.parse(CustomContentProvider.CONTENT_BASE_URI + "/" + TABLE_NAME);
|
||||
Uri CONTENT_URI_BY_TRACKID = Uri.parse(CustomContentProvider.CONTENT_BASE_URI + "/" + TABLE_NAME + "/trackid");
|
||||
String CONTENT_TYPE = "vnd.android.cursor.dir/vnd.de.dennisguse.waypoint";
|
||||
String CONTENT_ITEMTYPE = "vnd.android.cursor.item/vnd.de.dennisguse.waypoint";
|
||||
String DEFAULT_SORT_ORDER = _ID;
|
||||
|
||||
@@ -19,7 +19,7 @@ package de.dennisguse.opentracks.data.tables;
|
||||
import android.net.Uri;
|
||||
import android.provider.BaseColumns;
|
||||
|
||||
import de.dennisguse.opentracks.data.ContentProviderUtils;
|
||||
import de.dennisguse.opentracks.data.CustomContentProvider;
|
||||
|
||||
/**
|
||||
* Constants for the track points table.
|
||||
@@ -29,8 +29,8 @@ import de.dennisguse.opentracks.data.ContentProviderUtils;
|
||||
public interface TrackPointsColumns extends BaseColumns {
|
||||
|
||||
String TABLE_NAME = "trackpoints";
|
||||
Uri CONTENT_URI_BY_ID = Uri.parse(ContentProviderUtils.CONTENT_BASE_URI + "/" + TABLE_NAME);
|
||||
Uri CONTENT_URI_BY_TRACKID = Uri.parse(ContentProviderUtils.CONTENT_BASE_URI + "/" + TABLE_NAME + "/trackid");
|
||||
Uri CONTENT_URI_BY_ID = Uri.parse(CustomContentProvider.CONTENT_BASE_URI + "/" + TABLE_NAME);
|
||||
Uri CONTENT_URI_BY_TRACKID = Uri.parse(CustomContentProvider.CONTENT_BASE_URI + "/" + TABLE_NAME + "/trackid");
|
||||
String CONTENT_TYPE = "vnd.android.cursor.dir/vnd.de.dennisguse.trackpoint";
|
||||
String CONTENT_ITEMTYPE = "vnd.android.cursor.item/vnd.de.dennisguse.trackpoint";
|
||||
String DEFAULT_SORT_ORDER = _ID;
|
||||
|
||||
@@ -19,7 +19,7 @@ package de.dennisguse.opentracks.data.tables;
|
||||
import android.net.Uri;
|
||||
import android.provider.BaseColumns;
|
||||
|
||||
import de.dennisguse.opentracks.data.ContentProviderUtils;
|
||||
import de.dennisguse.opentracks.data.CustomContentProvider;
|
||||
|
||||
/**
|
||||
* Constants for the tracks table.
|
||||
@@ -29,9 +29,9 @@ import de.dennisguse.opentracks.data.ContentProviderUtils;
|
||||
public interface TracksColumns extends BaseColumns {
|
||||
|
||||
String TABLE_NAME = "tracks";
|
||||
Uri CONTENT_URI = Uri.parse(ContentProviderUtils.CONTENT_BASE_URI + "/" + TABLE_NAME);
|
||||
Uri CONTENT_URI_SENSOR_STATS = Uri.parse(ContentProviderUtils.CONTENT_BASE_URI + "/" + TABLE_NAME + "/sensorstats");
|
||||
Uri CONTENT_URI_AGGREGATED_STATISTICS = Uri.parse(ContentProviderUtils.CONTENT_BASE_URI + "/" + TABLE_NAME + "/aggregated/");
|
||||
Uri CONTENT_URI = Uri.parse(CustomContentProvider.CONTENT_BASE_URI + "/" + TABLE_NAME);
|
||||
Uri CONTENT_URI_SENSOR_STATS = Uri.parse(CustomContentProvider.CONTENT_BASE_URI + "/" + TABLE_NAME + "/sensorstats");
|
||||
Uri CONTENT_URI_AGGREGATED_STATISTICS = Uri.parse(CustomContentProvider.CONTENT_BASE_URI + "/" + TABLE_NAME + "/aggregated/");
|
||||
String CONTENT_TYPE = "vnd.android.cursor.dir/vnd.de.dennisguse.track";
|
||||
String CONTENT_ITEMTYPE = "vnd.android.cursor.item/vnd.de.dennisguse.track";
|
||||
String DEFAULT_SORT_ORDER = _ID;
|
||||
|
||||
@@ -167,8 +167,11 @@ public class ExportActivity extends AbstractActivity {
|
||||
new Thread(() -> {
|
||||
directoryFiles = ExportUtils.getAllFiles(ExportActivity.this, documentFile.getUri());
|
||||
runOnUiThread(() -> {
|
||||
createExportTasks(allInOneFile);
|
||||
nextExport(null);
|
||||
if (createExportTasks(allInOneFile)) {
|
||||
nextExport(null);
|
||||
} else {
|
||||
onExportEnded();
|
||||
}
|
||||
});
|
||||
}).start();
|
||||
} else {
|
||||
@@ -217,8 +220,12 @@ public class ExportActivity extends AbstractActivity {
|
||||
exportTasks.clear();
|
||||
}
|
||||
|
||||
private void createExportTasks(boolean allInOneFile) {
|
||||
private boolean createExportTasks(boolean allInOneFile) {
|
||||
List<Track> tracks = contentProviderUtils.getTracks();
|
||||
if (tracks.isEmpty()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
exportTasks = new ArrayList<>();
|
||||
if (allInOneFile) {
|
||||
String filename = "OpenTracks-Backup";
|
||||
@@ -227,6 +234,7 @@ public class ExportActivity extends AbstractActivity {
|
||||
exportTasks.addAll(tracks.stream().map(it -> new ExportTask(null, trackFileFormat, List.of(it.id()))).toList());
|
||||
}
|
||||
trackExportTotalCount = exportTasks.size();
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -391,7 +399,7 @@ public class ExportActivity extends AbstractActivity {
|
||||
|
||||
/**
|
||||
* Try to resolve the conflict if user gave the info needed.
|
||||
* Otherwise shows the buttons and views needed to gives the user the possibility of take a decision.
|
||||
* Otherwise, shows the buttons and views needed to gives the user the possibility of take a decision.
|
||||
*
|
||||
* @return true if it could resolve the conflict or false otherwise.
|
||||
*/
|
||||
|
||||
@@ -297,7 +297,17 @@ public class GPXTrackImporter extends DefaultHandler implements XMLImporter.Trac
|
||||
}
|
||||
|
||||
if (latitude == null || longitude == null) {
|
||||
return new TrackPoint(TrackPoint.Type.TRACKPOINT, parsedTime.toInstant());
|
||||
return new TrackPoint(
|
||||
null,
|
||||
TrackPoint.Type.TRACKPOINT,
|
||||
Position.of(parsedTime.toInstant()),
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
null
|
||||
);
|
||||
}
|
||||
|
||||
double latitudeParsed;
|
||||
|
||||
@@ -110,7 +110,7 @@ public class GpsHandler extends SensorHandler<Location, Position> {
|
||||
|
||||
@Override
|
||||
protected int getSensorPreferenceKey() {
|
||||
return R.string.settings_sensor_bluetooth_gps_key;
|
||||
return R.string.settings_sensor_gps_key;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -14,7 +14,6 @@ import java.time.Instant;
|
||||
|
||||
import de.dennisguse.opentracks.data.models.Cadence;
|
||||
import de.dennisguse.opentracks.data.models.Distance;
|
||||
import de.dennisguse.opentracks.data.models.HeartRate;
|
||||
import de.dennisguse.opentracks.data.models.Position;
|
||||
import de.dennisguse.opentracks.data.models.Speed;
|
||||
import de.dennisguse.opentracks.sensors.sensorData.AggregatorCyclingCadence;
|
||||
@@ -126,6 +125,7 @@ public class SensorManager implements SharedPreferences.OnSharedPreferenceChange
|
||||
gpsHandler.resetAggregated();
|
||||
}
|
||||
|
||||
@VisibleForTesting
|
||||
public GpsHandler getGpsHandler() {
|
||||
return gpsHandler;
|
||||
}
|
||||
@@ -185,17 +185,6 @@ public class SensorManager implements SharedPreferences.OnSharedPreferenceChange
|
||||
return handler;
|
||||
}
|
||||
|
||||
private SensorData<HeartRate> getHeartRate(Instant now) {
|
||||
SensorData<HeartRate> value = heartRateHandler.getSensorData(now);
|
||||
if (value != null) {
|
||||
|
||||
//TODO Move this check into HeartRateHandler
|
||||
if (value.data().isValid()) return value;
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
private SensorData<Cadence> getCadence(Instant now) {
|
||||
{
|
||||
SensorData<Cadence> value = cyclingCadenceHandler.getSensorData(now);
|
||||
@@ -250,16 +239,19 @@ public class SensorManager implements SharedPreferences.OnSharedPreferenceChange
|
||||
}
|
||||
|
||||
public SensorDataSet getSensorDataSet(Instant now) {
|
||||
//We always need a Position with now
|
||||
SensorData<Position> gpsSensorData = gpsHandler.getSensorData(now);
|
||||
|
||||
var a = temperatureHandler.getSensorData(now);
|
||||
var b = altitudeChangeHandler.getSensorData(now);
|
||||
if (gpsSensorData != null && gpsSensorData.data() != null) {
|
||||
gpsSensorData = new SensorData<>(gpsSensorData.data().with(now), gpsSensorData.sensorNameOrAddress());
|
||||
} else {
|
||||
gpsSensorData = new SensorData<>(Position.of(now), "");
|
||||
}
|
||||
|
||||
return new SensorDataSet(
|
||||
gpsSensorData != null ? gpsSensorData : new SensorData<>(Position.of(now), ""), //We always need a Position(now)
|
||||
gpsSensorData,
|
||||
getSpeed(now),
|
||||
getDistance(now),
|
||||
getHeartRate(now),
|
||||
heartRateHandler.getSensorData(now),
|
||||
temperatureHandler.getSensorData(now),
|
||||
getCadence(now),
|
||||
cyclingPowerHandler.getSensorData(now),
|
||||
@@ -267,37 +259,6 @@ public class SensorManager implements SharedPreferences.OnSharedPreferenceChange
|
||||
);
|
||||
}
|
||||
|
||||
@VisibleForTesting
|
||||
public void clear() {
|
||||
Log.i(TAG, "Removing all aggregators");
|
||||
|
||||
//TODO This is to ExportImportTest alive; refactor test.
|
||||
if (this.altitudeChangeHandler != null) {
|
||||
this.altitudeChangeHandler.setAggregator(null);
|
||||
}
|
||||
if (this.heartRateHandler != null) {
|
||||
this.heartRateHandler.setAggregator(null);
|
||||
}
|
||||
if (this.temperatureHandler != null) {
|
||||
this.temperatureHandler.setAggregator(null);
|
||||
}
|
||||
if (this.cyclingCadenceHandler != null) {
|
||||
this.cyclingCadenceHandler.setAggregator(null);
|
||||
}
|
||||
if (cyclingDistanceSpeedHandler != null) {
|
||||
cyclingDistanceSpeedHandler.setAggregator(null);
|
||||
}
|
||||
if (cyclingPowerHandler != null) {
|
||||
cyclingPowerHandler.setAggregator(null);
|
||||
}
|
||||
if (runningSpeedCadenceDistanceHandler != null) {
|
||||
runningSpeedCadenceDistanceHandler.setAggregator(null);
|
||||
}
|
||||
if (this.gpsHandler != null) {
|
||||
this.gpsHandler.setAggregator(null);
|
||||
}
|
||||
}
|
||||
|
||||
private boolean isStarted() {
|
||||
return wakeLock != null;
|
||||
}
|
||||
|
||||
@@ -34,7 +34,7 @@ public abstract class Aggregator<InputType, OutputType> {
|
||||
previous = next;
|
||||
}
|
||||
|
||||
protected abstract void computeValue(Raw<InputType> current);
|
||||
protected abstract void computeValue(@NonNull Raw<InputType> current);
|
||||
|
||||
/**
|
||||
* @return did we process data from a sensor.
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
package de.dennisguse.opentracks.sensors.sensorData;
|
||||
|
||||
import de.dennisguse.opentracks.data.models.Altitude;
|
||||
import androidx.annotation.NonNull;
|
||||
|
||||
import de.dennisguse.opentracks.data.models.AltitudeGainLoss;
|
||||
import de.dennisguse.opentracks.data.models.AtmosphericPressure;
|
||||
import de.dennisguse.opentracks.sensors.PressureSensorUtils;
|
||||
@@ -14,7 +15,7 @@ public class AggregatorBarometer extends Aggregator<AtmosphericPressure, Altitud
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void computeValue(Raw<AtmosphericPressure> current) {
|
||||
protected void computeValue(@NonNull Raw<AtmosphericPressure> current) {
|
||||
if (previous == null) {
|
||||
lastAcceptedSensorValue = current.value();
|
||||
output = new AltitudeGainLoss(0f, 0f);
|
||||
@@ -40,6 +41,4 @@ public class AggregatorBarometer extends Aggregator<AtmosphericPressure, Altitud
|
||||
public void resetAggregated() {
|
||||
output = null;
|
||||
}
|
||||
|
||||
public record Data(Altitude gain, Altitude loss) {}
|
||||
}
|
||||
|
||||
+3
-1
@@ -2,6 +2,8 @@ package de.dennisguse.opentracks.sensors.sensorData;
|
||||
|
||||
import android.util.Log;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
|
||||
import java.time.Duration;
|
||||
|
||||
import de.dennisguse.opentracks.data.models.Cadence;
|
||||
@@ -17,7 +19,7 @@ public class AggregatorCyclingCadence extends Aggregator<CyclingCadenceBluetooth
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void computeValue(Raw<CyclingCadenceBluetooth.CrankData> current) {
|
||||
protected void computeValue(@NonNull Raw<CyclingCadenceBluetooth.CrankData> current) {
|
||||
if (previous == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
+3
-1
@@ -2,6 +2,8 @@ package de.dennisguse.opentracks.sensors.sensorData;
|
||||
|
||||
import android.util.Log;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
|
||||
import java.time.Duration;
|
||||
|
||||
import de.dennisguse.opentracks.data.models.Distance;
|
||||
@@ -20,7 +22,7 @@ public class AggregatorCyclingDistanceSpeed extends Aggregator<CyclingDistanceSp
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void computeValue(Raw<CyclingDistanceSpeedBluetooth.WheelData> current) {
|
||||
protected void computeValue(@NonNull Raw<CyclingDistanceSpeedBluetooth.WheelData> current) {
|
||||
if (previous == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
+3
-1
@@ -1,5 +1,7 @@
|
||||
package de.dennisguse.opentracks.sensors.sensorData;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
|
||||
import de.dennisguse.opentracks.data.models.Power;
|
||||
import de.dennisguse.opentracks.sensors.driver.CyclingPowerBluetooth;
|
||||
|
||||
@@ -10,7 +12,7 @@ public class AggregatorCyclingPower extends Aggregator<CyclingPowerBluetooth.Dat
|
||||
}
|
||||
|
||||
@Override
|
||||
public void computeValue(Raw<CyclingPowerBluetooth.Data> current) {
|
||||
public void computeValue(@NonNull Raw<CyclingPowerBluetooth.Data> current) {
|
||||
this.output = current.value().power();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4,8 +4,6 @@ import android.location.Location;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
|
||||
import java.time.Instant;
|
||||
|
||||
import de.dennisguse.opentracks.data.models.Position;
|
||||
|
||||
public class AggregatorGPS extends Aggregator<Location, Position> {
|
||||
@@ -15,7 +13,7 @@ public class AggregatorGPS extends Aggregator<Location, Position> {
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void computeValue(Raw<Location> current) {
|
||||
protected void computeValue(@NonNull Raw<Location> current) {
|
||||
output = Position.of(current.value(), current.time());
|
||||
}
|
||||
|
||||
@@ -24,17 +22,7 @@ public class AggregatorGPS extends Aggregator<Location, Position> {
|
||||
/*
|
||||
* GPS data is not an aggregated value, but for now we want to ensure to only save the data once.
|
||||
* The data is too large to save it more often than needed (i.e., duplicated values).
|
||||
* TODO: this behavior can be changed if TrackRecordingManager.insertTrackPoint() would strip GPS data if it was already saved. This would simplify TrackPointCreator.createCurrentTrackPoint()
|
||||
*/
|
||||
output = null;
|
||||
}
|
||||
|
||||
@NonNull
|
||||
@Override
|
||||
public Position getAggregatedValue(Instant now) {
|
||||
Position value = super.getAggregatedValue(now);
|
||||
if (value == null) value = Position.empty();
|
||||
|
||||
return value.with(now);
|
||||
resetOutdated();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
package de.dennisguse.opentracks.sensors.sensorData;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
|
||||
import de.dennisguse.opentracks.data.models.HeartRate;
|
||||
|
||||
public class AggregatorHeartRate extends Aggregator<HeartRate, HeartRate> {
|
||||
@@ -9,7 +11,7 @@ public class AggregatorHeartRate extends Aggregator<HeartRate, HeartRate> {
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void computeValue(Raw<HeartRate> current) {
|
||||
protected void computeValue(@NonNull Raw<HeartRate> current) {
|
||||
if (current.value().isValid()) {
|
||||
this.output = current.value();
|
||||
}
|
||||
|
||||
@@ -19,7 +19,7 @@ public final class AggregatorRunning extends Aggregator<RunningSpeedAndCadenceBl
|
||||
}
|
||||
|
||||
@Override
|
||||
public void computeValue(Raw<RunningSpeedAndCadenceBluetooth.Data> current) {
|
||||
public void computeValue(@NonNull Raw<RunningSpeedAndCadenceBluetooth.Data> current) {
|
||||
if (previous == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
+3
-1
@@ -1,5 +1,7 @@
|
||||
package de.dennisguse.opentracks.sensors.sensorData;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
|
||||
import java.time.Duration;
|
||||
|
||||
import de.dennisguse.opentracks.data.models.Temperature;
|
||||
@@ -10,7 +12,7 @@ public class AggregatorTemperature extends Aggregator<Temperature, Temperature>
|
||||
super(sensorAddress, sensorName);
|
||||
}
|
||||
|
||||
protected void computeValue(Raw<Temperature> current) {
|
||||
protected void computeValue(@NonNull Raw<Temperature> current) {
|
||||
this.output = current.value();
|
||||
}
|
||||
|
||||
|
||||
@@ -10,6 +10,10 @@ import de.dennisguse.opentracks.sensors.sensorData.SensorDataSet;
|
||||
*/
|
||||
public record RecordingData(
|
||||
Track track,
|
||||
/*
|
||||
* The current view on the SensorData.
|
||||
* May contain data from previous measurements that are not present in SensorDataSet.
|
||||
*/
|
||||
TrackPoint latestTrackPoint,
|
||||
SensorDataSet sensorDataSet,
|
||||
Statistics currentSegment) {
|
||||
|
||||
@@ -131,10 +131,10 @@ public class TrackRecordingManager implements SharedPreferences.OnSharedPreferen
|
||||
|
||||
RecordingData getDataForUI() {
|
||||
Pair<TrackPoint, SensorDataSet> current = trackPointCreator.createCurrentTrackPoint(lastTrackPointUIWithSpeed, lastTrackPointUIWithAltitude, lastStoredTrackPointWithLocation);
|
||||
TrackPoint trackPoint = current.first;
|
||||
TrackStatisticsUpdater tmpTrackStatisticsUpdater = new TrackStatisticsUpdater(trackStatisticsUpdater, trackPoint);
|
||||
|
||||
TrackStatisticsUpdater tmpTrackStatisticsUpdater = new TrackStatisticsUpdater(trackStatisticsUpdater, current.first);
|
||||
|
||||
ALTITUDE_CORRECTION_MANAGER.correctAltitude(context, current.first);
|
||||
TrackPoint TrackPointWithEGM2008 = ALTITUDE_CORRECTION_MANAGER.correctAltitude(context, trackPoint);
|
||||
|
||||
Track track = contentProviderUtils.getTrack(trackId); //Get copy TODO: Avoid re-loading data from DB
|
||||
if (track == null) {
|
||||
@@ -145,7 +145,7 @@ public class TrackRecordingManager implements SharedPreferences.OnSharedPreferen
|
||||
TrackBuilder trackBuilder = new TrackBuilder(track);
|
||||
trackBuilder.setStatistics(tmpTrackStatisticsUpdater.getTrackStatistics());
|
||||
|
||||
return new RecordingData(trackBuilder.getTrack(), current.first, current.second, tmpTrackStatisticsUpdater.getCurrentSegment());
|
||||
return new RecordingData(trackBuilder.getTrack(), TrackPointWithEGM2008, current.second, tmpTrackStatisticsUpdater.getCurrentSegment());
|
||||
}
|
||||
|
||||
@VisibleForTesting(otherwise = VisibleForTesting.PACKAGE_PRIVATE)
|
||||
|
||||
@@ -48,7 +48,8 @@ public class TrackRecordingService extends Service implements TrackPointCreator.
|
||||
|
||||
private static final String TAG = TrackRecordingService.class.getSimpleName();
|
||||
|
||||
private static final Duration RECORDING_DATA_UPDATE_INTERVAL = Duration.ofSeconds(1);
|
||||
@VisibleForTesting
|
||||
public static final Duration RECORDING_DATA_UPDATE_INTERVAL = Duration.ofSeconds(1);
|
||||
|
||||
public static final RecordingStatus STATUS_DEFAULT = RecordingStatus.notRecording();
|
||||
public static final GpsStatusValue STATUS_GPS_DEFAULT = GpsStatusValue.GPS_NONE;
|
||||
|
||||
@@ -102,10 +102,10 @@ public class TrackPointCreator implements SharedPreferences.OnSharedPreferenceCh
|
||||
}
|
||||
|
||||
if (lastTrackPointUISpeed != null)
|
||||
position.with(lastTrackPointUISpeed.position().speed());
|
||||
position = position.with(lastTrackPointUISpeed.position().speed());
|
||||
|
||||
if (lastTrackPointUIAltitude != null)
|
||||
position.with(lastTrackPointUIAltitude.position().altitude());
|
||||
position = position.with(lastTrackPointUIAltitude.position().altitude());
|
||||
|
||||
TrackPoint currentTrackPoint = new TrackPoint(
|
||||
null,
|
||||
|
||||
-41
@@ -1,41 +0,0 @@
|
||||
package de.dennisguse.opentracks.settings.bluetooth;
|
||||
|
||||
import android.content.Context;
|
||||
import android.location.LocationManager;
|
||||
import android.util.AttributeSet;
|
||||
|
||||
import androidx.core.location.LocationManagerCompat;
|
||||
import androidx.preference.PreferenceDialogFragmentCompat;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import de.dennisguse.opentracks.sensors.driver.GpsInternal;
|
||||
|
||||
public class BluetoothLeGpsPreference extends BluetoothLeSensorPreference {
|
||||
|
||||
public BluetoothLeGpsPreference(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
|
||||
super(context, attrs, defStyleAttr, defStyleRes);
|
||||
}
|
||||
|
||||
public BluetoothLeGpsPreference(Context context, AttributeSet attrs, int defStyleAttr) {
|
||||
super(context, attrs, defStyleAttr);
|
||||
}
|
||||
|
||||
public BluetoothLeGpsPreference(Context context, AttributeSet attrs) {
|
||||
super(context, attrs);
|
||||
}
|
||||
|
||||
public BluetoothLeGpsPreference(Context context) {
|
||||
super(context);
|
||||
}
|
||||
|
||||
@Override
|
||||
public PreferenceDialogFragmentCompat createInstance() {
|
||||
boolean hasInternalSensor = LocationManagerCompat.hasProvider((LocationManager) getContext().getSystemService(Context.LOCATION_SERVICE), GpsInternal.LOCATION_PROVIDER);
|
||||
|
||||
return BluetoothLeSensorPreferenceDialog
|
||||
.newInstance(getKey(), List.of(), hasInternalSensor);
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
+2
-1
@@ -5,6 +5,7 @@ import android.util.Log;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
@@ -22,7 +23,7 @@ public class RecordingLayoutIO {
|
||||
public static List<RecordingLayout> fromCsv(@NonNull String csv, @NonNull Resources resources) {
|
||||
return CsvLayoutUtils.getCsvLines(csv).stream()
|
||||
.map(line -> RecordingLayoutIO.fromCsvLine(line, resources))
|
||||
.toList();
|
||||
.collect(Collectors.toCollection(ArrayList::new));
|
||||
}
|
||||
|
||||
public static RecordingLayout fromCsvLine(@NonNull String csvLine, @NonNull Resources resources) {
|
||||
|
||||
@@ -244,22 +244,6 @@ public abstract class GenericStatisticsViewHolder extends StatisticViewHolder<St
|
||||
}
|
||||
}
|
||||
|
||||
public static class Altitude extends GenericStatisticsViewHolder {
|
||||
|
||||
@Override
|
||||
public void onChanged(UnitSystem unitSystem, RecordingData data) {
|
||||
TrackPoint latestTrackPoint = data.latestTrackPoint();
|
||||
Float altitude = latestTrackPoint != null && latestTrackPoint.position().hasAltitude() ? (float) latestTrackPoint.position().altitude().toM() : null;
|
||||
String altitudeReference = latestTrackPoint != null && latestTrackPoint.position().hasAltitude() ? getContext().getString(latestTrackPoint.position().altitude().getLabelId()) : null;
|
||||
Pair<String, String> valueAndUnit = StringUtils.getAltitudeParts(getContext(), altitude, unitSystem);
|
||||
|
||||
getBinding().statsValue.setText(valueAndUnit.first);
|
||||
getBinding().statsUnit.setText(valueAndUnit.second);
|
||||
getBinding().statsDescriptionMain.setText(R.string.stats_altitude);
|
||||
getBinding().statsDescriptionSecondary.setText(altitudeReference);
|
||||
}
|
||||
}
|
||||
|
||||
public static class Gain extends GenericStatisticsViewHolder {
|
||||
|
||||
@Override
|
||||
|
||||
@@ -28,7 +28,7 @@ public class Mapping {
|
||||
m.put(context.getString(R.string.stats_custom_layout_elapsed_segment_time_key), GenericStatisticsViewHolder.ElapsedSegmentTime::new);
|
||||
m.put(context.getString(R.string.stats_custom_layout_fastest_pace_key), GenericStatisticsViewHolder.FastestPace::new);
|
||||
|
||||
m.put(context.getString(R.string.stats_custom_layout_altitude_key), GenericStatisticsViewHolder.Altitude::new);
|
||||
m.put(context.getString(R.string.stats_custom_layout_altitude_key), SensorStatisticsViewHolder.Altitude::new);
|
||||
m.put(context.getString(R.string.stats_custom_layout_gain_key), GenericStatisticsViewHolder.Gain::new);
|
||||
m.put(context.getString(R.string.stats_custom_layout_loss_key), GenericStatisticsViewHolder.Loss::new);
|
||||
m.put(context.getString(R.string.stats_custom_layout_coordinates_key), GenericStatisticsViewHolder.Coordinates::new);
|
||||
|
||||
@@ -5,6 +5,7 @@ import android.view.LayoutInflater;
|
||||
|
||||
import de.dennisguse.opentracks.R;
|
||||
import de.dennisguse.opentracks.data.models.HeartRateZones;
|
||||
import de.dennisguse.opentracks.data.models.TrackPoint;
|
||||
import de.dennisguse.opentracks.databinding.StatsSensorItemBinding;
|
||||
import de.dennisguse.opentracks.sensors.sensorData.SensorDataSet;
|
||||
import de.dennisguse.opentracks.services.RecordingData;
|
||||
@@ -128,4 +129,20 @@ public abstract class SensorStatisticsViewHolder extends StatisticViewHolder<Sta
|
||||
getBinding().statsDescriptionSecondary.setText(sensorName);
|
||||
}
|
||||
}
|
||||
|
||||
public static class Altitude extends SensorStatisticsViewHolder {
|
||||
|
||||
@Override
|
||||
public void onChanged(UnitSystem unitSystem, RecordingData data) {
|
||||
TrackPoint latestTrackPoint = data.latestTrackPoint();
|
||||
Float altitude = latestTrackPoint != null && latestTrackPoint.position().hasAltitude() ? (float) latestTrackPoint.position().altitude().toM() : null;
|
||||
String altitudeReference = latestTrackPoint != null && latestTrackPoint.position().hasAltitude() ? getContext().getString(latestTrackPoint.position().altitude().getLabelId()) : null;
|
||||
Pair<String, String> valueAndUnit = StringUtils.getAltitudeParts(getContext(), altitude, unitSystem);
|
||||
|
||||
getBinding().statsValue.setText(valueAndUnit.first);
|
||||
getBinding().statsUnit.setText(valueAndUnit.second);
|
||||
getBinding().statsDescriptionMain.setText(R.string.stats_altitude);
|
||||
getBinding().statsDescriptionSecondary.setText(altitudeReference);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -130,7 +130,7 @@ limitations under the License.
|
||||
<!-- Description -->
|
||||
<string name="description_activity_type">Aktivitetstype: %1$s</string>
|
||||
<string name="description_average_moving_pace_in_minute">Gennemsnitstempo i bevægelse: %1$s min./km (%2$s min./mil)</string>
|
||||
<string name="description_average_moving_speed">Gennemsnitshastighed i bevægelse: %1$.2f km/t (%2$.1f mil/t)</string>
|
||||
<string name="description_average_moving_speed">Gennemsnitlig bevægelseshastighed: %1$.2f km/t (%2$.1f mil/t)</string>
|
||||
<string name="description_average_pace_in_minute">Gennemsnitstempo: %1$s min./km (%2$s min./mil)</string>
|
||||
<string name="description_average_speed">Gennemsnitshastighed: %1$.2f km/t (%2$.1f mil/t)</string>
|
||||
<string name="description_altitude_gain">Stigning: %1$d m (%2$d fod)</string>
|
||||
@@ -220,7 +220,7 @@ limitations under the License.
|
||||
<!-- Search -->
|
||||
<!-- Sensor State -->
|
||||
<string name="sensor_state_heart_rate">Puls</string>
|
||||
<string name="sensor_state_heart_rate_value">%1$d slag per minut</string>
|
||||
<string name="sensor_state_heart_rate_value">%1$d slag/min.</string>
|
||||
<string name="sensor_state_power">Effekt</string>
|
||||
<string name="sensor_could_not_scan">Kunne ikke scanne efter Bluetooth-enheder (fejl %1$i).</string>
|
||||
<string name="bluetooth_disabled">Aktivér venligst Bluetooth.</string>
|
||||
@@ -544,7 +544,7 @@ limitations under the License.
|
||||
<string name="settings_announcements_idle_title">Stillestående</string>
|
||||
<string name="unit_knots">knob</string>
|
||||
<string name="settings_recording_idle_timeout_title">Tid før stilstand aktiveres</string>
|
||||
<string name="settings_announcements_average_speed_pace">Gennemsnitlig hastighed/tempo</string>
|
||||
<string name="settings_announcements_average_speed_pace">Gennemsnitshastighed/tempo</string>
|
||||
<string name="value_float_knots_recommended">%1$.1f knob (anbefalet)</string>
|
||||
<string name="activity_type_swimming">svømning</string>
|
||||
<string name="settings_recording_min_required_accuracy_summary">Placering ignoreres hvis nøjagtigheden er større end %1$s</string>
|
||||
@@ -603,4 +603,12 @@ limitations under the License.
|
||||
<string name="settings_announcements_lap_power">Omgangseffekt</string>
|
||||
<string name="power_x_watt">Power {x, plural, =1 {1 watt} other {# watt} }</string>
|
||||
<string name="power">Effekt</string>
|
||||
<string name="marker_detail_title">%1$d af %2$d: %3$s</string>
|
||||
<string name="sensor_state_temperature">Termometer</string>
|
||||
<string name="stats_sensors_temperature">Temperatur</string>
|
||||
<string name="sensor_state_cadence_unit">o/m</string>
|
||||
<string name="sensor_state_heart_rate_unit">slag/min.</string>
|
||||
<string name="settings_announcements_segment_average_speed_pace">Gennemsnitshastighed/tempo (segment)</string>
|
||||
<string name="stats_average_segment_pace">Gns. segment-tempo</string>
|
||||
<string name="stats_elapsed_segment_time">Forløbet segmentid</string>
|
||||
</resources>
|
||||
|
||||
@@ -622,4 +622,7 @@ Infatti, un\'applicazione deve supportare <i>ACTION_VIEW</i> con MIME <i>applica
|
||||
<string name="settings_announcements_segment_average_speed_pace">Velocità/Ritmo medi (segmento)</string>
|
||||
<string name="stats_average_segment_pace">Ritmo medio nel segmento</string>
|
||||
<string name="stats_elapsed_segment_time">Tempo trascorso nel segmento</string>
|
||||
<string name="stats_sensors_temperature">Temperatura</string>
|
||||
<string name="description_sensor_temperature">Temperatura (Celsius)</string>
|
||||
<string name="sensor_state_temperature">Termometro</string>
|
||||
</resources>
|
||||
|
||||
@@ -254,4 +254,6 @@
|
||||
<string name="waypoint_type_waterfall">מפל</string>
|
||||
<string name="waypoint_type_water_fountain">מזרקת מים</string>
|
||||
<string name="waypoint_type_waypoint">ציון דרך</string>
|
||||
</resources>
|
||||
<string name="about_preference_title">על אודות</string>
|
||||
<string name="about_libraries">ספריות</string>
|
||||
</resources>
|
||||
|
||||
@@ -304,4 +304,13 @@ limitations under the License.
|
||||
<string name="waypoint_type_waypoint">地点</string>
|
||||
<string name="help_feature_content">"* GPS トラックを作成します\n\n\n\n * 心拍センサーを使用します (Bluetooth LE のみ)\n\n\n\n * 音声アナウンス(時間または距離)\n\n\n\n * マーカーの作成 (写真を含む)\n\n\n\n * 完全にオフラインで動作します\n\n\n\n * 地図上にトラックを表示します (下記を参照)"</string>
|
||||
<string name="help_data_google_play_services_content">いいえ</string>
|
||||
</resources>
|
||||
<string name="about_history">OpenTracksは<a href="https://code.google.com/archive/p/mytracks/"><i>GoogleのMyTracks</i></a> (2016に開発停止)のフォークです。</string>
|
||||
<string name="help_feature_title">OpenTracks には以下の機能があります:</string>
|
||||
<string name="activity_type_escooter">電動スクーター</string>
|
||||
<string name="activity_type_kickscooter">スクーター</string>
|
||||
<string name="generic_overwrite">上書き</string>
|
||||
<string name="generic_skip">スキップ</string>
|
||||
<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>
|
||||
</resources>
|
||||
|
||||
@@ -26,9 +26,17 @@
|
||||
<string name="settings_sensor_bluetooth_cycling_power_key" translatable="false">bluetoothCyclingPowerSensor</string>
|
||||
<string name="settings_sensor_bluetooth_running_speed_and_cadence_key" translatable="false">bluetoothRunningSpeedAndCadenceSensor</string>
|
||||
<string name="settings_sensor_bluetooth_pressure_key" translatable="false">bluetoothPressureSensor</string>
|
||||
<string name="settings_sensor_bluetooth_gps_key" translatable="false">bluetoothGPSSensor</string>
|
||||
<string name="settings_sensor_gps_key" translatable="false">bluetoothGPSSensor</string>
|
||||
<string name="sensor_type_value_none" translatable="false">NONE</string>
|
||||
<string name="sensor_type_value_internal" translatable="false">INTERNAL</string>
|
||||
<string-array name="sensor_type_no_bluetooth_entryValues">
|
||||
<item>@string/sensor_type_value_none</item>
|
||||
<item>@string/sensor_type_value_internal</item>
|
||||
</string-array>
|
||||
<string-array name="sensor_type_no_bluetooth_entries">
|
||||
<item>@string/value_none</item>
|
||||
<item>@string/value_internal_sensor</item>
|
||||
</string-array>
|
||||
|
||||
<string name="settings_sensor_bluetooth_cycling_speed_wheel_circumference_key" translatable="false">bluetoothCyclingSpeedWheelCircumference</string>
|
||||
<string name="settings_sensor_bluetooth_cycling_speed_wheel_circumference_default" translatable="false">2135</string>
|
||||
|
||||
@@ -5,10 +5,13 @@
|
||||
android:title="@string/settings_sensors_title">
|
||||
|
||||
<PreferenceCategory android:title="@string/settings_gps_sensor">
|
||||
<de.dennisguse.opentracks.settings.bluetooth.BluetoothLeGpsPreference
|
||||
<ListPreference
|
||||
android:defaultValue="@string/sensor_type_value_internal"
|
||||
android:key="@string/settings_sensor_bluetooth_gps_key"
|
||||
android:title="@string/settings_gps_sensor" />
|
||||
android:entryValues="@array/sensor_type_no_bluetooth_entryValues"
|
||||
android:entries="@array/sensor_type_no_bluetooth_entries"
|
||||
android:key="@string/settings_sensor_gps_key"
|
||||
android:title="@string/settings_gps_sensor"
|
||||
app:useSimpleSummaryProvider="true" />
|
||||
<ListPreference
|
||||
android:defaultValue="@string/min_sampling_interval_default"
|
||||
android:entryValues="@array/min_sampling_interval_values"
|
||||
|
||||
Reference in New Issue
Block a user