diff --git a/src/androidTest/java/de/dennisguse/opentracks/io/file/exporter/KmlTrackWriterTest.java b/src/androidTest/java/de/dennisguse/opentracks/io/file/exporter/KmlTrackExporterTest.java similarity index 89% rename from src/androidTest/java/de/dennisguse/opentracks/io/file/exporter/KmlTrackWriterTest.java rename to src/androidTest/java/de/dennisguse/opentracks/io/file/exporter/KmlTrackExporterTest.java index b2ef08a6f..4cfe9fcbe 100644 --- a/src/androidTest/java/de/dennisguse/opentracks/io/file/exporter/KmlTrackWriterTest.java +++ b/src/androidTest/java/de/dennisguse/opentracks/io/file/exporter/KmlTrackExporterTest.java @@ -17,7 +17,7 @@ import de.dennisguse.opentracks.io.file.TrackFileFormat; import static org.junit.Assert.assertEquals; @RunWith(JUnit4.class) -public class KmlTrackWriterTest { +public class KmlTrackExporterTest { private final Context context = ApplicationProvider.getApplicationContext(); @@ -38,7 +38,7 @@ public class KmlTrackWriterTest { TrackPoint trackPoint = new TrackPoint(TrackPoint.Type.SEGMENT_START_MANUAL, Instant.ofEpochSecond(0)); ByteArrayOutputStream outputStream = new ByteArrayOutputStream(); - KmlTrackWriter kmlTrackWriter = (KmlTrackWriter) TrackFileFormat.KML_WITH_TRACKDETAIL_AND_SENSORDATA.newTrackWriter(context); + KMLTrackExporter kmlTrackWriter = (KMLTrackExporter) TrackFileFormat.KML_WITH_TRACKDETAIL_AND_SENSORDATA.createTrackExporter(context); kmlTrackWriter.prepare(outputStream); kmlTrackWriter.writeTrackPoint(trackPoint); 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 1419c9c2d..742626b85 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 @@ -27,6 +27,7 @@ import java.util.ArrayList; import java.util.List; import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeoutException; +import java.util.stream.Collectors; import de.dennisguse.opentracks.R; import de.dennisguse.opentracks.content.data.Marker; @@ -67,8 +68,6 @@ public class ExportImportTest { if (Looper.myLooper() == null) Looper.prepare(); } - private TrackRecordingServiceInterface service; - private final Context context = ApplicationProvider.getApplicationContext(); private final ContentProviderUtils contentProviderUtils = new ContentProviderUtils(context); @@ -85,8 +84,8 @@ public class ExportImportTest { private Track.Id importTrackId; @Before - public void setUp() throws TimeoutException, InterruptedException { - service = ((TrackRecordingServiceInterface) mServiceRule.bindService(new Intent(context, TrackRecordingService.class))); + public void setUp() throws TimeoutException { + TrackRecordingServiceInterface service = ((TrackRecordingServiceInterface) mServiceRule.bindService(new Intent(context, TrackRecordingService.class))); trackId = service.startNewTrack(); @@ -138,7 +137,7 @@ public class ExportImportTest { // given Track track = contentProviderUtils.getTrack(trackId); - TrackExporter trackExporter = TrackFileFormat.KML_WITH_TRACKDETAIL.newTrackExporter(context); + TrackExporter trackExporter = TrackFileFormat.KML_WITH_TRACKDETAIL.createTrackExporter(context); // when // 1. export @@ -162,10 +161,10 @@ public class ExportImportTest { assertEquals(track.getUuid(), importedTrack.getUuid()); // 2. trackpoints - assertTrackpoints(false, false, false, false, false); + assertTrackpoints(trackPoints, false, false, false, false, false); // 3. trackstatistics - assertTrackStatistics(false); + assertTrackStatistics(false, false); // 4. markers assertMarkers(); @@ -177,7 +176,7 @@ public class ExportImportTest { // given Track track = contentProviderUtils.getTrack(trackId); - TrackExporter trackExporter = TrackFileFormat.KML_WITH_TRACKDETAIL_AND_SENSORDATA.newTrackExporter(context); + TrackExporter trackExporter = TrackFileFormat.KML_WITH_TRACKDETAIL_AND_SENSORDATA.createTrackExporter(context); // when // 1. export @@ -200,10 +199,10 @@ public class ExportImportTest { assertEquals(track.getIcon(), importedTrack.getIcon()); // 2. trackpoints - assertTrackpoints(true, true, true, true, true); + assertTrackpoints(trackPoints, true, true, true, true, true); // 2. trackstatistics - assertTrackStatistics(true); + assertTrackStatistics(false, true); // 4. markers assertMarkers(); @@ -216,7 +215,7 @@ public class ExportImportTest { PreferencesUtils.setBoolean(context, R.string.import_prevent_reimport_key, true); Track track = contentProviderUtils.getTrack(trackId); - TrackExporter trackExporter = TrackFileFormat.KML_WITH_TRACKDETAIL_AND_SENSORDATA.newTrackExporter(context); + TrackExporter trackExporter = TrackFileFormat.KML_WITH_TRACKDETAIL_AND_SENSORDATA.createTrackExporter(context); // when // 1. export @@ -271,7 +270,7 @@ public class ExportImportTest { // given Track track = contentProviderUtils.getTrack(trackId); - TrackExporter trackExporter = TrackFileFormat.GPX.newTrackExporter(context); + TrackExporter trackExporter = TrackFileFormat.GPX.createTrackExporter(context); // when // 1. export @@ -296,10 +295,16 @@ public class ExportImportTest { //assertEquals(track.getIcon(), trackImported.getIcon()); // 2. trackpoints - assertTrackpoints(false, true, true, true, true); + // The GPX exporter does not support exporting TrackPoints without lat/lng. + // Therefore, the track segmentation is changes. + List trackPointsWithCoordinates = trackPoints.stream().filter(it -> TrackPoint.Type.SEGMENT_START_AUTOMATIC.equals(it.getType()) || TrackPoint.Type.TRACKPOINT.equals(it.getType())).collect(Collectors.toList()); + trackPointsWithCoordinates.get(0).setType(TrackPoint.Type.SEGMENT_START_AUTOMATIC); + trackPointsWithCoordinates.get(3).setType(TrackPoint.Type.SEGMENT_START_AUTOMATIC); + + assertTrackpoints(trackPointsWithCoordinates, false, true, true, true, true); // 3. trackstatistics - assertTrackStatistics(true); + assertTrackStatistics(true, true); // 4. markers assertMarkers(); @@ -312,7 +317,7 @@ public class ExportImportTest { PreferencesUtils.setBoolean(context, R.string.import_prevent_reimport_key, true); Track track = contentProviderUtils.getTrack(trackId); - TrackExporter trackExporter = TrackFileFormat.GPX.newTrackExporter(context); + TrackExporter trackExporter = TrackFileFormat.GPX.createTrackExporter(context); // when // 1. export @@ -349,8 +354,9 @@ public class ExportImportTest { } } - private void assertTrackpoints(boolean verifyPower, boolean verifyHeartrate, boolean verifyCadence, boolean verifyElevationGain, boolean verifyElevationLoss) { + private void assertTrackpoints(List trackPoints, boolean verifyPower, boolean verifyHeartrate, boolean verifyCadence, boolean verifyElevationGain, boolean verifyElevationLoss) { List importedTrackPoints = contentProviderUtils.getTrackPoints(importTrackId); + assertEquals(trackPoints.size(), importedTrackPoints.size()); for (int i = 0; i < trackPoints.size(); i++) { @@ -402,7 +408,7 @@ public class ExportImportTest { } } - private void assertTrackStatistics(boolean verifyElevationGainAndLoss) { + private void assertTrackStatistics(boolean isGpx, boolean verifyElevationGainAndLoss) { Track importedTrack = contentProviderUtils.getTrack(importTrackId); assertNotNull(importedTrack.getTrackStatistics()); @@ -412,19 +418,21 @@ public class ExportImportTest { // Time assertTrue(trackStatistics.getStartTime().isBefore(trackStatistics.getStopTime())); //Just to be sure. - assertEquals(trackStatistics.getStartTime(), importedTrackStatistics.getStartTime()); - assertEquals(trackStatistics.getStopTime(), importedTrackStatistics.getStopTime()); + if (!isGpx) { + assertEquals(trackStatistics.getStartTime(), importedTrackStatistics.getStartTime()); + assertEquals(trackStatistics.getStopTime(), importedTrackStatistics.getStopTime()); - assertEquals(trackStatistics.getTotalTime(), importedTrackStatistics.getTotalTime()); - assertEquals(trackStatistics.getMovingTime(), importedTrackStatistics.getMovingTime()); + assertEquals(trackStatistics.getTotalTime(), importedTrackStatistics.getTotalTime()); + assertEquals(trackStatistics.getMovingTime(), importedTrackStatistics.getMovingTime()); - // Distance - assertEquals(trackStatistics.getTotalDistance(), importedTrackStatistics.getTotalDistance(), 0.01); + // Distance + assertEquals(trackStatistics.getTotalDistance(), importedTrackStatistics.getTotalDistance(), 0.01); - // Speed - assertEquals(trackStatistics.getMaxSpeed(), importedTrackStatistics.getMaxSpeed(), 0.01); - assertEquals(trackStatistics.getAverageSpeed(), importedTrackStatistics.getAverageSpeed(), 0.01); - assertEquals(trackStatistics.getAverageMovingSpeed(), importedTrackStatistics.getAverageMovingSpeed(), 0.01); + // Speed + assertEquals(trackStatistics.getMaxSpeed(), importedTrackStatistics.getMaxSpeed(), 0.01); + assertEquals(trackStatistics.getAverageSpeed(), importedTrackStatistics.getAverageSpeed(), 0.01); + assertEquals(trackStatistics.getAverageMovingSpeed(), importedTrackStatistics.getAverageMovingSpeed(), 0.01); + } // Elevation assertEquals(trackStatistics.getMinElevation(), importedTrackStatistics.getMinElevation(), 0.01); diff --git a/src/main/java/de/dennisguse/opentracks/content/data/TrackPoint.java b/src/main/java/de/dennisguse/opentracks/content/data/TrackPoint.java index b238eae8c..ae51ecdbc 100644 --- a/src/main/java/de/dennisguse/opentracks/content/data/TrackPoint.java +++ b/src/main/java/de/dennisguse/opentracks/content/data/TrackPoint.java @@ -36,7 +36,7 @@ import java.util.Objects; * Drawbacks: * * GPS-provided timestamp might be more precise (but also have GPS week rollover) * * System clock might be changed (and thus non-monotonic) - * TODO: if these might be problems, we need to store both timestamps. + * TODO: if these might be a problem, we need to store both timestamps. * * @author Sandor Dornbush */ @@ -177,7 +177,7 @@ public class TrackPoint { return latitude != null || longitude != null; } - @Nullable + @NonNull public Location getLocation() { Location location = new Location(""); location.setTime(time.toEpochMilli()); diff --git a/src/main/java/de/dennisguse/opentracks/content/provider/ShareContentProvider.java b/src/main/java/de/dennisguse/opentracks/content/provider/ShareContentProvider.java index 65fe9449d..f0daa9052 100644 --- a/src/main/java/de/dennisguse/opentracks/content/provider/ShareContentProvider.java +++ b/src/main/java/de/dennisguse/opentracks/content/provider/ShareContentProvider.java @@ -221,7 +221,7 @@ public class ShareContentProvider extends CustomContentProvider { } } - final TrackExporter trackExporter = getTrackFileFormat(uri).newTrackExporter(getContext()); + final TrackExporter trackExporter = getTrackFileFormat(uri).createTrackExporter(getContext()); PipeDataWriter pipeDataWriter = (output, uri1, mimeType, opts, args) -> { try (FileOutputStream fileOutputStream = new FileOutputStream(output.getFileDescriptor())) { diff --git a/src/main/java/de/dennisguse/opentracks/io/file/TrackFileFormat.java b/src/main/java/de/dennisguse/opentracks/io/file/TrackFileFormat.java index 09a17da86..73d03b43a 100644 --- a/src/main/java/de/dennisguse/opentracks/io/file/TrackFileFormat.java +++ b/src/main/java/de/dennisguse/opentracks/io/file/TrackFileFormat.java @@ -6,12 +6,10 @@ import java.util.Locale; import de.dennisguse.opentracks.R; import de.dennisguse.opentracks.content.provider.ContentProviderUtils; -import de.dennisguse.opentracks.io.file.exporter.FileTrackExporter; -import de.dennisguse.opentracks.io.file.exporter.GpxTrackWriter; -import de.dennisguse.opentracks.io.file.exporter.KmlTrackWriter; +import de.dennisguse.opentracks.io.file.exporter.GPXTrackExporter; +import de.dennisguse.opentracks.io.file.exporter.KMLTrackExporter; import de.dennisguse.opentracks.io.file.exporter.KmzTrackExporter; import de.dennisguse.opentracks.io.file.exporter.TrackExporter; -import de.dennisguse.opentracks.io.file.exporter.TrackWriter; /** * Definition of all possible track formats. @@ -20,8 +18,8 @@ public enum TrackFileFormat { KML_ONLY_TRACK { @Override - public TrackWriter newTrackWriter(Context context) { - return new KmlTrackWriter(context, false, false, false); + public TrackExporter createTrackExporter(Context context) { + return new KMLTrackExporter(context, false, false, false); } @Override @@ -35,8 +33,8 @@ public enum TrackFileFormat { }, KML_WITH_TRACKDETAIL { @Override - public TrackWriter newTrackWriter(Context context) { - return new KmlTrackWriter(context, true, false, false); + public TrackExporter createTrackExporter(Context context) { + return new KMLTrackExporter(context, true, false, false); } @Override @@ -50,8 +48,8 @@ public enum TrackFileFormat { }, KML_WITH_TRACKDETAIL_AND_SENSORDATA { @Override - public TrackWriter newTrackWriter(Context context) { - return new KmlTrackWriter(context, true, true, false); + public TrackExporter createTrackExporter(Context context) { + return new KMLTrackExporter(context, true, true, false); } @Override @@ -64,16 +62,12 @@ public enum TrackFileFormat { } }, KMZ_ONLY_TRACK { - private static final boolean exportPhotos = false; @Override - public TrackWriter newTrackWriter(Context context) { - return new KmlTrackWriter(context, false, false, exportPhotos); - } - - public TrackExporter newTrackExporter(Context context) { - return newKmzTrackExporter(context, this.newTrackWriter(context), exportPhotos); + public TrackExporter createTrackExporter(Context context) { + KMLTrackExporter exporter = new KMLTrackExporter(context, false, false, exportPhotos); + return new KmzTrackExporter(context, new ContentProviderUtils(context), exporter, exportPhotos); } @Override @@ -95,12 +89,9 @@ public enum TrackFileFormat { private static final boolean exportPhotos = false; @Override - public TrackWriter newTrackWriter(Context context) { - return new KmlTrackWriter(context, true, false, exportPhotos); - } - - public TrackExporter newTrackExporter(Context context) { - return newKmzTrackExporter(context, this.newTrackWriter(context), exportPhotos); + public TrackExporter createTrackExporter(Context context) { + KMLTrackExporter exporter = new KMLTrackExporter(context, true, false, exportPhotos); + return new KmzTrackExporter(context, new ContentProviderUtils(context), exporter, exportPhotos); } @Override @@ -123,8 +114,9 @@ public enum TrackFileFormat { private static final boolean exportPhotos = false; @Override - public TrackWriter newTrackWriter(Context context) { - return new KmlTrackWriter(context, true, true, exportPhotos); + public TrackExporter createTrackExporter(Context context) { + KMLTrackExporter exporter = new KMLTrackExporter(context, true, true, exportPhotos); + return new KmzTrackExporter(context, new ContentProviderUtils(context), exporter, exportPhotos); } @Override @@ -132,10 +124,6 @@ public enum TrackFileFormat { return MIME_KMZ; } - public TrackExporter newTrackExporter(Context context) { - return newKmzTrackExporter(context, this.newTrackWriter(context), exportPhotos); - } - public String getExtension() { return "kmz"; } @@ -152,8 +140,9 @@ public enum TrackFileFormat { private static final boolean exportPhotos = true; @Override - public TrackWriter newTrackWriter(Context context) { - return new KmlTrackWriter(context, true, true, exportPhotos); + public TrackExporter createTrackExporter(Context context) { + KMLTrackExporter exporter = new KMLTrackExporter(context, true, true, exportPhotos); + return new KmzTrackExporter(context, new ContentProviderUtils(context), exporter, exportPhotos); } @Override @@ -161,10 +150,6 @@ public enum TrackFileFormat { return MIME_KMZ; } - public TrackExporter newTrackExporter(Context context) { - return newKmzTrackExporter(context, newTrackWriter(context), exportPhotos); - } - public String getExtension() { return "kmz"; } @@ -177,8 +162,8 @@ public enum TrackFileFormat { }, GPX { @Override - public TrackWriter newTrackWriter(Context context) { - return new GpxTrackWriter(context.getString(R.string.app_name)); + public TrackExporter createTrackExporter(Context context) { + return new GPXTrackExporter(new ContentProviderUtils(context), context.getString(R.string.app_name)); } @Override @@ -195,31 +180,17 @@ public enum TrackFileFormat { private static final String MIME_KML = "application/vnd.google-earth.kml+xml"; - private static TrackExporter newKmzTrackExporter(Context context, TrackWriter trackWriter, boolean exportPhotos) { - ContentProviderUtils contentProviderUtils = new ContentProviderUtils(context); - - FileTrackExporter fileTrackExporter = new FileTrackExporter(contentProviderUtils, trackWriter); - - return new KmzTrackExporter(context, contentProviderUtils, fileTrackExporter, exportPhotos); - } - /** * Returns the mime type for each format. */ public abstract String getMimeType(); - public TrackExporter newTrackExporter(Context context) { - ContentProviderUtils contentProviderUtils = new ContentProviderUtils(context); - TrackWriter trackWriter = newTrackWriter(context); - return new FileTrackExporter(contentProviderUtils, trackWriter); - } - /** * Creates a new track writer for the format. * * @param context the context */ - public abstract TrackWriter newTrackWriter(Context context); + public abstract TrackExporter createTrackExporter(Context context); /** * Returns the file extension for each format. @@ -234,7 +205,7 @@ public enum TrackFileFormat { } /** - * Returns the name of for each format. + * Returns the name for each format. */ public String getName() { return this.name().toLowerCase(Locale.US); diff --git a/src/main/java/de/dennisguse/opentracks/io/file/exporter/FileTrackExporter.java b/src/main/java/de/dennisguse/opentracks/io/file/exporter/FileTrackExporter.java deleted file mode 100644 index 42581efa6..000000000 --- a/src/main/java/de/dennisguse/opentracks/io/file/exporter/FileTrackExporter.java +++ /dev/null @@ -1,190 +0,0 @@ -/* - * Copyright 2011 Google Inc. - * - * Licensed under the Apache License, Version 2.0 (the "License"); you may not - * use this file except in compliance with the License. You may obtain a copy of - * the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations under - * the License. - */ - -package de.dennisguse.opentracks.io.file.exporter; - -import android.database.Cursor; -import android.util.Log; - -import androidx.annotation.NonNull; - -import java.io.OutputStream; -import java.time.Duration; -import java.time.Instant; - -import de.dennisguse.opentracks.content.data.Marker; -import de.dennisguse.opentracks.content.data.Track; -import de.dennisguse.opentracks.content.data.TrackPoint; -import de.dennisguse.opentracks.content.provider.ContentProviderUtils; -import de.dennisguse.opentracks.content.provider.TrackPointIterator; - -/** - * Track Writer for writing tracks to an {@link OutputStream}. - * - * @author Sandor Dornbush - * @author Rodrigo Damazio - */ -public class FileTrackExporter implements TrackExporter { - - private static final String TAG = FileTrackExporter.class.getSimpleName(); - - private final ContentProviderUtils contentProviderUtils; - private final TrackWriter trackWriter; - - /** - * Constructor. - * - * @param contentProviderUtils the content provider utils - * @param trackWriter the track writer - */ - public FileTrackExporter(ContentProviderUtils contentProviderUtils, TrackWriter trackWriter) { - this.contentProviderUtils = contentProviderUtils; - this.trackWriter = trackWriter; - } - - @Override - public boolean writeTrack(Track track, @NonNull OutputStream outputStream) { - return writeTrack(new Track[]{track}, outputStream); - } - - @Override - public boolean writeTrack(Track[] tracks, @NonNull OutputStream outputStream) { - try { - trackWriter.prepare(outputStream); - trackWriter.writeHeader(tracks); - for (Track track : tracks) { - writeMarkers(track); - } - boolean hasMultipleTracks = tracks.length > 1; - if (hasMultipleTracks) { - trackWriter.writeMultiTrackBegin(); - } - //TODO Why use startTime of first track for the others? - Instant startTime = tracks[0].getTrackStatistics().getStartTime(); - for (Track track : tracks) { - Duration offset = Duration.between(track.getTrackStatistics().getStartTime(), startTime); - writeLocations(track, offset); - } - if (hasMultipleTracks) { - trackWriter.writeMultiTrackEnd(); - } - trackWriter.writeFooter(); - trackWriter.close(); - - return true; - } catch (InterruptedException e) { - Log.e(TAG, "Thread interrupted", e); - return false; - } - } - - private void writeMarkers(Track track) throws InterruptedException { - /* - * TODO: Stream through the markers in chunks. - * I am leaving the number of markers very high which should not be a problem, because we don't try to load them into objects all at the same time. - */ - boolean hasMarkers = false; - try (Cursor cursor = contentProviderUtils.getMarkerCursor(track.getId(), null, -1)) { - if (cursor != null && cursor.moveToFirst()) { - for (int i = 0; i < cursor.getCount(); i++) { - if (Thread.interrupted()) { - throw new InterruptedException(); - } - if (!hasMarkers) { - trackWriter.writeBeginMarkers(track); - hasMarkers = true; - } - Marker marker = contentProviderUtils.createMarker(cursor); - trackWriter.writeMarker(marker); - - cursor.moveToNext(); - } - } - } - if (hasMarkers) { - trackWriter.writeEndMarkers(); - } - } - - /** - * Writes the locations. - */ - private void writeLocations(Track track, Duration offset) throws InterruptedException { - boolean wroteTrack = false; - boolean wroteSegment = false; - - try (TrackPointIterator trackPointIterator = contentProviderUtils.getTrackPointLocationIterator(track.getId(), null)) { - while (trackPointIterator.hasNext()) { - if (Thread.interrupted()) throw new InterruptedException(); - - TrackPoint trackPoint = trackPointIterator.next(); - setLocationTime(trackPoint, offset); - - if (!wroteTrack) { - trackWriter.writeBeginTrack(track, trackPoint); - wroteTrack = true; - } - - boolean newSegment = TrackPoint.Type.SEGMENT_START_AUTOMATIC.equals(trackPoint.getType()) || TrackPoint.Type.SEGMENT_START_MANUAL.equals(trackPoint.getType()); - if (newSegment) { - if (wroteSegment) trackWriter.writeCloseSegment(); - trackWriter.writeOpenSegment(); - trackWriter.writeTrackPoint(trackPoint); - wroteSegment = true; - continue; - } - if (TrackPoint.Type.SEGMENT_END_MANUAL.equals(trackPoint.getType())) { - if (!wroteSegment) trackWriter.writeOpenSegment(); - trackWriter.writeTrackPoint(trackPoint); - trackWriter.writeCloseSegment(); - wroteSegment = false; - continue; - } - - trackWriter.writeTrackPoint(trackPoint); - } - - if (wroteSegment) { - // Should not be necessary as tracks should end with SEGMENT_END_MANUAL. - //Close the last segment - trackWriter.writeCloseSegment(); - } - - if (wroteTrack) { - TrackPoint lastValidTrackPoint = contentProviderUtils.getLastValidTrackPoint(track.getId()); - setLocationTime(lastValidTrackPoint, offset); - trackWriter.writeEndTrack(track, lastValidTrackPoint); - } else { - // Write an empty track - trackWriter.writeBeginTrack(track, null); - trackWriter.writeEndTrack(track, null); - } - } - } - - /** - * Sets a trackPoint time. - * - * @param trackPoint the trackPoint - * @param offset the time offset - */ - //TODO Why? - private void setLocationTime(TrackPoint trackPoint, Duration offset) { - if (trackPoint != null) { - trackPoint.setTime(trackPoint.getTime().minus(offset)); - } - } -} diff --git a/src/main/java/de/dennisguse/opentracks/io/file/exporter/GpxTrackWriter.java b/src/main/java/de/dennisguse/opentracks/io/file/exporter/GPXTrackExporter.java similarity index 62% rename from src/main/java/de/dennisguse/opentracks/io/file/exporter/GpxTrackWriter.java rename to src/main/java/de/dennisguse/opentracks/io/file/exporter/GPXTrackExporter.java index 7128c4f16..8be9bdd3c 100644 --- a/src/main/java/de/dennisguse/opentracks/io/file/exporter/GpxTrackWriter.java +++ b/src/main/java/de/dennisguse/opentracks/io/file/exporter/GPXTrackExporter.java @@ -1,5 +1,5 @@ /* - * Copyright 2008 Google Inc. + * Copyright 2011 Google Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not * use this file except in compliance with the License. You may obtain a copy of @@ -16,6 +16,11 @@ package de.dennisguse.opentracks.io.file.exporter; +import android.database.Cursor; +import android.util.Log; + +import androidx.annotation.NonNull; + import java.io.OutputStream; import java.io.PrintWriter; import java.text.NumberFormat; @@ -24,15 +29,21 @@ import java.util.Locale; import de.dennisguse.opentracks.content.data.Marker; import de.dennisguse.opentracks.content.data.Track; import de.dennisguse.opentracks.content.data.TrackPoint; +import de.dennisguse.opentracks.content.provider.ContentProviderUtils; +import de.dennisguse.opentracks.content.provider.TrackPointIterator; import de.dennisguse.opentracks.util.StringUtils; /** - * Write track as GPX to a file. + * Convert {@link Track} incl. {@link Marker} and {@link TrackPoint} to KML. + * NOTE: + * * does not export {@link TrackPoint} without a latitude/longitude (not supported by GPX 1.1). * * @author Sandor Dornbush + * @author Rodrigo Damazio */ -//TODO Export markers -public class GpxTrackWriter implements TrackWriter { +public class GPXTrackExporter implements TrackExporter { + + private static final String TAG = GPXTrackExporter.class.getSimpleName(); private static final NumberFormat ELEVATION_FORMAT = NumberFormat.getInstance(Locale.US); private static final NumberFormat COORDINATE_FORMAT = NumberFormat.getInstance(Locale.US); @@ -62,19 +73,106 @@ public class GpxTrackWriter implements TrackWriter { CADENCE_FORMAT.setGroupingUsed(false); } + private final ContentProviderUtils contentProviderUtils; + private final String creator; private PrintWriter printWriter; - public GpxTrackWriter(String creator) { + public GPXTrackExporter(ContentProviderUtils contentProviderUtils, String creator) { + this.contentProviderUtils = contentProviderUtils; this.creator = creator; } @Override + public boolean writeTrack(Track track, @NonNull OutputStream outputStream) { + try { + prepare(outputStream); + writeHeader(track); + + writeMarkers(track); + + writeLocations(track); + + writeFooter(); + close(); + + return true; + } catch (InterruptedException e) { + Log.e(TAG, "Thread interrupted", e); + return false; + } + } + + @Override + public boolean writeTrack(Track[] tracks, @NonNull OutputStream outputStream) { + throw new UnsupportedOperationException(); + } + + private void writeLocations(Track track) throws InterruptedException { + boolean wroteTrack = false; + boolean wroteSegment = false; + + try (TrackPointIterator trackPointIterator = contentProviderUtils.getTrackPointLocationIterator(track.getId(), null)) { + while (trackPointIterator.hasNext()) { + if (Thread.interrupted()) throw new InterruptedException(); + + TrackPoint trackPoint = trackPointIterator.next(); + + if (!wroteTrack) { + writeBeginTrack(track, trackPoint); + wroteTrack = true; + } + + switch (trackPoint.getType()) { + case SEGMENT_START_MANUAL: + if (wroteSegment) writeCloseSegment(); + writeOpenSegment(); + wroteSegment = true; + Log.i(TAG, "Exporting " + TrackPoint.Type.SEGMENT_START_MANUAL.name() + " is not supported."); + break; + case SEGMENT_END_MANUAL: + writeCloseSegment(); + wroteSegment = false; + Log.i(TAG, "Exporting " + TrackPoint.Type.SEGMENT_END_MANUAL.name() + " is not supported."); + break; + case SEGMENT_START_AUTOMATIC: + if (wroteSegment) writeCloseSegment(); + writeOpenSegment(); + wroteSegment = true; + writeTrackPoint(trackPoint); + break; + case TRACKPOINT: + if (!wroteSegment) { + // Might happen for older data (pre v3.15.0) + writeOpenSegment(); + wroteSegment = true; + } + writeTrackPoint(trackPoint); + break; + } + } + + if (wroteSegment) { + // Should not be necessary as tracks should end with SEGMENT_END_MANUAL. + // Anyhow, make sure that the last segment is closed. + writeCloseSegment(); + } + + if (wroteTrack) { + TrackPoint lastValidTrackPoint = contentProviderUtils.getLastValidTrackPoint(track.getId()); + writeEndTrack(track, lastValidTrackPoint); + } else { + // Write an empty track + writeBeginTrack(track, null); + writeEndTrack(track, null); + } + } + } + public void prepare(OutputStream outputStream) { this.printWriter = new PrintWriter(outputStream); } - @Override public void close() { if (printWriter != null) { printWriter.flush(); @@ -82,8 +180,8 @@ public class GpxTrackWriter implements TrackWriter { } } - @Override - public void writeHeader(Track[] tracks) { + + public void writeHeader(Track track) { if (printWriter != null) { printWriter.println(""); printWriter.println(""); - Track track = tracks[0]; printWriter.println("" + StringUtils.formatCData(track.getName()) + ""); printWriter.println("" + StringUtils.formatCData(track.getDescription()) + ""); printWriter.println(""); } } - @Override public void writeFooter() { if (printWriter != null) { printWriter.println(""); } } - @Override - public void writeBeginMarkers(Track track) { - // Do nothing + private void writeMarkers(Track track) throws InterruptedException { + try (Cursor cursor = contentProviderUtils.getMarkerCursor(track.getId(), null, -1)) { + if (cursor != null && cursor.moveToFirst()) { + for (int i = 0; i < cursor.getCount(); i++) { + if (Thread.interrupted()) { + throw new InterruptedException(); + } + Marker marker = contentProviderUtils.createMarker(cursor); + writeMarker(marker); + + cursor.moveToNext(); + } + } + } } - @Override - public void writeEndMarkers() { - // Do nothing - } - - @Override public void writeMarker(Marker marker) { if (printWriter != null) { printWriter.println(""); @@ -142,17 +243,6 @@ public class GpxTrackWriter implements TrackWriter { } } - @Override - public void writeMultiTrackBegin() { - // Do nothing - } - - @Override - public void writeMultiTrackEnd() { - // Do nothing - } - - @Override public void writeBeginTrack(Track track, TrackPoint startTrackPoint) { if (printWriter != null) { printWriter.println(""); @@ -167,28 +257,24 @@ public class GpxTrackWriter implements TrackWriter { } } - @Override public void writeEndTrack(Track track, TrackPoint endTrackPoint) { if (printWriter != null) { printWriter.println(""); } } - @Override public void writeOpenSegment() { printWriter.println(""); } - @Override public void writeCloseSegment() { printWriter.println(""); } - @Override public void writeTrackPoint(TrackPoint trackPoint) { if (printWriter != null) { - String coordinates = trackPoint.hasLocation() ? " " + formatLocation(trackPoint.getLatitude(), trackPoint.getLongitude()) : ""; - printWriter.println(""); + + printWriter.println(""); if (trackPoint.hasAltitude()) { printWriter.println("" + ELEVATION_FORMAT.format(trackPoint.getAltitude()) + ""); @@ -208,7 +294,7 @@ public class GpxTrackWriter implements TrackWriter { } if (trackPoint.hasCyclingCadence()) { - printWriter.println("" + HEARTRATE_FORMAT.format(trackPoint.getCyclingCadence_rpm()) + ""); + printWriter.println("" + CADENCE_FORMAT.format(trackPoint.getCyclingCadence_rpm()) + ""); } if (trackPoint.hasElevationGain()) { diff --git a/src/main/java/de/dennisguse/opentracks/io/file/exporter/KmlTrackWriter.java b/src/main/java/de/dennisguse/opentracks/io/file/exporter/KMLTrackExporter.java similarity index 74% rename from src/main/java/de/dennisguse/opentracks/io/file/exporter/KmlTrackWriter.java rename to src/main/java/de/dennisguse/opentracks/io/file/exporter/KMLTrackExporter.java index d564bf8ac..b502aad04 100644 --- a/src/main/java/de/dennisguse/opentracks/io/file/exporter/KmlTrackWriter.java +++ b/src/main/java/de/dennisguse/opentracks/io/file/exporter/KMLTrackExporter.java @@ -1,5 +1,5 @@ /* - * Copyright 2008 Google Inc. + * Copyright 2011 Google Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not * use this file except in compliance with the License. You may obtain a copy of @@ -13,13 +13,20 @@ * License for the specific language governing permissions and limitations under * the License. */ + package de.dennisguse.opentracks.io.file.exporter; import android.content.Context; +import android.database.Cursor; import android.location.Location; +import android.util.Log; + +import androidx.annotation.NonNull; +import androidx.annotation.VisibleForTesting; import java.io.OutputStream; import java.io.PrintWriter; +import java.time.Duration; import java.time.Instant; import java.util.ArrayList; import java.util.List; @@ -31,15 +38,20 @@ import de.dennisguse.opentracks.content.data.Marker; import de.dennisguse.opentracks.content.data.Track; import de.dennisguse.opentracks.content.data.TrackPoint; import de.dennisguse.opentracks.content.provider.ContentProviderUtils; +import de.dennisguse.opentracks.content.provider.TrackPointIterator; import de.dennisguse.opentracks.util.FileUtils; import de.dennisguse.opentracks.util.StringUtils; /** - * Write track as KML to a file. + * Convert {@link Track} incl. {@link Marker} and {@link TrackPoint} to KML. * + * @author Sandor Dornbush + * @author Rodrigo Damazio * @author Leif Hendrik Wilden */ -public class KmlTrackWriter implements TrackWriter { +public class KMLTrackExporter implements TrackExporter { + + private static final String TAG = KMLTrackExporter.class.getSimpleName(); public static final String MARKER_STYLE = "waypoint"; private static final String START_STYLE = "start"; @@ -76,13 +88,7 @@ public class KmlTrackWriter implements TrackWriter { private TrackPoint startTrackPoint; - /** - * @param context the context - * @param exportTrackDetail should detailed information about the track be exported (e.g., title, description, markers, timing)? - * @param exportSensorData should {@link TrackPoint}'s sensor data be exported? - * @param exportPhotos should pictures be exported (if true: exports to KMZ)? - */ - public KmlTrackWriter(Context context, boolean exportTrackDetail, boolean exportSensorData, boolean exportPhotos) { + public KMLTrackExporter(Context context, boolean exportTrackDetail, boolean exportSensorData, boolean exportPhotos) { this.context = context; this.exportTrackDetail = exportTrackDetail; this.exportSensorData = exportSensorData; @@ -91,21 +97,137 @@ public class KmlTrackWriter implements TrackWriter { this.contentProviderUtils = new ContentProviderUtils(context); } - @Override - public void prepare(OutputStream outputStream) { + public boolean writeTrack(Track track, @NonNull OutputStream outputStream) { + return writeTrack(new Track[]{track}, outputStream); + } + + public boolean writeTrack(Track[] tracks, @NonNull OutputStream outputStream) { + try { + prepare(outputStream); + writeHeader(tracks); + for (Track track : tracks) { + writeMarkers(track); + } + boolean hasMultipleTracks = tracks.length > 1; + if (hasMultipleTracks) { + writeMultiTrackBegin(); + } + //TODO Why use startTime of first track for the others? + Instant startTime = tracks[0].getTrackStatistics().getStartTime(); + for (Track track : tracks) { + Duration offset = Duration.between(track.getTrackStatistics().getStartTime(), startTime); + writeLocations(track, offset); + } + if (hasMultipleTracks) { + writeMultiTrackEnd(); + } + writeFooter(); + close(); + + return true; + } catch (InterruptedException e) { + Log.e(TAG, "Thread interrupted", e); + return false; + } + } + + private void writeMarkers(Track track) throws InterruptedException { + boolean hasMarkers = false; + try (Cursor cursor = contentProviderUtils.getMarkerCursor(track.getId(), null, -1)) { + if (cursor != null && cursor.moveToFirst()) { + for (int i = 0; i < cursor.getCount(); i++) { + if (Thread.interrupted()) { + throw new InterruptedException(); + } + if (!hasMarkers) { + writeBeginMarkers(track); + hasMarkers = true; + } + Marker marker = contentProviderUtils.createMarker(cursor); + writeMarker(marker); + + cursor.moveToNext(); + } + } + } + if (hasMarkers) { + writeEndMarkers(); + } + } + + private void writeLocations(Track track, Duration offset) throws InterruptedException { + boolean wroteTrack = false; + boolean wroteSegment = false; + + try (TrackPointIterator trackPointIterator = contentProviderUtils.getTrackPointLocationIterator(track.getId(), null)) { + while (trackPointIterator.hasNext()) { + if (Thread.interrupted()) throw new InterruptedException(); + + TrackPoint trackPoint = trackPointIterator.next(); + setLocationTime(trackPoint, offset); + + if (!wroteTrack) { + writeBeginTrack(track, trackPoint); + wroteTrack = true; + } + + switch (trackPoint.getType()) { + case SEGMENT_START_MANUAL: + case SEGMENT_START_AUTOMATIC: + if (wroteSegment) writeCloseSegment(); + writeOpenSegment(); + writeTrackPoint(trackPoint); + wroteSegment = true; + break; + case SEGMENT_END_MANUAL: + if (!wroteSegment) writeOpenSegment(); + writeTrackPoint(trackPoint); + writeCloseSegment(); + wroteSegment = false; + break; + case TRACKPOINT: + if (!wroteSegment) { + // Might happen for older data (pre v3.15.0) + writeOpenSegment(); + wroteSegment = true; + } + writeTrackPoint(trackPoint); + break; + } + } + + if (wroteSegment) { + // Should not be necessary as tracks should end with SEGMENT_END_MANUAL. + // Anyhow, make sure that the last segment is closed. + writeCloseSegment(); + } + + if (wroteTrack) { + TrackPoint lastValidTrackPoint = contentProviderUtils.getLastValidTrackPoint(track.getId()); + setLocationTime(lastValidTrackPoint, offset); + writeEndTrack(track, lastValidTrackPoint); + } else { + // Write an empty track + writeBeginTrack(track, null); + writeEndTrack(track, null); + } + } + } + + @VisibleForTesting + void prepare(OutputStream outputStream) { this.printWriter = new PrintWriter(outputStream); } - @Override - public void close() { + @VisibleForTesting + void close() { if (printWriter != null) { printWriter.flush(); printWriter = null; } } - @Override - public void writeHeader(Track[] tracks) { + private void writeHeader(Track[] tracks) { if (printWriter != null) { printWriter.println(""); printWriter.println(""); printWriter.println(""); } } - @Override - public void writeBeginMarkers(Track track) { + private void writeBeginMarkers(Track track) { if (printWriter != null) { printWriter.println(""); if (exportTrackDetail) { @@ -160,15 +280,7 @@ public class KmlTrackWriter implements TrackWriter { } } - @Override - public void writeEndMarkers() { - if (printWriter != null) { - printWriter.println(""); - } - } - - @Override - public void writeMarker(Marker marker) { + private void writeMarker(Marker marker) { if (printWriter != null && exportTrackDetail) { boolean existsPhoto = FileUtils.buildInternalPhotoFile(context, marker.getTrackId(), marker.getPhotoURI()) != null; if (marker.hasPhoto() && exportPhotos && existsPhoto) { @@ -180,7 +292,13 @@ public class KmlTrackWriter implements TrackWriter { } } - public void writeMultiTrackBegin() { + private void writeEndMarkers() { + if (printWriter != null) { + printWriter.println(""); + } + } + + private void writeMultiTrackBegin() { if (printWriter != null) { printWriter.println(""); printWriter.println("" + context.getString(R.string.generic_tracks) + ""); @@ -188,14 +306,13 @@ public class KmlTrackWriter implements TrackWriter { } } - public void writeMultiTrackEnd() { + private void writeMultiTrackEnd() { if (printWriter != null) { printWriter.println(""); } } - @Override - public void writeBeginTrack(Track track, TrackPoint startTrackPoint) { + private void writeBeginTrack(Track track, TrackPoint startTrackPoint) { this.startTrackPoint = startTrackPoint; if (printWriter != null) { String name = context.getString(R.string.marker_label_start, track.getName()); @@ -218,8 +335,8 @@ public class KmlTrackWriter implements TrackWriter { } } - @Override - public void writeEndTrack(Track track, TrackPoint endTrackPoint) { + + private void writeEndTrack(Track track, TrackPoint endTrackPoint) { if (printWriter != null) { printWriter.println(""); printWriter.println(""); @@ -233,8 +350,8 @@ public class KmlTrackWriter implements TrackWriter { } } - @Override - public void writeOpenSegment() { + @VisibleForTesting + void writeOpenSegment() { if (printWriter != null) { printWriter.println(""); speedList.clear(); @@ -246,8 +363,8 @@ public class KmlTrackWriter implements TrackWriter { } } - @Override - public void writeCloseSegment() { + @VisibleForTesting + void writeCloseSegment() { if (printWriter != null) { printWriter.println(""); printWriter.println(""); @@ -277,8 +394,8 @@ public class KmlTrackWriter implements TrackWriter { } } - @Override - public void writeTrackPoint(TrackPoint trackPoint) { + @VisibleForTesting + void writeTrackPoint(TrackPoint trackPoint) { if (printWriter != null) { if (exportTrackDetail) { printWriter.println("" + getTime(trackPoint.getLocation()) + ""); @@ -351,14 +468,14 @@ public class KmlTrackWriter implements TrackWriter { printWriter.println("" + StringUtils.formatCData(marker.getName()) + ""); printWriter.println("" + StringUtils.formatCData(marker.getDescription()) + ""); printWriter.print(""); - printWriter.print("" + marker.getLocation().getLongitude() + ""); - printWriter.print("" + marker.getLocation().getLatitude() + ""); + printWriter.print("" + marker.getLongitude() + ""); + printWriter.print("" + marker.getLatitude() + ""); printWriter.print("20"); printWriter.print("" + heading + ""); printWriter.print("90"); printWriter.println(""); printWriter.println("" + getTime(marker.getLocation()) + ""); - printWriter.println("#" + KmlTrackWriter.MARKER_STYLE + ""); + printWriter.println("#" + MARKER_STYLE + ""); writeCategory(marker.getCategory()); if (exportPhotos) { @@ -474,4 +591,17 @@ public class KmlTrackWriter implements TrackWriter { printWriter.println("" + StringUtils.formatCData(extendedDataType) + ""); printWriter.println(""); } + + /** + * Sets a trackPoint time. + * + * @param trackPoint the trackPoint + * @param offset the time offset + */ + //TODO Why? + private void setLocationTime(TrackPoint trackPoint, Duration offset) { + if (trackPoint != null) { + trackPoint.setTime(trackPoint.getTime().minus(offset)); + } + } } diff --git a/src/main/java/de/dennisguse/opentracks/io/file/exporter/KmzTrackExporter.java b/src/main/java/de/dennisguse/opentracks/io/file/exporter/KmzTrackExporter.java index 4c2489c3f..4d707fa18 100644 --- a/src/main/java/de/dennisguse/opentracks/io/file/exporter/KmzTrackExporter.java +++ b/src/main/java/de/dennisguse/opentracks/io/file/exporter/KmzTrackExporter.java @@ -50,19 +50,12 @@ public class KmzTrackExporter implements TrackExporter { private static final int BUFFER_SIZE = 4096; private final ContentProviderUtils contentProviderUtils; - private final FileTrackExporter fileTrackExporter; + private final KMLTrackExporter fileTrackExporter; private final boolean exportPhotos; private final Context context; - /** - * Constructor. - * - * @param context the context - * @param contentProviderUtils the content provider utils - * @param trackExporter the file track exporter - */ - public KmzTrackExporter(Context context, ContentProviderUtils contentProviderUtils, FileTrackExporter trackExporter, boolean exportPhotos) { + public KmzTrackExporter(Context context, ContentProviderUtils contentProviderUtils, KMLTrackExporter trackExporter, boolean exportPhotos) { this.context = context; this.contentProviderUtils = contentProviderUtils; this.fileTrackExporter = trackExporter; diff --git a/src/main/java/de/dennisguse/opentracks/io/file/exporter/TrackWriter.java b/src/main/java/de/dennisguse/opentracks/io/file/exporter/TrackWriter.java deleted file mode 100644 index 6fca72845..000000000 --- a/src/main/java/de/dennisguse/opentracks/io/file/exporter/TrackWriter.java +++ /dev/null @@ -1,83 +0,0 @@ -/* - * Copyright 2010 Google Inc. - * - * Licensed under the Apache License, Version 2.0 (the "License"); you may not - * use this file except in compliance with the License. You may obtain a copy of - * the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations under - * the License. - */ -package de.dennisguse.opentracks.io.file.exporter; - -import java.io.OutputStream; - -import de.dennisguse.opentracks.content.data.Marker; -import de.dennisguse.opentracks.content.data.Track; -import de.dennisguse.opentracks.content.data.TrackPoint; - -/** - * Interface for writing tracks to a file. The expected sequence of calls is: - * - *
- * {@link #prepare(OutputStream)}
- * {@link #writeHeader(Track[])}
- * For each track:
- *     {@link #writeBeginMarkers(Track)}
- *     For each marker:
- *         {@link #writeMarker(Marker)}
- *     {@link #writeEndMarkers()}
- * {@link #writeMultiTrackBegin()}
- * For each track:
- *     {@link #writeBeginTrack(Track, TrackPoint)}
- *     For each segment:
- *         {@link #writeOpenSegment()}
- *         For each trackPoint in the segment:
- *             {@link #writeTrackPoint(TrackPoint)}
- *         {@link #writeCloseSegment()}
- *     {@link #writeEndTrack(Track, TrackPoint)}
- * {@link #writeMultiTrackEnd()}
- * {@link #writeFooter()}
- * {@link #close()}
- * 
- * - * @author Rodrigo Damazio - */ -public interface TrackWriter { - - void prepare(OutputStream outputStream); - - /** - * Closes the output stream. - */ - void close(); - - void writeHeader(Track[] tracks); - - void writeFooter(); - - void writeBeginMarkers(Track track); - - void writeEndMarkers(); - - void writeMarker(Marker marker); - - void writeMultiTrackBegin(); - - void writeMultiTrackEnd(); - - void writeBeginTrack(Track track, TrackPoint startTrackPoint); - - void writeEndTrack(Track track, TrackPoint endTrackPoint); - - void writeOpenSegment(); - - void writeCloseSegment(); - - void writeTrackPoint(TrackPoint trackPoint); -} \ No newline at end of file diff --git a/src/main/java/de/dennisguse/opentracks/io/file/importer/AbstractFileTrackImporter.java b/src/main/java/de/dennisguse/opentracks/io/file/importer/AbstractFileTrackImporter.java index 5288d4322..b9f0b2897 100644 --- a/src/main/java/de/dennisguse/opentracks/io/file/importer/AbstractFileTrackImporter.java +++ b/src/main/java/de/dennisguse/opentracks/io/file/importer/AbstractFileTrackImporter.java @@ -36,7 +36,6 @@ import java.util.Locale; import java.util.UUID; import javax.xml.parsers.ParserConfigurationException; -import javax.xml.parsers.SAXParser; import javax.xml.parsers.SAXParserFactory; import de.dennisguse.opentracks.R; @@ -121,11 +120,7 @@ abstract class AbstractFileTrackImporter extends DefaultHandler implements Track @Override public Track.Id importFile(InputStream inputStream) { try { - SAXParser saxParser = SAXParserFactory.newInstance().newSAXParser(); - long start = System.currentTimeMillis(); - - saxParser.parse(inputStream, this); - Log.d(TAG, "Total import time: " + (System.currentTimeMillis() - start) + "ms"); + SAXParserFactory.newInstance().newSAXParser().parse(inputStream, this); return trackIds.get(0); } catch (IOException | SAXException | ParserConfigurationException e) { Log.e(TAG, "Unable to import file", e); @@ -375,7 +370,7 @@ abstract class AbstractFileTrackImporter extends DefaultHandler implements Track */ if (trackPoint.hasLocation() && trackData.lastLocationInCurrentSegment.hasLocation()) { float speed = trackData.lastLocationInCurrentSegment.distanceTo(trackPoint) / timeDifference.toMillis(); - trackPoint.setSpeed((float) speed); + trackPoint.setSpeed(speed); } } } diff --git a/src/main/java/de/dennisguse/opentracks/io/file/importer/GpxFileTrackImporter.java b/src/main/java/de/dennisguse/opentracks/io/file/importer/GpxFileTrackImporter.java index 65937b02a..e82dae619 100644 --- a/src/main/java/de/dennisguse/opentracks/io/file/importer/GpxFileTrackImporter.java +++ b/src/main/java/de/dennisguse/opentracks/io/file/importer/GpxFileTrackImporter.java @@ -29,6 +29,10 @@ import de.dennisguse.opentracks.content.provider.ContentProviderUtils; /** * Imports a GPX file. * Uses https://www8.garmin.com/xmlschemas/TrackPointExtensionv2.xsd + *

+ * {@link de.dennisguse.opentracks.io.file.exporter.GPXTrackExporter} does not export information if a segment was started automatic or manually. + * Therefore, all segments starts are marked as SEGMENT_START_AUTOMATIC. + * Thus, the {@link de.dennisguse.opentracks.stats.TrackStatistics} cannot be restored correctly. * * @author Jimmy Shih */ @@ -200,9 +204,7 @@ public class GpxFileTrackImporter extends AbstractFileTrackImporter { boolean isFirstTrackPointInSegment = isFirstTrackPointInSegment(); TrackPoint trackPoint = getTrackPoint(); if (isFirstTrackPointInSegment) { - TrackPoint.Type type = !trackPoint.hasLocation() ? TrackPoint.Type.SEGMENT_START_MANUAL : TrackPoint.Type.SEGMENT_START_AUTOMATIC; - - trackPoint.setType(type); + trackPoint.setType(TrackPoint.Type.SEGMENT_START_AUTOMATIC); } insertTrackPoint(trackPoint); } diff --git a/src/main/java/de/dennisguse/opentracks/io/file/importer/KmlFileTrackImporter.java b/src/main/java/de/dennisguse/opentracks/io/file/importer/KmlFileTrackImporter.java index f5c067d13..7fb010db8 100644 --- a/src/main/java/de/dennisguse/opentracks/io/file/importer/KmlFileTrackImporter.java +++ b/src/main/java/de/dennisguse/opentracks/io/file/importer/KmlFileTrackImporter.java @@ -28,7 +28,7 @@ import java.util.ArrayList; import de.dennisguse.opentracks.content.data.TrackPoint; import de.dennisguse.opentracks.content.provider.ContentProviderUtils; -import de.dennisguse.opentracks.io.file.exporter.KmlTrackWriter; +import de.dennisguse.opentracks.io.file.exporter.KMLTrackExporter; /** * Imports a KML file. @@ -39,7 +39,7 @@ public class KmlFileTrackImporter extends AbstractFileTrackImporter { private static final String TAG = KmlFileTrackImporter.class.getSimpleName(); - private static final String MARKER_STYLE = "#" + KmlTrackWriter.MARKER_STYLE; + private static final String MARKER_STYLE = "#" + KMLTrackExporter.MARKER_STYLE; private static final String TAG_COORDINATES = "coordinates"; private static final String TAG_DESCRIPTION = "description"; @@ -306,22 +306,22 @@ public class KmlFileTrackImporter extends AbstractFileTrackImporter { } } switch (extendedDataType) { - case KmlTrackWriter.EXTENDED_DATA_TYPE_SPEED: + case KMLTrackExporter.EXTENDED_DATA_TYPE_SPEED: speedList.add(value); break; - case KmlTrackWriter.EXTENDED_DATA_TYPE_POWER: + case KMLTrackExporter.EXTENDED_DATA_TYPE_POWER: powerList.add(value); break; - case KmlTrackWriter.EXTENDED_DATA_TYPE_HEART_RATE: + case KMLTrackExporter.EXTENDED_DATA_TYPE_HEART_RATE: heartRateList.add(value); break; - case KmlTrackWriter.EXTENDED_DATA_TYPE_CADENCE: + case KMLTrackExporter.EXTENDED_DATA_TYPE_CADENCE: cadenceList.add(value); break; - case KmlTrackWriter.EXTENDED_DATA_TYPE_ELEVATION_GAIN: + case KMLTrackExporter.EXTENDED_DATA_TYPE_ELEVATION_GAIN: elevationGainList.add(value); break; - case KmlTrackWriter.EXTENDED_DATA_TYPE_ELEVATION_LOSS: + case KMLTrackExporter.EXTENDED_DATA_TYPE_ELEVATION_LOSS: elevationLossList.add(value); break; default: diff --git a/src/main/java/de/dennisguse/opentracks/util/ExportUtils.java b/src/main/java/de/dennisguse/opentracks/util/ExportUtils.java index 74bf3a672..0d9a76576 100644 --- a/src/main/java/de/dennisguse/opentracks/util/ExportUtils.java +++ b/src/main/java/de/dennisguse/opentracks/util/ExportUtils.java @@ -35,7 +35,7 @@ public class ExportUtils { } public static boolean exportTrack(Context context, TrackFileFormat trackFileFormat, DocumentFile directory, Track track) { - TrackExporter trackExporter = trackFileFormat.newTrackExporter(context); + TrackExporter trackExporter = trackFileFormat.createTrackExporter(context); Uri exportDocumentFileUri = getExportDocumentFileUri(context, track.getId(), trackFileFormat, directory); if (exportDocumentFileUri == null) {