forked from upstream-mirrors/OpenTracks
Compare commits
1 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 53f9432d45 |
@@ -100,8 +100,8 @@ __Only required permissions:__
|
||||
|
||||
### Public API
|
||||
|
||||
OpenTracks includes an API for starting/stopping recording by another installed application (e.g., [Automate](https://llamalab.com/automate/), [Tasker](https://tasker.joaoapps.com), or [Easer](https://github.com/renyuneyun/Easer)).
|
||||
The API is disabled by default to protect the user's privacy, but it can easily be enabled in the settings. Once enabled, the API can be invoked by sending an explicit Intent to start an activity.
|
||||
OpenTracks includes an API for starting/stopping recording by another installed application (e.g., [Automate](https://llamalab.com/automate/), [Tasker](https://tasker.joaoapps.com), or [Easer](https://github.com/renyuneyun/Easer)).
|
||||
The API can be invoked by sending an explicit Intent to start an activity.
|
||||
|
||||
`Package` (depends on the variant installed):
|
||||
* F-Droid: `de.dennisguse.opentracks`
|
||||
@@ -121,17 +121,9 @@ The API is disabled by default to protect the user's privacy, but it can easily
|
||||
* **Stop a recording:** `de.dennisguse.opentracks.publicapi.StopRecording`
|
||||
* **Create a marker:** `de.dennisguse.opentracks.publicapi.CreateMarker`
|
||||
|
||||
For testing the API using adb, the general command syntax is:
|
||||
For testing via adb: `adb shell am start -e someParameter someValue -n "package/class"`
|
||||
|
||||
```shell
|
||||
adb shell am start -e someParameter someValue -n "package/class"
|
||||
```
|
||||
|
||||
Depending on the package and class, a complete command could look something like this:
|
||||
|
||||
```shell
|
||||
adb shell am start -n "de.dennisguse.opentracks.playstore/de.dennisguse.opentracks.publicapi.StartRecording"
|
||||
```
|
||||
The Public API is disabled by default to protect the user's privacy, but it can easily be enabled in the settings.
|
||||
|
||||
## File formats compatibility with open-source software
|
||||
| | [GPX 1.1](https://www.topografix.com/GPX/1/1/) | [KML 2.3](https://docs.opengeospatial.org/is/12-007r2/12-007r2.html) | [KMZ 2.3](https://docs.opengeospatial.org/is/12-007r2/12-007r2.html) |
|
||||
|
||||
+5
-5
@@ -6,7 +6,7 @@ buildscript {
|
||||
mavenCentral()
|
||||
}
|
||||
dependencies {
|
||||
classpath 'com.android.tools.build:gradle:8.10.0'
|
||||
classpath 'com.android.tools.build:gradle:8.9.1'
|
||||
}
|
||||
}
|
||||
|
||||
@@ -46,8 +46,8 @@ android {
|
||||
|
||||
defaultConfig {
|
||||
applicationId 'de.dennisguse.opentracks'
|
||||
versionCode 6130
|
||||
versionName "v4.19.3"
|
||||
versionCode 6102
|
||||
versionName "v4.19.0"
|
||||
|
||||
buildConfigField "String", "VERSION_NAME_FULL", "\"${getVersionName()}\""
|
||||
|
||||
@@ -144,7 +144,7 @@ dependencies {
|
||||
|
||||
implementation 'androidx.appcompat:appcompat:1.7.0'
|
||||
implementation 'androidx.preference:preference:1.2.1'
|
||||
implementation 'androidx.documentfile:documentfile:1.1.0'
|
||||
implementation 'androidx.documentfile:documentfile:1.0.1'
|
||||
implementation 'androidx.gridlayout:gridlayout:1.1.0'
|
||||
implementation 'com.google.android.material:material:1.12.0'
|
||||
implementation 'androidx.constraintlayout:constraintlayout:2.2.1'
|
||||
@@ -158,7 +158,7 @@ dependencies {
|
||||
androidTestImplementation 'androidx.test:rules:1.6.1'
|
||||
androidTestImplementation 'androidx.test:runner:1.6.2'
|
||||
androidTestImplementation 'androidx.test.espresso:espresso-core:3.6.1'
|
||||
androidTestImplementation 'org.mockito:mockito-android:5.18.0'
|
||||
androidTestImplementation 'org.mockito:mockito-android:5.17.0'
|
||||
|
||||
androidTestUtil 'androidx.test:orchestrator:1.5.1'
|
||||
}
|
||||
|
||||
@@ -1,4 +0,0 @@
|
||||
v4.19.1: OpenTracks
|
||||
|
||||
Bugfix:
|
||||
- Exported GPX files missed one schemaLocation
|
||||
@@ -1,5 +0,0 @@
|
||||
v4.19.2: OpenTracks
|
||||
|
||||
Bugfix:
|
||||
- Fix crash due to faulty translations
|
||||
|
||||
@@ -1,5 +0,0 @@
|
||||
v4.19.3: OpenTracks
|
||||
|
||||
Bugfix:
|
||||
- Fix CSV export for altitude (was cut at 1000m)
|
||||
|
||||
+44
@@ -0,0 +1,44 @@
|
||||
package de.dennisguse.opentracks.io.file.exporter;
|
||||
|
||||
import org.junit.Test;
|
||||
|
||||
import java.nio.ByteBuffer;
|
||||
import java.nio.ByteOrder;
|
||||
|
||||
public class FITv2TrackExporterTest {
|
||||
|
||||
private final byte FIT_PROTOCOL_VERSION = 2 << 4 + 0;
|
||||
private final short FIT_PROFILE_VERSION = 21 * 1000 + 158;
|
||||
|
||||
private long checksum;
|
||||
private final byte FILE_HEADER_SIZE = 14;
|
||||
|
||||
@Test
|
||||
public void run() {
|
||||
ByteBuffer littleEndian = ByteBuffer
|
||||
.allocate(FILE_HEADER_SIZE)
|
||||
.order(ByteOrder.LITTLE_ENDIAN);
|
||||
|
||||
littleEndian.put(0, FILE_HEADER_SIZE);
|
||||
littleEndian.put(1, FIT_PROTOCOL_VERSION);
|
||||
|
||||
// Profile version: 1513
|
||||
littleEndian.putShort(2, FIT_PROFILE_VERSION);
|
||||
|
||||
// Data size: 32bit
|
||||
littleEndian.putInt(4, 0); // We do not calculate the data size.
|
||||
|
||||
// .FIT
|
||||
littleEndian.putChar(8, '.');
|
||||
littleEndian.putChar(9, 'F');
|
||||
littleEndian.putChar(10, 'I');
|
||||
littleEndian.putChar(11, 'T');
|
||||
|
||||
// CRC (not implemented)
|
||||
littleEndian.put(12, (byte) 0);
|
||||
littleEndian.put(13, (byte) 0);
|
||||
|
||||
var a = littleEndian.array();
|
||||
littleEndian.array();
|
||||
}
|
||||
}
|
||||
+40
-63
@@ -18,7 +18,6 @@ import androidx.test.core.app.ApplicationProvider;
|
||||
import androidx.test.ext.junit.runners.AndroidJUnit4;
|
||||
import androidx.test.filters.LargeTest;
|
||||
import androidx.test.platform.app.InstrumentationRegistry;
|
||||
import androidx.test.rule.GrantPermissionRule;
|
||||
import androidx.test.rule.ServiceTestRule;
|
||||
|
||||
import org.junit.After;
|
||||
@@ -46,7 +45,6 @@ import java.util.concurrent.TimeoutException;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import de.dennisguse.opentracks.R;
|
||||
import de.dennisguse.opentracks.TestUtil;
|
||||
import de.dennisguse.opentracks.TimezoneRule;
|
||||
import de.dennisguse.opentracks.content.data.TestDataUtil;
|
||||
import de.dennisguse.opentracks.data.ContentProviderUtils;
|
||||
@@ -89,9 +87,6 @@ public class ExportImportTest {
|
||||
@Rule
|
||||
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.
|
||||
@Rule
|
||||
public TimezoneRule timezoneRule = new TimezoneRule(TimeZone.getTimeZone("Europe/Berlin"));
|
||||
@@ -149,7 +144,7 @@ public class ExportImportTest {
|
||||
|
||||
Distance sensorDistance = Distance.of(10); // recording distance interval
|
||||
|
||||
sendLocation(trackPointCreator, "2020-02-02T02:02:03Z", 3.1234567, 14.0014567, 10, 13, 15, 1020.25, 1f);
|
||||
sendLocation(trackPointCreator, "2020-02-02T02:02:03Z", 3, 14, 10, 13, 15, 10, 1f);
|
||||
contentProviderUtils.insertMarker(new Marker(trackId, service.getLastStoredTrackPointWithLocation(), "Marker 1", "Marker 1 desc", "Marker 1 category", null, null));
|
||||
|
||||
// A sensor-only TrackPoint
|
||||
@@ -160,10 +155,11 @@ public class ExportImportTest {
|
||||
mockSensorData(trackPointCreator, 15f, null, 67f, 3f, 50f, null);
|
||||
trackPointCreator.setClock("2020-02-02T02:02:15Z");
|
||||
mockSensorData(trackPointCreator, null, null, 68f, 3f, 50f, null);
|
||||
|
||||
trackPointCreator.setClock("2020-02-02T02:02:16Z");
|
||||
mockSensorData(trackPointCreator, 5f, Distance.of(2), 69f, 3f, 50f, null); // Distance will be added to next TrackPoint
|
||||
|
||||
sendLocation(trackPointCreator, "2020-02-02T02:02:17Z", 3.1234567, 14.0014567, 10, 13, 15, 1020.25, 0f);
|
||||
sendLocation(trackPointCreator, "2020-02-02T02:02:17Z", 3, 14.001, 10, 13, 15, 10, 0f);
|
||||
contentProviderUtils.insertMarker(new Marker(trackId, service.getLastStoredTrackPointWithLocation(), "Marker 2", "Marker 2 desc", "Marker 2 category", null, null));
|
||||
|
||||
trackPointCreator.setClock("2020-02-02T02:02:18Z");
|
||||
@@ -173,14 +169,14 @@ public class ExportImportTest {
|
||||
trackPointCreator.setClock("2020-02-02T02:03:20Z");
|
||||
service.resumeTrack(trackId);
|
||||
|
||||
sendLocation(trackPointCreator, "2020-02-02T02:03:21Z", 3.1234567, 14.0024567, 10, 13, 15, 999.123, 0f);
|
||||
sendLocation(trackPointCreator, "2020-02-02T02:03:21Z", 3, 14.002, 10, 13, 15, 10, 0f);
|
||||
|
||||
sendLocation(trackPointCreator, "2020-02-02T02:03:22Z", 3.1234567, 16, 10, 13, 15, 999.123, 0f);
|
||||
sendLocation(trackPointCreator, "2020-02-02T02:03:22Z", 3, 16, 10, 13, 15, 10, 0f);
|
||||
|
||||
trackPointCreator.setClock("2020-02-02T02:03:30Z");
|
||||
service.getTrackRecordingManager().onIdle();
|
||||
|
||||
sendLocation(trackPointCreator, "2020-02-02T02:03:50Z", 3.1234567, 16.001, 10, 27, 15, 999.123, 0f);
|
||||
sendLocation(trackPointCreator, "2020-02-02T02:03:50Z", 3, 16.001, 10, 27, 15, 10, 0f);
|
||||
|
||||
trackPointCreator.getSensorManager().sensorDataSet = new SensorDataSet(trackPointCreator);
|
||||
trackPointCreator.setClock("2020-02-02T02:04:00Z");
|
||||
@@ -213,16 +209,16 @@ public class ExportImportTest {
|
||||
assertEquals(Duration.ofSeconds(26), trackStatistics.getMovingTime()); //TODO Likely too low
|
||||
|
||||
// Distance
|
||||
assertEquals(222049.34375, trackStatistics.getTotalDistance().toM(), 0.01); //TODO Too low
|
||||
assertEquals(222125.53125, trackStatistics.getTotalDistance().toM(), 0.01); //TODO Too low
|
||||
|
||||
// Speed
|
||||
assertEquals(8540.359, trackStatistics.getMaxSpeed().toMPS(), 0.01);
|
||||
assertEquals(3965.166, trackStatistics.getAverageSpeed().toMPS(), 0.01);
|
||||
assertEquals(8540.359, trackStatistics.getAverageMovingSpeed().toMPS(), 0.01);
|
||||
assertEquals(8543.29, trackStatistics.getMaxSpeed().toMPS(), 0.01);
|
||||
assertEquals(3966.52, trackStatistics.getAverageSpeed().toMPS(), 0.01);
|
||||
assertEquals(8543.28, trackStatistics.getAverageMovingSpeed().toMPS(), 0.01);
|
||||
|
||||
// Altitude
|
||||
assertEquals(999.122, trackStatistics.getMinAltitude(), 0.01);
|
||||
assertEquals(1020.25, trackStatistics.getMaxAltitude(), 0.01);
|
||||
assertEquals(10, trackStatistics.getMinAltitude(), 0.01);
|
||||
assertEquals(10, trackStatistics.getMaxAltitude(), 0.01);
|
||||
|
||||
assertEquals(2, trackStatistics.getTotalAltitudeGain(), 0.01);
|
||||
assertEquals(2, trackStatistics.getTotalAltitudeLoss(), 0.01);
|
||||
@@ -233,8 +229,8 @@ public class ExportImportTest {
|
||||
new TrackPoint(TrackPoint.Type.TRACKPOINT,
|
||||
new Position(
|
||||
Instant.parse("2020-02-02T02:02:03Z"),
|
||||
3.123456, 14.001456, Distance.of(10),
|
||||
Altitude.WGS84.of(1020.25), null,
|
||||
3d, 14d, Distance.of(10),
|
||||
Altitude.WGS84.of(10), null,
|
||||
null,
|
||||
Speed.of(15)))
|
||||
.setAltitudeLoss(1f)
|
||||
@@ -255,8 +251,8 @@ public class ExportImportTest {
|
||||
new TrackPoint(TrackPoint.Type.TRACKPOINT,
|
||||
new Position(
|
||||
Instant.parse("2020-02-02T02:02:17Z"),
|
||||
3.123456, 14.001456, Distance.of(10),
|
||||
Altitude.WGS84.of(1020.25), null,
|
||||
3d, 14.001, Distance.of(10),
|
||||
Altitude.WGS84.of(10), null,
|
||||
null,
|
||||
Speed.of(5)))
|
||||
.setSensorDistance(Distance.of(2))
|
||||
@@ -270,16 +266,16 @@ public class ExportImportTest {
|
||||
new TrackPoint(TrackPoint.Type.TRACKPOINT,
|
||||
new Position(
|
||||
Instant.parse("2020-02-02T02:03:21Z"),
|
||||
3.123456, 14.002456, Distance.of(10),
|
||||
Altitude.WGS84.of(999.1229858398438), null,
|
||||
3d, 14.002d, Distance.of(10),
|
||||
Altitude.WGS84.of(10), null,
|
||||
null,
|
||||
Speed.of(15)))
|
||||
.setAltitudeLoss(0f)
|
||||
.setAltitudeGain(0f),
|
||||
new TrackPoint(TrackPoint.Type.SEGMENT_START_AUTOMATIC,
|
||||
new Position(Instant.parse("2020-02-02T02:03:22Z"),
|
||||
3.123456, 16d, Distance.of(10),
|
||||
Altitude.WGS84.of(999.1229858398438), null,
|
||||
3d, 16d, Distance.of(10),
|
||||
Altitude.WGS84.of(10), null,
|
||||
null,
|
||||
Speed.of(15)))
|
||||
.setAltitudeLoss(0f)
|
||||
@@ -290,8 +286,8 @@ public class ExportImportTest {
|
||||
new TrackPoint(TrackPoint.Type.TRACKPOINT,
|
||||
new Position(
|
||||
Instant.parse("2020-02-02T02:03:50Z"),
|
||||
3.123456, 16.001, Distance.of(10),
|
||||
Altitude.WGS84.of(999.1229858398438), null,
|
||||
3d, 16.001, Distance.of(10),
|
||||
Altitude.WGS84.of(10), null,
|
||||
null, Speed.of(15)))
|
||||
.setAltitudeLoss(0f)
|
||||
.setAltitudeGain(0f),
|
||||
@@ -338,26 +334,7 @@ public class ExportImportTest {
|
||||
|
||||
// Time
|
||||
assertEquals(track.getZoneOffset(), importedTrack.getZoneOffset());
|
||||
assertEquals(Instant.parse("2020-02-02T02:02:02Z"), importedTrackStatistics.getStartTime());
|
||||
assertEquals(Instant.parse("2020-02-02T02:04:00Z"), importedTrackStatistics.getStopTime());
|
||||
|
||||
assertEquals(Duration.ofSeconds(56), importedTrackStatistics.getTotalTime());
|
||||
assertEquals(Duration.ofSeconds(26), importedTrackStatistics.getMovingTime());
|
||||
|
||||
// Distance
|
||||
assertEquals(222049.421, importedTrackStatistics.getTotalDistance().toM(), 0.01);
|
||||
|
||||
// Speed
|
||||
assertEquals(8540.362, importedTrackStatistics.getMaxSpeed().toMPS(), 0.01);
|
||||
assertEquals(3965.168, importedTrackStatistics.getAverageSpeed().toMPS(), 0.01);
|
||||
assertEquals(8540.362, importedTrackStatistics.getAverageMovingSpeed().toMPS(), 0.01);
|
||||
|
||||
// Altitude
|
||||
assertEquals(999.122, importedTrackStatistics.getMinAltitude(), 0.01);
|
||||
assertEquals(1020.25, importedTrackStatistics.getMaxAltitude(), 0.01);
|
||||
assertEquals(2, importedTrackStatistics.getTotalAltitudeGain(), 0.01);
|
||||
assertEquals(2, importedTrackStatistics.getTotalAltitudeLoss(), 0.01);
|
||||
|
||||
assertEquals(track.getTrackStatistics(), importedTrackStatistics);
|
||||
|
||||
// 4. markers
|
||||
assertMarkers();
|
||||
@@ -429,8 +406,8 @@ public class ExportImportTest {
|
||||
new TrackPoint(TrackPoint.Type.SEGMENT_START_AUTOMATIC,
|
||||
new Position(
|
||||
Instant.parse("2020-02-02T02:02:03Z"),
|
||||
3.123456, 14.001456d, Distance.of(10),
|
||||
Altitude.WGS84.of(1020.2), null,
|
||||
3d, 14d, Distance.of(10),
|
||||
Altitude.WGS84.of(10), null,
|
||||
null,
|
||||
Speed.of(15)))
|
||||
.setAltitudeLoss(1f)
|
||||
@@ -438,8 +415,8 @@ public class ExportImportTest {
|
||||
new TrackPoint(TrackPoint.Type.TRACKPOINT,
|
||||
new Position(
|
||||
Instant.parse("2020-02-02T02:02:17Z"),
|
||||
3.123456, 14.001456, Distance.of(10),
|
||||
Altitude.WGS84.of(1020.2), null,
|
||||
3d, 14.001, Distance.of(10),
|
||||
Altitude.WGS84.of(10), null,
|
||||
null,
|
||||
Speed.of(5)))
|
||||
.setAltitudeLoss(1f)
|
||||
@@ -451,8 +428,8 @@ public class ExportImportTest {
|
||||
new TrackPoint(TrackPoint.Type.SEGMENT_START_AUTOMATIC,
|
||||
new Position(
|
||||
Instant.parse("2020-02-02T02:03:21Z"),
|
||||
3.123456, 14.002456, Distance.of(10),
|
||||
Altitude.WGS84.of(999.0999755859375), null,
|
||||
3d, 14.002, Distance.of(10),
|
||||
Altitude.WGS84.of(10), null,
|
||||
null,
|
||||
Speed.of(15)))
|
||||
.setAltitudeLoss(0f)
|
||||
@@ -460,8 +437,8 @@ public class ExportImportTest {
|
||||
new TrackPoint(TrackPoint.Type.SEGMENT_START_AUTOMATIC,
|
||||
new Position(
|
||||
Instant.parse("2020-02-02T02:03:22Z"),
|
||||
3.123456, 16d, Distance.of(10),
|
||||
Altitude.WGS84.of(999.0999755859375), null,
|
||||
3d, 16d, Distance.of(10),
|
||||
Altitude.WGS84.of(10), null,
|
||||
null,
|
||||
Speed.of(15)))
|
||||
.setAltitudeLoss(0f)
|
||||
@@ -469,8 +446,8 @@ public class ExportImportTest {
|
||||
new TrackPoint(TrackPoint.Type.TRACKPOINT,
|
||||
new Position(
|
||||
Instant.parse("2020-02-02T02:03:50Z"),
|
||||
3.123456, 16.001, Distance.of(10),
|
||||
Altitude.WGS84.of(999.0999755859375), null,
|
||||
3d, 16.001, Distance.of(10),
|
||||
Altitude.WGS84.of(10), null,
|
||||
null,
|
||||
Speed.of(10)))
|
||||
.setAltitudeLoss(0f)
|
||||
@@ -490,16 +467,16 @@ public class ExportImportTest {
|
||||
assertEquals(Duration.ofSeconds(107), importedTrackStatistics.getMovingTime());
|
||||
|
||||
// Distance
|
||||
assertEquals(222271.734, importedTrackStatistics.getTotalDistance().toM(), 0.01);
|
||||
assertEquals(222347.85, importedTrackStatistics.getTotalDistance().toM(), 0.01);
|
||||
|
||||
// Speed
|
||||
assertEquals(2077.305, importedTrackStatistics.getMaxSpeed().toMPS(), 0.01);
|
||||
assertEquals(2077.305, importedTrackStatistics.getAverageSpeed().toMPS(), 0.01);
|
||||
assertEquals(2077.305, importedTrackStatistics.getAverageMovingSpeed().toMPS(), 0.01);
|
||||
assertEquals(2078.01, importedTrackStatistics.getMaxSpeed().toMPS(), 0.01);
|
||||
assertEquals(2078.01, importedTrackStatistics.getAverageSpeed().toMPS(), 0.01);
|
||||
assertEquals(2078.01, importedTrackStatistics.getAverageMovingSpeed().toMPS(), 0.01);
|
||||
|
||||
// Altitude
|
||||
assertEquals(999.099, importedTrackStatistics.getMinAltitude(), 0.01);
|
||||
assertEquals(1020.2, importedTrackStatistics.getMaxAltitude(), 0.01);
|
||||
assertEquals(10, importedTrackStatistics.getMinAltitude(), 0.01);
|
||||
assertEquals(10, importedTrackStatistics.getMaxAltitude(), 0.01);
|
||||
assertEquals(2, importedTrackStatistics.getTotalAltitudeGain(), 0.01);
|
||||
assertEquals(2, importedTrackStatistics.getTotalAltitudeLoss(), 0.01);
|
||||
|
||||
@@ -637,7 +614,7 @@ public class ExportImportTest {
|
||||
}
|
||||
}
|
||||
|
||||
private void sendLocation(TrackPointCreator trackPointCreator, String time, double latitude, double longitude, float accuracy, float verticalAccuracy, float speed, double altitude, Float altitudeGain) {
|
||||
private void sendLocation(TrackPointCreator trackPointCreator, String time, double latitude, double longitude, float accuracy, float verticalAccuracy, float speed, float altitude, Float altitudeGain) {
|
||||
Location location = new Location("mock");
|
||||
location.setLatitude(latitude);
|
||||
location.setLongitude(longitude);
|
||||
|
||||
@@ -1,13 +1,13 @@
|
||||
#time,trackpoint_type,latitude,longitude,altitude,accuracy_horizontal,accuracy_vertical,speed,altitude_gain,altitude_loss,sensor_distance,heartrate,cadence,power
|
||||
"2020-02-02T03:02:02+01:00","SEGMENT_START_MANUAL",,,,,,,,,,,,
|
||||
"2020-02-02T03:02:03+01:00","TRACKPOINT",3.123456,14.001456,1020.2,10,,54,1,1,,,,
|
||||
"2020-02-02T03:02:03+01:00","TRACKPOINT",3,14,10,10,,54,1,1,,,,
|
||||
"2020-02-02T03:02:04+01:00","TRACKPOINT",,,,,,54,1,1,10,66,3,50
|
||||
"2020-02-02T03:02:15+01:00","TRACKPOINT",,,,,,,,,,68,3,50
|
||||
"2020-02-02T03:02:17+01:00","TRACKPOINT",3.123456,14.001456,1020.2,10,,18,0,0,2,69,3,50
|
||||
"2020-02-02T03:02:17+01:00","TRACKPOINT",3,14.001,10,10,,18,0,0,2,69,3,50
|
||||
"2020-02-02T03:02:18+01:00","SEGMENT_END_MANUAL",,,,,,,,,,,,
|
||||
"2020-02-02T03:03:20+01:00","SEGMENT_START_MANUAL",,,,,,,,,,,,
|
||||
"2020-02-02T03:03:21+01:00","TRACKPOINT",3.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:21+01:00","TRACKPOINT",3,14.002,10,10,,54,0,0,,,,
|
||||
"2020-02-02T03:03:22+01:00","SEGMENT_START_AUTOMATIC",3,16,10,10,,54,0,0,,,,
|
||||
"2020-02-02T03:03: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:03:50+01:00","TRACKPOINT",3,16.001,10,10,,54,0,0,,,,
|
||||
"2020-02-02T03:04:00+01:00","SEGMENT_END_MANUAL",,,,,,,,,,,,
|
||||
|
@@ -60,6 +60,7 @@ public class CSVTrackExporter implements TrackExporter {
|
||||
ALTITUDE_FORMAT.setGroupingUsed(false);
|
||||
|
||||
COORDINATE_FORMAT.setMaximumFractionDigits(6);
|
||||
COORDINATE_FORMAT.setMaximumIntegerDigits(3);
|
||||
COORDINATE_FORMAT.setGroupingUsed(false);
|
||||
|
||||
SPEED_FORMAT.setMaximumFractionDigits(2);
|
||||
@@ -93,17 +94,17 @@ public class CSVTrackExporter implements TrackExporter {
|
||||
new Column("trackpoint_type", t -> quote(t.getType().name())),
|
||||
new Column("latitude", t -> t.hasLocation() ? COORDINATE_FORMAT.format(t.getLatitude()) : ""),
|
||||
new Column("longitude", t -> t.hasLocation() ? COORDINATE_FORMAT.format(t.getLongitude()) : ""),
|
||||
new Column("altitude", t -> t.hasAltitude() ? ALTITUDE_FORMAT.format(t.getAltitude().toM()) : ""),
|
||||
new Column("altitude", t -> t.hasAltitude() ? COORDINATE_FORMAT.format(t.getAltitude().toM()) : ""),
|
||||
new Column("accuracy_horizontal", t -> t.hasHorizontalAccuracy() ? DISTANCE_FORMAT.format(t.getHorizontalAccuracy().toM()) : ""),
|
||||
new Column("accuracy_vertical", t -> t.hasVerticalAccuracy() ? DISTANCE_FORMAT.format(t.getVerticalAccuracy().toM()) : ""),
|
||||
|
||||
new Column("speed", t -> t.hasSpeed() ? SPEED_FORMAT.format(t.getSpeed().toKMH()) : ""),
|
||||
new Column("altitude_gain", t -> t.hasAltitudeGain() ? ALTITUDE_FORMAT.format(t.getAltitudeGain()) : ""),
|
||||
new Column("altitude_loss", t -> t.hasAltitudeLoss() ? ALTITUDE_FORMAT.format(t.getAltitudeLoss()) : ""),
|
||||
new Column("altitude_gain", t -> t.hasAltitudeGain() ? DISTANCE_FORMAT.format(t.getAltitudeGain()) : ""),
|
||||
new Column("altitude_loss", t -> t.hasAltitudeLoss() ? DISTANCE_FORMAT.format(t.getAltitudeLoss()) : ""),
|
||||
new Column("sensor_distance", t -> t.hasSensorDistance() ? DISTANCE_FORMAT.format(t.getSensorDistance().toM()) : ""),
|
||||
new Column("heartrate", t -> t.hasHeartRate() ? HEARTRATE_FORMAT.format(t.getHeartRate().getBPM()) : ""),
|
||||
new Column("cadence", t -> t.hasCadence() ? CADENCE_FORMAT.format(t.getCadence().getRPM()) : ""),
|
||||
new Column("power", t -> t.hasPower() ? POWER_FORMAT.format(t.getPower().getW()) : ""));
|
||||
new Column("power", t -> t.hasPower() ? ALTITUDE_FORMAT.format(t.getPower().getW()) : ""));
|
||||
|
||||
try {
|
||||
prepare(outputStream);
|
||||
@@ -135,7 +136,7 @@ public class CSVTrackExporter implements TrackExporter {
|
||||
while (trackPointIterator.hasNext()) {
|
||||
if (Thread.interrupted()) throw new InterruptedException();
|
||||
|
||||
writeTrackPoint(columns, trackPointIterator.next());
|
||||
writeTrackPoint(columns, trackPointIterator.next());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,74 @@
|
||||
package de.dennisguse.opentracks.io.file.exporter;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.annotation.VisibleForTesting;
|
||||
|
||||
import java.io.OutputStream;
|
||||
import java.nio.ByteBuffer;
|
||||
import java.nio.ByteOrder;
|
||||
import java.nio.channels.Channels;
|
||||
import java.nio.channels.WritableByteChannel;
|
||||
import java.util.List;
|
||||
|
||||
import de.dennisguse.opentracks.data.models.Track;
|
||||
|
||||
/**
|
||||
* FIT v2.0
|
||||
* https://developer.garmin.com/fit/
|
||||
* <p>
|
||||
* Encoding is little endian.
|
||||
*/
|
||||
public class FITv2TrackExporter implements TrackExporter {
|
||||
|
||||
private final byte FIT_PROTOCOL_VERSION = 2 << 4 + 0;
|
||||
private final short FIT_PROFILE_VERSION = 21 * 1000 + 158;
|
||||
|
||||
private long checksum;
|
||||
private final byte FILE_HEADER_SIZE = 14;
|
||||
|
||||
|
||||
@Override
|
||||
public boolean writeTrack(List<Track> tracks, @NonNull OutputStream outputStream) {
|
||||
WritableByteChannel channel = Channels.newChannel(outputStream);
|
||||
|
||||
writeFileHeader(channel);
|
||||
// writeFileId(channel);
|
||||
writeFooter(channel);
|
||||
return false;
|
||||
}
|
||||
|
||||
// https://developer.garmin.com/fit/protocol/
|
||||
@VisibleForTesting
|
||||
void writeFileHeader(@NonNull WritableByteChannel channel) {
|
||||
ByteBuffer littleEndian = ByteBuffer
|
||||
.allocate(FILE_HEADER_SIZE)
|
||||
.order(ByteOrder.LITTLE_ENDIAN);
|
||||
|
||||
littleEndian.put(0, FILE_HEADER_SIZE);
|
||||
littleEndian.put(1, FIT_PROTOCOL_VERSION);
|
||||
|
||||
// Profile version
|
||||
littleEndian.putShort(2, FIT_PROFILE_VERSION);
|
||||
|
||||
// Data size: 32bit
|
||||
littleEndian.putInt(4, 0); // We do not calculate the data size.
|
||||
|
||||
// .FIT
|
||||
littleEndian.putChar(8, '.');
|
||||
littleEndian.putChar(9, 'F');
|
||||
littleEndian.putChar(10, 'I');
|
||||
littleEndian.putChar(11, 'T');
|
||||
|
||||
// CRC (not implemented)
|
||||
littleEndian.put(12, (byte) 0);
|
||||
littleEndian.put(13, (byte) 0);
|
||||
|
||||
var a = littleEndian.array();
|
||||
littleEndian.array();
|
||||
}
|
||||
|
||||
|
||||
private void writeFooter(@NonNull WritableByteChannel channel) {
|
||||
|
||||
}
|
||||
}
|
||||
@@ -637,7 +637,7 @@ Meldet das GPS-Gerät ungenaue Daten (z.B. Standort, Geschwindigkeit, Höhe), ka
|
||||
<string name="settings_announcements_unit">Distanz/Geschwindigkeits Einheit</string>
|
||||
<string name="power_x_watt">Leistung {x, plural, =1 {1 Watt} other {# Watt} }</string>
|
||||
<string name="power">Leistung</string>
|
||||
<string name="voiceDistance">{n, plural, =1 {1 } other {{n,number,#.0}} }</string>
|
||||
<string name="voiceDistance">{n, plural, =1 {1 } anderes {{n,number,#.0}} }</string>
|
||||
<string name="settings_recording_min_sampling_interval_title">Abtastzeitintervall</string>
|
||||
<string name="voiceIdle">Untätig werden.</string>
|
||||
<string name="voice_announce_lap_power_key">StimmeAnkündigungLapPower</string>
|
||||
|
||||
@@ -657,11 +657,11 @@ Si le dispositif GPS signale des données inexactes (par exemple, la localisatio
|
||||
<string name="permission_recording_failed">OpenTracks a besoin de la permission d\'accéder à votre localisation ainsi qu\'aux appareils à proximité.</string>
|
||||
<string name="create_marker_error">Impossible de créer un marqueur. Veuillez réessayer.</string>
|
||||
<string name="power">Puissance</string>
|
||||
<string name="power_x_watt">Puissance {x, plural, =1 {1 watt} other {# watt} }</string>
|
||||
<string name="power_x_watt">Puissance {x, plural, =1 {1 watt} autres {# watt} }</string>
|
||||
<string name="settings_announcements_lap_power">Puissance par tour</string>
|
||||
<string name="activity_type_kickscooter">trottinette</string>
|
||||
<string name="voiceSpeed">{n, plural, =1 {1} other {{n,number,#.0}} }</string>
|
||||
<string name="voiceDistance">{n, plural, =1 {1 } other {{n,number,#.0}} }</string>
|
||||
<string name="voiceSpeed">{n, plural, =1 {1} autres {{n,number,#.0}} }</string>
|
||||
<string name="voiceDistance">{n, plural, =1 {1 } autres {{n,number,#.0}} }</string>
|
||||
<string name="settings_locale_system_default">Paramètres par défaut du système</string>
|
||||
<string name="settings_locale_title">Langue</string>
|
||||
<string name="stats_gain_loss">Gain et perte</string>
|
||||
|
||||
@@ -507,11 +507,11 @@
|
||||
<string name="settings_announcements_unit">Aonad faid/luas</string>
|
||||
<string name="value_internal_sensor">Braiteoir inmheánach</string>
|
||||
<string name="voiceIdle">Ag éirí díomhaoin.</string>
|
||||
<string name="voiceSecondsPlural">{n, plural, =1 {1 soicind} other {{ n,number} soicind} }</string>
|
||||
<string name="voiceSpeedKilometersPerHourPlural">{n, plural, =1 {1 chiliméadar in aghaidh na huaire} other {{ n,number, #.0} ciliméadar san uair} }</string>
|
||||
<string name="voiceSpeedMKnotsPlural">{n, plural, =1 {1 snaidhm} other {{ n,number, #.0} snaidhm} }</string>
|
||||
<string name="voiceDistanceNauticalMilesPlural">{n, plural, =1 {1 mhuirmhíle} other {{ n,number, #.0} muirmhíle} }</string>
|
||||
<string name="voiceDistance">{n, plural, =1 {1 } other {{ n,number, #.0}} }</string>
|
||||
<string name="voiceSecondsPlural">{n, iolra, =1 {1 soicind} eile {{ n,uimhir} soicind} }</string>
|
||||
<string name="voiceSpeedKilometersPerHourPlural">{n, iolra, =1 {1 chiliméadar in aghaidh na huaire} eile {{ n,uimhir, #.0} ciliméadar san uair} }</string>
|
||||
<string name="voiceSpeedMKnotsPlural">{n, iolra, =1 {1 snaidhm} eile {{ n,uimhir, #.0} snaidhm} }</string>
|
||||
<string name="voiceDistanceNauticalMilesPlural">{n, iolra, =1 {1 mhuirmhíle} eile {{ n,uimhir, #.0} muirmhíle} }</string>
|
||||
<string name="voiceDistance">{n, iolra, =1 {1 } eile {{ n,uimhir, #.0}} }</string>
|
||||
<string name="settings_recording_min_sampling_interval_title">Eatramh ama samplála</string>
|
||||
<string name="sensor_state_cadence_unit">rpm</string>
|
||||
<string name="sensor_state_heart_rate_unit">bpm</string>
|
||||
@@ -527,20 +527,20 @@
|
||||
<string name="settings_api_summary">Cianrialú agus staitisticí API le haghaidh aipeanna eile</string>
|
||||
<string name="create_marker_error">Ní féidir marcóir a chruthú. Déan iarracht arís.</string>
|
||||
<string name="sensor_state_power_unit">W</string>
|
||||
<string name="voiceMinutesPlural">{n, plural, =1 {1 nóiméad} other {{ n,number} nóiméad} }</string>
|
||||
<string name="voiceDistanceKilometersPlural">{n, plural, =1 {1 chiliméadar} other {{ n,number, #.0} ciliméadar} }</string>
|
||||
<string name="voiceMinutesPlural">{n, iolra, =1 {1 nóiméad} eile {{ n,uimhir} nóiméad} }</string>
|
||||
<string name="voiceDistanceKilometersPlural">{n, iolra, =1 {1 chiliméadar} eile {{ n,uimhir, #.0} ciliméadar} }</string>
|
||||
<string name="sensor_state_power_max">Cumhacht Max</string>
|
||||
<string name="bluetooth_sensor_summary">%1$s (%2$s)</string>
|
||||
<string name="voiceHoursPlural">{n, plural, =1 {1 uair} other {{ n,number} uair an chloig} }</string>
|
||||
<string name="voiceDistanceMilesPlural">{n, plural, =1 {1 mhíle} other {{ n,number, #.0} míle} }</string>
|
||||
<string name="voiceHoursPlural">{n, iolra, =1 {1 uair} eile {{ n,uimhir} uair an chloig} }</string>
|
||||
<string name="voiceDistanceMilesPlural">{n, iolra, =1 {1 mhíle} eile {{ n,uimhir, #.0} míle} }</string>
|
||||
<string name="no_compatible_camera_installed">Níl feidhmchlár ceamara comhoiriúnach suiteáilte</string>
|
||||
<string name="voiceSpeedMilesPerHourPlural">{n, plural, =1 {1 mhíle san uair} other {{ n,number, #.0} míle san uair} }</string>
|
||||
<string name="voiceSpeed">{n, plural, =1 {1} other {{ n,number, #.0}} }</string>
|
||||
<string name="voiceSpeedMilesPerHourPlural">{n, iolra, =1 {1 mhíle san uair} eile {{ n,uimhir, #.0} míle san uair} }</string>
|
||||
<string name="voiceSpeed">{n, iolra, =1 {1} eile {{ n,uimhir, #.0}} }</string>
|
||||
<string name="voice_on_device_speaker_title">Úsáid cainteoir an ghléis</string>
|
||||
<string name="no_compatible_gallery_installed">Níl feidhmchlár gailearaí íomhá comhoiriúnach suiteáilte</string>
|
||||
<string name="settings_recording_location_distance_summary">%1$s idir suíomhanna taifeadta</string>
|
||||
<string name="settings_announcements_lap_power">Cumhacht glúine</string>
|
||||
<string name="power_x_watt">Cumhacht {x, plural, = 1 {1 vata} other {# vata} }</string>
|
||||
<string name="power_x_watt">Cumhacht {x, iolra, = 1 {1 vata} eile {# vata} }</string>
|
||||
<string name="empty">" "</string>
|
||||
<string name="settings_locale_system_default">Réamhshocrú córais</string>
|
||||
<string name="settings_locale_title">Teanga</string>
|
||||
|
||||
@@ -65,7 +65,7 @@
|
||||
<string name="settings_recording_location_distance_summary">பதிவுசெய்யப்பட்ட இடங்களுக்கு இடையில் %1$s</string>
|
||||
<string name="voice_announce_lap_power_key">VoiceAnnouncelappower</string>
|
||||
<string name="settings_announcements_lap_power">மடியில் ஆற்றல்</string>
|
||||
<string name="power_x_watt">ஆற்றல் {x, plural, = 1 {1 watt} other {# வாட்ச்}}</string>
|
||||
<string name="power_x_watt">ஆற்றல் {x, பன்மை, = 1 {1 watt} பிற {# வாட்ச்}}</string>
|
||||
<string name="power">விசை</string>
|
||||
<string name="help_feature_content">* சி.பி.எச் தடங்களை உருவாக்கவும்\n * இதய துடிப்பு சென்சார் பயன்படுத்தவும் (புளூடூத் லு மட்டும்)\n * குரல் அறிவிப்புகள் (நேரம் அல்லது தூரத்தின் அடிப்படையில்)\n * குறிப்பான்களை உருவாக்கு (படங்கள் உள்ளிட்டவை)\n * முற்றிலும் ஆஃப்லைனில் வேலை செய்கிறது\n * வரைபடத்தில் தடங்களைக் காண்பி (கீழே காண்க)</string>
|
||||
<string name="help_track_map_title">வரைபடத்தில் ஒரு பாதையை எவ்வாறு காண்பிப்பது?</string>
|
||||
@@ -411,13 +411,13 @@
|
||||
<string name="value_smallest_recommended">சிறியது (பரிந்துரைக்கப்படுகிறது)</string>
|
||||
<string name="value_int_seconds">%1$d கள் (பரிந்துரைக்கப்படுகிறது)</string>
|
||||
<string name="voiceIdle">சும்மா ஆக.</string>
|
||||
<string name="voiceHoursPlural">{n, plural, = 1 {1 hour} other {{n,number} மணிநேரம்}}</string>
|
||||
<string name="voiceMinutesPlural">{n, plural, = 1 {1 minute} other {{n,number} நிமிடங்கள்}}</string>
|
||||
<string name="voiceSecondsPlural">{n, plural, = 1 {1 second} other {{n,number} விநாடிகள்}}</string>
|
||||
<string name="voiceSpeedKilometersPerHourPlural">{n, plural, = 1 {1 kilometer per hour} other {{n,number,#. 0} ஒரு மணி நேரத்திற்கு கிலோமீட்டர்}}</string>
|
||||
<string name="voiceSpeedMKnotsPlural">{n, plural, = 1 {1 knot} other {{n,number,#. 0} முடிச்சுகள்}}</string>
|
||||
<string name="voiceSpeedMilesPerHourPlural">{n, plural, = 1 {1 mile per hour} other {{n,number,#. 0} ஒரு மணி நேரத்திற்கு மைல்கள்}}</string>
|
||||
<string name="voiceSpeed">{n, plural, = 1 {1} other {{n,number,#. 0}}}</string>
|
||||
<string name="voiceHoursPlural">{n, பன்மை, = 1 {1 hour} பிற {{n, எண்} மணிநேரம்}}</string>
|
||||
<string name="voiceMinutesPlural">{n, பன்மை, = 1 {1 minute} பிற {{n, எண்} நிமிடங்கள்}}</string>
|
||||
<string name="voiceSecondsPlural">{n, பன்மை, = 1 {1 second} பிற {{n, எண்} விநாடிகள்}}</string>
|
||||
<string name="voiceSpeedKilometersPerHourPlural">{n, பன்மை, = 1 {1 kilometer per hour} பிற {{n, எண்,#. 0} ஒரு மணி நேரத்திற்கு கிலோமீட்டர்}}</string>
|
||||
<string name="voiceSpeedMKnotsPlural">{n, பன்மை, = 1 {1 knot} பிற {{n, எண்,#. 0} முடிச்சுகள்}}</string>
|
||||
<string name="voiceSpeedMilesPerHourPlural">{n, பன்மை, = 1 {1 mile per hour} பிற {{n, எண்,#. 0} ஒரு மணி நேரத்திற்கு மைல்கள்}}</string>
|
||||
<string name="voiceSpeed">{n, பன்மை, = 1 {1} பிற {{n, எண்,#. 0}}}</string>
|
||||
<string name="voice_on_device_speaker_title">சாதனத்தின் ச்பீக்கரைப் பயன்படுத்தவும்</string>
|
||||
<string name="voice_per_kilometer">ஒரு கிலோமீட்டருக்கு</string>
|
||||
<string name="voice_per_nautical_mile">ஒரு கடல் மைலுக்கு</string>
|
||||
@@ -426,10 +426,10 @@
|
||||
<string name="lap_speed">மடியில் விரைவு</string>
|
||||
<string name="speed">சராசரி நகரும் விரைவு</string>
|
||||
<string name="total_distance">மொத்த தூரம்</string>
|
||||
<string name="voiceDistanceKilometersPlural">{n, plural, = 1 {1 kilometer} other {{n,number,#. 0} கிலோமீட்டர்}}</string>
|
||||
<string name="voiceDistanceMilesPlural">{n, plural, = 1 {1 mile} other {{n,number,#. 0} மைல்கள்}}</string>
|
||||
<string name="voiceDistanceNauticalMilesPlural">{n, plural, = 1 {1 nautical mile} other {{n,number,#. 0} கடல் மைல்கள்}}</string>
|
||||
<string name="voiceDistance">{n, plural, = 1 {1 } other {{n,number,#. 0}}}</string>
|
||||
<string name="voiceDistanceKilometersPlural">{n, பன்மை, = 1 {1 kilometer} பிற {{n, எண்,#. 0} கிலோமீட்டர்}}</string>
|
||||
<string name="voiceDistanceMilesPlural">{n, பன்மை, = 1 {1 mile} பிற {{n, எண்,#. 0} மைல்கள்}}</string>
|
||||
<string name="voiceDistanceNauticalMilesPlural">{n, பன்மை, = 1 {1 nautical mile} பிற {{n, எண்,#. 0} கடல் மைல்கள்}}</string>
|
||||
<string name="voiceDistance">{n, பன்மை, = 1 {1 } பிற {{n, எண்,#. 0}}}</string>
|
||||
<string name="average_heart_rate">சராசரி இதய துடிப்பு</string>
|
||||
<string name="current_heart_rate">தற்போதைய இதய துடிப்பு</string>
|
||||
<string name="lap_heart_rate">மடியில் இதய துடிப்பு</string>
|
||||
|
||||
Reference in New Issue
Block a user