diff --git a/src/androidTest/java/de/dennisguse/opentracks/io/file/exporter/KmlTrackExporterTest.java b/src/androidTest/java/de/dennisguse/opentracks/io/file/exporter/KmlTrackExporterTest.java deleted file mode 100644 index a5d96614d..000000000 --- a/src/androidTest/java/de/dennisguse/opentracks/io/file/exporter/KmlTrackExporterTest.java +++ /dev/null @@ -1,59 +0,0 @@ -package de.dennisguse.opentracks.io.file.exporter; - -import static org.junit.Assert.assertEquals; - -import android.content.Context; - -import androidx.test.core.app.ApplicationProvider; - -import org.junit.Test; -import org.junit.runner.RunWith; -import org.junit.runners.JUnit4; - -import java.io.ByteArrayOutputStream; -import java.time.Instant; -import java.time.ZoneOffset; - -import de.dennisguse.opentracks.data.models.TrackPoint; -import de.dennisguse.opentracks.io.file.TrackFileFormat; - -@RunWith(JUnit4.class) -public class KmlTrackExporterTest { - - private final Context context = ApplicationProvider.getApplicationContext(); - - /** - * Sensor data by type should only be created if present in at least on TrackPoint. - */ - @Test - public void writeCloseSegment_only_write_sensordata_if_present() { - String expected = """ - 1970-01-01T00:00:00Z - - 1970-01-01T01:00:00+01:00 - - - - - - - """; - - // given - TrackPoint trackPoint = new TrackPoint(TrackPoint.Type.SEGMENT_START_MANUAL, Instant.ofEpochSecond(0)); - - ByteArrayOutputStream outputStream = new ByteArrayOutputStream(); - KMLTrackExporter kmlTrackWriter = (KMLTrackExporter) TrackFileFormat.KML_WITH_TRACKDETAIL_AND_SENSORDATA.createTrackExporter(context, null); - kmlTrackWriter.prepare(outputStream); - - kmlTrackWriter.writeTrackPoint(ZoneOffset.UTC, trackPoint); - kmlTrackWriter.writeTrackPoint(ZoneOffset.ofTotalSeconds(3600), trackPoint); - - // when - kmlTrackWriter.writeCloseSegment(); - kmlTrackWriter.close(); - - // then - assertEquals(expected, outputStream.toString()); - } -} \ No newline at end of file diff --git a/src/androidTest/java/de/dennisguse/opentracks/io/file/importer/ExportImportTest.java b/src/androidTest/java/de/dennisguse/opentracks/io/file/importer/ExportImportTest.java index ea999b1ae..2a411518d 100644 --- a/src/androidTest/java/de/dennisguse/opentracks/io/file/importer/ExportImportTest.java +++ b/src/androidTest/java/de/dennisguse/opentracks/io/file/importer/ExportImportTest.java @@ -168,9 +168,12 @@ public class ExportImportTest { sendLocation(trackPointCreator, "2020-02-02T02:03:22Z", 3, 16, 10, 13, 15, 10, 0); - sendLocation(trackPointCreator, "2020-02-02T02:03:23Z", 3, 16.001, 10, 27, 15, 10, 0); + trackPointCreator.setClock("2020-02-02T02:03:30Z"); + service.getTrackRecordingManager().onIdle(); - trackPointCreator.setClock("2020-02-02T02:03:24Z"); + sendLocation(trackPointCreator, "2020-02-02T02:03:50Z", 3, 16.001, 10, 27, 15, 10, 0); + + trackPointCreator.setClock("2020-02-02T02:04:00Z"); service.endCurrentTrack(); Track track = contentProviderUtils.getTrack(trackId); @@ -182,7 +185,7 @@ public class ExportImportTest { track = contentProviderUtils.getTrack(trackId); trackPoints = TestDataUtil.getTrackPoints(contentProviderUtils, trackId); markers = contentProviderUtils.getMarkers(trackId); - assertEquals(11, trackPoints.size()); + assertEquals(12, trackPoints.size()); assertEquals(2, markers.size()); } @@ -226,29 +229,29 @@ 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:03:24Z"), importedTrackStatistics.getStopTime()); + assertEquals(Instant.parse("2020-02-02T02:04:00Z"), importedTrackStatistics.getStopTime()); TrackStatistics originalTrackStatistics = track.getTrackStatistics(); assertEquals(originalTrackStatistics.getTotalTime(), importedTrackStatistics.getTotalTime()); - assertEquals(Duration.ofSeconds(20), importedTrackStatistics.getTotalTime()); + assertEquals(Duration.ofSeconds(56), importedTrackStatistics.getTotalTime()); assertEquals(originalTrackStatistics.getMovingTime(), importedTrackStatistics.getMovingTime()); - assertEquals(Duration.ofSeconds(20), importedTrackStatistics.getMovingTime()); + assertEquals(Duration.ofSeconds(26), importedTrackStatistics.getMovingTime()); //TODO Likely too low // Distance assertEquals(originalTrackStatistics.getTotalDistance(), importedTrackStatistics.getTotalDistance()); - assertEquals(222236.70, importedTrackStatistics.getTotalDistance().toM(), 0.01); + assertEquals(222125.53125, importedTrackStatistics.getTotalDistance().toM(), 0.01); //TODO Too low // Speed assertEquals(originalTrackStatistics.getMaxSpeed(), importedTrackStatistics.getMaxSpeed()); - assertEquals(11111.83, importedTrackStatistics.getMaxSpeed().toMPS(), 0.01); + assertEquals(8543.29, importedTrackStatistics.getMaxSpeed().toMPS(), 0.01); assertEquals(originalTrackStatistics.getAverageSpeed(), importedTrackStatistics.getAverageSpeed()); - assertEquals(11111.83, importedTrackStatistics.getAverageSpeed().toMPS(), 0.01); + assertEquals(3966.52, importedTrackStatistics.getAverageSpeed().toMPS(), 0.01); assertEquals(originalTrackStatistics.getAverageMovingSpeed(), importedTrackStatistics.getAverageMovingSpeed()); - assertEquals(11111.83, importedTrackStatistics.getAverageMovingSpeed().toMPS(), 0.01); + assertEquals(8543.28, importedTrackStatistics.getAverageMovingSpeed().toMPS(), 0.01); // Altitude assertEquals(originalTrackStatistics.getMinAltitude(), importedTrackStatistics.getMinAltitude(), 0.01); @@ -368,7 +371,7 @@ public class ExportImportTest { .setAltitudeGain(0f) .setSpeed(Speed.of(15)) .setHorizontalAccuracy(Distance.of(10)), - new TrackPoint(TrackPoint.Type.TRACKPOINT, Instant.parse("2020-02-02T02:03:23Z")) + new TrackPoint(TrackPoint.Type.TRACKPOINT, Instant.parse("2020-02-02T02:03:50Z")) .setLatitude(3) .setLongitude(16.001) .setAltitude(10) @@ -379,24 +382,23 @@ public class ExportImportTest { ), actual); // 3. trackstatistics - TrackStatistics trackStatistics = track.getTrackStatistics(); TrackStatistics importedTrackStatistics = importedTrack.getTrackStatistics(); // Time assertEquals(track.getZoneOffset(), importedTrack.getZoneOffset()); assertEquals(Instant.parse("2020-02-02T02:02:03Z"), importedTrackStatistics.getStartTime()); - assertEquals(Instant.parse("2020-02-02T02:03:23Z"), importedTrackStatistics.getStopTime()); + assertEquals(Instant.parse("2020-02-02T02:03:50Z"), importedTrackStatistics.getStopTime()); - assertEquals(Duration.ofSeconds(80), importedTrackStatistics.getTotalTime()); - assertEquals(Duration.ofSeconds(80), importedTrackStatistics.getMovingTime()); + assertEquals(Duration.ofSeconds(107), importedTrackStatistics.getTotalTime()); + assertEquals(Duration.ofSeconds(107), importedTrackStatistics.getMovingTime()); // Distance assertEquals(222347.85, importedTrackStatistics.getTotalDistance().toM(), 0.01); // Speed - assertEquals(2779.34, importedTrackStatistics.getMaxSpeed().toMPS(), 0.01); - assertEquals(2779.34, importedTrackStatistics.getAverageSpeed().toMPS(), 0.01); - assertEquals(2779.34, 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(10, importedTrackStatistics.getMinAltitude(), 0.01); diff --git a/src/androidTest/java/de/dennisguse/opentracks/io/file/importer/KMLTrackImporterTest.java b/src/androidTest/java/de/dennisguse/opentracks/io/file/importer/KMLTrackImporterTest.java index 228d423ec..64a3d16b0 100644 --- a/src/androidTest/java/de/dennisguse/opentracks/io/file/importer/KMLTrackImporterTest.java +++ b/src/androidTest/java/de/dennisguse/opentracks/io/file/importer/KMLTrackImporterTest.java @@ -186,7 +186,7 @@ public class KMLTrackImporterTest { .setAltitude(439.1626281738281) .setAltitudeGain(0f) .setSpeed(Speed.of(0.1577)), - new TrackPoint(TrackPoint.Type.IDLE, Instant.parse("2020-11-28T17:06:47.888Z")) + new TrackPoint(TrackPoint.Type.TRACKPOINT, Instant.parse("2020-11-28T17:06:47.888Z")) .setLatitude(12.340057) .setLongitude(1.23405) .setAltitude(421.8070983886719) @@ -200,13 +200,13 @@ public class KMLTrackImporterTest { .setAltitude(419.93902587890625) .setAltitudeGain(0f) .setSpeed(Speed.of(0)), - new TrackPoint(TrackPoint.Type.IDLE, Instant.parse("2020-11-28T17:06:56.905Z")) + new TrackPoint(TrackPoint.Type.TRACKPOINT, Instant.parse("2020-11-28T17:06:56.905Z")) .setLatitude(12.340057) .setLongitude(1.23405) .setAltitude(419.9036560058594) .setAltitudeGain(0f) .setSpeed(Speed.of(0)), - new TrackPoint(TrackPoint.Type.IDLE, Instant.parse("2020-11-28T17:07:20.870Z")) + new TrackPoint(TrackPoint.Type.TRACKPOINT, Instant.parse("2020-11-28T17:07:20.870Z")) .setLatitude(12.340082) .setLongitude(1.234046) .setAltitude(417.99432373046875) diff --git a/src/androidTest/res/raw/csv_export.csv b/src/androidTest/res/raw/csv_export.csv index 69c064573..cdefb81a1 100644 --- a/src/androidTest/res/raw/csv_export.csv +++ b/src/androidTest/res/raw/csv_export.csv @@ -8,5 +8,6 @@ "2020-02-02T03:03:20+01:00","SEGMENT_START_MANUAL",,,,,,,,,,,, "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:23+01:00","TRACKPOINT",3,16.001,10,10,,54,0,0,,,, -"2020-02-02T03:03:24+01:00","SEGMENT_END_MANUAL",,,,,,,,,,,, \ No newline at end of file +"2020-02-02T03:03:30+01:00","IDLE",,,,,,,,,,,, +"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",,,,,,,,,,,, \ No newline at end of file diff --git a/src/main/java/de/dennisguse/opentracks/data/models/TrackPoint.java b/src/main/java/de/dennisguse/opentracks/data/models/TrackPoint.java index 6ed2262da..af6049ae9 100644 --- a/src/main/java/de/dennisguse/opentracks/data/models/TrackPoint.java +++ b/src/main/java/de/dennisguse/opentracks/data/models/TrackPoint.java @@ -66,6 +66,7 @@ public class TrackPoint { public final int type_db; + Type(int type_db) { this.type_db = type_db; } diff --git a/src/main/java/de/dennisguse/opentracks/io/file/exporter/GPXTrackExporter.java b/src/main/java/de/dennisguse/opentracks/io/file/exporter/GPXTrackExporter.java index a4de84eab..7a7362a05 100644 --- a/src/main/java/de/dennisguse/opentracks/io/file/exporter/GPXTrackExporter.java +++ b/src/main/java/de/dennisguse/opentracks/io/file/exporter/GPXTrackExporter.java @@ -171,6 +171,9 @@ public class GPXTrackExporter implements TrackExporter { sensorPoints.add(trackPoint); } } + case IDLE -> { + // Not supported as IDLE-TrackPoints have no location. + } default -> throw new RuntimeException("Exporting this TrackPoint type is not implemented: " + trackPoint.getType()); } diff --git a/src/main/java/de/dennisguse/opentracks/io/file/exporter/KMLTrackExporter.java b/src/main/java/de/dennisguse/opentracks/io/file/exporter/KMLTrackExporter.java index 3de5f615a..7ba372323 100644 --- a/src/main/java/de/dennisguse/opentracks/io/file/exporter/KMLTrackExporter.java +++ b/src/main/java/de/dennisguse/opentracks/io/file/exporter/KMLTrackExporter.java @@ -61,6 +61,7 @@ public class KMLTrackExporter implements TrackExporter { public static final String EXTENDED_DATA_TYPE_ACTIVITYTYPE = "type"; + public static final String EXTENDED_DATA_TYPE_TRACKPOINT = "trackpoint_type"; public static final String EXTENDED_DATA_TYPE_SPEED = "speed"; public static final String EXTENDED_DATA_TYPE_DISTANCE = "distance"; public static final String EXTENDED_DATA_TYPE_CADENCE = "cadence"; @@ -83,6 +84,9 @@ public class KMLTrackExporter implements TrackExporter { private final ContentProviderUtils contentProviderUtils; private PrintWriter printWriter; + + private ArrayList trackpointTypeList = new ArrayList<>(); + private final List speedList = new ArrayList<>(); private final List distanceList = new ArrayList<>(); private final List powerList = new ArrayList<>(); @@ -177,12 +181,13 @@ public class KMLTrackExporter implements TrackExporter { writeCloseSegment(); wroteSegment = false; } - case TRACKPOINT -> { + case TRACKPOINT, IDLE -> { if (!wroteSegment) { // Might happen for older data (pre v3.15.0) writeOpenSegment(); wroteSegment = true; } + writeTrackPoint(track.getZoneOffset(), trackPoint); } default -> @@ -309,6 +314,7 @@ public class KMLTrackExporter implements TrackExporter { @VisibleForTesting void writeOpenSegment() { printWriter.println(""); + trackpointTypeList.clear(); speedList.clear(); distanceList.clear(); powerList.clear(); @@ -324,32 +330,35 @@ public class KMLTrackExporter implements TrackExporter { void writeCloseSegment() { printWriter.println(""); printWriter.println(""); + + writeTrackPointType(trackpointTypeList); + if (speedList.stream().anyMatch(Objects::nonNull)) { - writeSimpleArrayData(speedList, EXTENDED_DATA_TYPE_SPEED); + writeSimpleArraySensorData(speedList, EXTENDED_DATA_TYPE_SPEED); } if (distanceList.stream().anyMatch(Objects::nonNull)) { - writeSimpleArrayData(distanceList, EXTENDED_DATA_TYPE_DISTANCE); + writeSimpleArraySensorData(distanceList, EXTENDED_DATA_TYPE_DISTANCE); } if (powerList.stream().anyMatch(Objects::nonNull)) { - writeSimpleArrayData(powerList, EXTENDED_DATA_TYPE_POWER); + writeSimpleArraySensorData(powerList, EXTENDED_DATA_TYPE_POWER); } if (cadenceList.stream().anyMatch(Objects::nonNull)) { - writeSimpleArrayData(cadenceList, EXTENDED_DATA_TYPE_CADENCE); + writeSimpleArraySensorData(cadenceList, EXTENDED_DATA_TYPE_CADENCE); } if (heartRateList.stream().anyMatch(Objects::nonNull)) { - writeSimpleArrayData(heartRateList, EXTENDED_DATA_TYPE_HEART_RATE); + writeSimpleArraySensorData(heartRateList, EXTENDED_DATA_TYPE_HEART_RATE); } if (altitudeGainList.stream().anyMatch(Objects::nonNull)) { - writeSimpleArrayData(altitudeGainList, EXTENDED_DATA_TYPE_ALTITUDE_GAIN); + writeSimpleArraySensorData(altitudeGainList, EXTENDED_DATA_TYPE_ALTITUDE_GAIN); } if (altitudeLossList.stream().anyMatch(Objects::nonNull)) { - writeSimpleArrayData(altitudeLossList, EXTENDED_DATA_TYPE_ALTITUDE_LOSS); + writeSimpleArraySensorData(altitudeLossList, EXTENDED_DATA_TYPE_ALTITUDE_LOSS); } if (accuracyHorizontal.stream().anyMatch(Objects::nonNull)) { - writeSimpleArrayData(accuracyHorizontal, EXTENDED_DATA_TYPE_ACCURACY_HORIZONTAL); + writeSimpleArraySensorData(accuracyHorizontal, EXTENDED_DATA_TYPE_ACCURACY_HORIZONTAL); } if (accuracyVertical.stream().anyMatch(Objects::nonNull)) { - writeSimpleArrayData(accuracyVertical, EXTENDED_DATA_TYPE_ACCURACY_VERTICAL); + writeSimpleArraySensorData(accuracyVertical, EXTENDED_DATA_TYPE_ACCURACY_VERTICAL); } printWriter.println(""); printWriter.println(""); @@ -360,6 +369,8 @@ public class KMLTrackExporter implements TrackExporter { void writeTrackPoint(ZoneOffset zoneOffset, TrackPoint trackPoint) { printWriter.println("" + getTime(zoneOffset, trackPoint.getLocation()) + ""); + trackpointTypeList.add(trackPoint.getType()); + if (trackPoint.hasLocation()) { printWriter.println("" + getCoordinates(trackPoint.getLocation(), " ") + ""); } else { @@ -378,13 +389,7 @@ public class KMLTrackExporter implements TrackExporter { accuracyVertical.add(trackPoint.hasVerticalAccuracy() ? (float) trackPoint.getVerticalAccuracy().toM() : null); } - /** - * Writes the simple array data. - * - * @param list a list of simple array data - * @param name the name of the simple array data - */ - private void writeSimpleArrayData(List list, String name) { + private void writeSimpleArraySensorData(List list, String name) { printWriter.println(""); for (int i = 0; i < list.size(); i++) { Float value = list.get(i); @@ -397,14 +402,14 @@ public class KMLTrackExporter implements TrackExporter { printWriter.println(""); } - /** - * Writes a placemark. - * - * @param name the name - * @param activityType the activityType - * @param description the description - * @param location the location - */ + private void writeTrackPointType(List list) { + printWriter.println(""); + for (TrackPoint.Type value : list) { + printWriter.println("" + value.name() + ""); + } + printWriter.println(""); + } + private void writePlacemark(String name, String activityType, String description, Location location, ZoneOffset zoneOffset) { if (location != null) { printWriter.println(""); diff --git a/src/main/java/de/dennisguse/opentracks/io/file/importer/KmlTrackImporter.java b/src/main/java/de/dennisguse/opentracks/io/file/importer/KmlTrackImporter.java index fffc1f86f..83dc1d51c 100644 --- a/src/main/java/de/dennisguse/opentracks/io/file/importer/KmlTrackImporter.java +++ b/src/main/java/de/dennisguse/opentracks/io/file/importer/KmlTrackImporter.java @@ -94,7 +94,9 @@ public class KmlTrackImporter extends DefaultHandler implements XMLImporter.Trac private final ArrayList whenList = new ArrayList<>(); private final ArrayList locationList = new ArrayList<>(); - private String dataType; + private String dataType; //Could be converted to an ENUM + + private final ArrayList trackpointTypeList = new ArrayList<>(); private final ArrayList sensorSpeedList = new ArrayList<>(); private final ArrayList sensorDistanceList = new ArrayList<>(); private final ArrayList sensorCadenceList = new ArrayList<>(); @@ -287,6 +289,7 @@ public class KmlTrackImporter extends DefaultHandler implements XMLImporter.Trac locationList.clear(); whenList.clear(); + trackpointTypeList.clear(); sensorSpeedList.clear(); sensorDistanceList.clear(); sensorHeartRateList.clear(); @@ -313,12 +316,14 @@ public class KmlTrackImporter extends DefaultHandler implements XMLImporter.Trac trackPoint.setLocation(location); } + if (i < trackpointTypeList.size() && trackpointTypeList.get(i) != null) { + + TrackPoint.Type type = TrackPoint.Type.valueOf(trackpointTypeList.get(i)); + trackPoint.setType(type); + } + if (i < sensorSpeedList.size() && sensorSpeedList.get(i) != null) { trackPoint.setSpeed(Speed.of(sensorSpeedList.get(i))); - - if (TrackPoint.IDLE_SPEED.greaterOrEqualThan(trackPoint.getSpeed())) { - trackPoint.setType(TrackPoint.Type.IDLE); - } } if (i < sensorDistanceList.size() && sensorDistanceList.get(i) != null) { trackPoint.setSensorDistance(Distance.of(sensorDistanceList.get(i))); @@ -403,6 +408,10 @@ public class KmlTrackImporter extends DefaultHandler implements XMLImporter.Trac } private void onExtendedDataValueEnd() throws SAXException { + if (dataType.equals(KMLTrackExporter.EXTENDED_DATA_TYPE_TRACKPOINT)) { + trackpointTypeList.add(content != null ? content.trim() : null); + return; + } Float value = null; if (content != null) { content = content.trim(); diff --git a/src/main/java/de/dennisguse/opentracks/services/TrackRecordingManager.java b/src/main/java/de/dennisguse/opentracks/services/TrackRecordingManager.java index 7f7706699..fe4bbbd8a 100644 --- a/src/main/java/de/dennisguse/opentracks/services/TrackRecordingManager.java +++ b/src/main/java/de/dennisguse/opentracks/services/TrackRecordingManager.java @@ -10,6 +10,7 @@ import android.util.Log; import android.util.Pair; import androidx.annotation.NonNull; +import androidx.annotation.VisibleForTesting; import java.time.Duration; import java.time.ZoneOffset; @@ -29,7 +30,7 @@ import de.dennisguse.opentracks.stats.TrackStatistics; import de.dennisguse.opentracks.stats.TrackStatisticsUpdater; import de.dennisguse.opentracks.util.TrackNameUtils; -class TrackRecordingManager implements SharedPreferences.OnSharedPreferenceChangeListener { +public class TrackRecordingManager implements SharedPreferences.OnSharedPreferenceChangeListener { private static final String TAG = TrackRecordingManager.class.getSimpleName(); @@ -162,7 +163,8 @@ class TrackRecordingManager implements SharedPreferences.OnSharedPreferenceChang return new Marker.Id(ContentUris.parseId(uri)); } - void onIdle() { + @VisibleForTesting(otherwise = VisibleForTesting.PACKAGE_PRIVATE) + public void onIdle() { Log.d(TAG, "Becoming idle"); onNewTrackPoint(trackPointCreator.createIdle()); } diff --git a/src/main/java/de/dennisguse/opentracks/stats/TrackStatisticsUpdater.java b/src/main/java/de/dennisguse/opentracks/stats/TrackStatisticsUpdater.java index 05e9baa72..6b56844fc 100644 --- a/src/main/java/de/dennisguse/opentracks/stats/TrackStatisticsUpdater.java +++ b/src/main/java/de/dennisguse/opentracks/stats/TrackStatisticsUpdater.java @@ -79,7 +79,7 @@ public class TrackStatisticsUpdater { this.trackStatistics = new TrackStatistics(toCopy.trackStatistics); this.lastTrackPoint = toCopy.lastTrackPoint; - this.idle = idle; + this.idle = toCopy.idle; resetAverageHeartRate(); } @@ -181,6 +181,7 @@ public class TrackStatisticsUpdater { currentSegment.reset(trackPoint.getTime()); lastTrackPoint = null; + idle = false; resetAverageHeartRate(); }