From f8afa9dbadf2c14b90d342ddf01610ac9fbffbd3 Mon Sep 17 00:00:00 2001 From: Dennis Guse Date: Mon, 6 Jul 2020 20:20:46 +0200 Subject: [PATCH] Import/export track UUID (necessary for synchronization). If a track with a UUID is presented in OpenTracks, it cannot be imported again (enforced by database constraint). Fixes #307. --- doc/opentracks-schema-1.0.xsd | 8 + .../provider/CustomContentProviderTest.java | 18 +- .../provider/CustomSQLiteOpenHelperTest.java | 57 +++-- .../AbstractTestFileTrackImporter.java | 12 +- .../io/file/importer/ExportImportTest.java | 56 ++++- .../importer/GpxFileTrackImporterTest.java | 217 ------------------ .../importer/KmlFileTrackImporterTest.java | 133 ----------- .../ImportProgressDialogFragment.java | 4 +- .../io/file/exporter/GpxTrackWriter.java | 34 +-- .../io/file/exporter/KmlTrackWriter.java | 8 +- .../io/file/exporter/KmzTrackExporter.java | 7 +- .../importer/AbstractFileTrackImporter.java | 29 +-- .../file/importer/GpxFileTrackImporter.java | 7 +- .../file/importer/KmlFileTrackImporter.java | 125 +++++----- .../io/file/importer/KmzTrackImporter.java | 13 +- 15 files changed, 246 insertions(+), 482 deletions(-) create mode 100644 doc/opentracks-schema-1.0.xsd delete mode 100644 src/androidTest/java/de/dennisguse/opentracks/io/file/importer/GpxFileTrackImporterTest.java delete mode 100644 src/androidTest/java/de/dennisguse/opentracks/io/file/importer/KmlFileTrackImporterTest.java diff --git a/doc/opentracks-schema-1.0.xsd b/doc/opentracks-schema-1.0.xsd new file mode 100644 index 000000000..55c2b3d4b --- /dev/null +++ b/doc/opentracks-schema-1.0.xsd @@ -0,0 +1,8 @@ + + + + + + \ No newline at end of file diff --git a/src/androidTest/java/de/dennisguse/opentracks/content/provider/CustomContentProviderTest.java b/src/androidTest/java/de/dennisguse/opentracks/content/provider/CustomContentProviderTest.java index 2e1484073..1bace4ece 100644 --- a/src/androidTest/java/de/dennisguse/opentracks/content/provider/CustomContentProviderTest.java +++ b/src/androidTest/java/de/dennisguse/opentracks/content/provider/CustomContentProviderTest.java @@ -22,7 +22,6 @@ import android.net.Uri; import androidx.test.core.app.ApplicationProvider; -import org.junit.Assert; import org.junit.Before; import org.junit.Test; @@ -30,6 +29,9 @@ import de.dennisguse.opentracks.content.data.TrackPointsColumns; import de.dennisguse.opentracks.content.data.TracksColumns; import de.dennisguse.opentracks.content.data.WaypointsColumns; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertTrue; + /** * Tests {@link CustomContentProvider}. * @@ -51,7 +53,7 @@ public class CustomContentProviderTest { */ @Test public void testOnCreate() { - Assert.assertTrue(customContentProvider.onCreate(context)); + assertTrue(customContentProvider.onCreate(context)); } /** @@ -59,13 +61,13 @@ public class CustomContentProviderTest { */ @Test public void testGetType() { - Assert.assertEquals(TracksColumns.CONTENT_TYPE, customContentProvider.getType(TracksColumns.CONTENT_URI)); - Assert.assertEquals(TracksColumns.CONTENT_ITEMTYPE, customContentProvider.getType(ContentUris.appendId(TracksColumns.CONTENT_URI.buildUpon(), 1).build())); + assertEquals(TracksColumns.CONTENT_TYPE, customContentProvider.getType(TracksColumns.CONTENT_URI)); + assertEquals(TracksColumns.CONTENT_ITEMTYPE, customContentProvider.getType(ContentUris.appendId(TracksColumns.CONTENT_URI.buildUpon(), 1).build())); - Assert.assertEquals(TrackPointsColumns.CONTENT_TYPE, customContentProvider.getType(TrackPointsColumns.CONTENT_URI_BY_ID)); - Assert.assertEquals(TrackPointsColumns.CONTENT_ITEMTYPE, customContentProvider.getType(ContentUris.appendId(TrackPointsColumns.CONTENT_URI_BY_TRACKID.buildUpon(), 1).build())); + assertEquals(TrackPointsColumns.CONTENT_TYPE, customContentProvider.getType(TrackPointsColumns.CONTENT_URI_BY_ID)); + assertEquals(TrackPointsColumns.CONTENT_ITEMTYPE, customContentProvider.getType(ContentUris.appendId(TrackPointsColumns.CONTENT_URI_BY_TRACKID.buildUpon(), 1).build())); - Assert.assertEquals(WaypointsColumns.CONTENT_TYPE, customContentProvider.getType(WaypointsColumns.CONTENT_URI)); - Assert.assertEquals(WaypointsColumns.CONTENT_ITEMTYPE, customContentProvider.getType(ContentUris.appendId(WaypointsColumns.CONTENT_URI.buildUpon(), 1).build())); + assertEquals(WaypointsColumns.CONTENT_TYPE, customContentProvider.getType(WaypointsColumns.CONTENT_URI)); + assertEquals(WaypointsColumns.CONTENT_ITEMTYPE, customContentProvider.getType(ContentUris.appendId(WaypointsColumns.CONTENT_URI.buildUpon(), 1).build())); } } \ No newline at end of file diff --git a/src/androidTest/java/de/dennisguse/opentracks/content/provider/CustomSQLiteOpenHelperTest.java b/src/androidTest/java/de/dennisguse/opentracks/content/provider/CustomSQLiteOpenHelperTest.java index 6bab2c3b1..0dd5abe7a 100644 --- a/src/androidTest/java/de/dennisguse/opentracks/content/provider/CustomSQLiteOpenHelperTest.java +++ b/src/androidTest/java/de/dennisguse/opentracks/content/provider/CustomSQLiteOpenHelperTest.java @@ -2,6 +2,7 @@ package de.dennisguse.opentracks.content.provider; import android.content.Context; import android.database.Cursor; +import android.database.sqlite.SQLiteConstraintException; import android.database.sqlite.SQLiteDatabase; import android.database.sqlite.SQLiteOpenHelper; @@ -9,7 +10,7 @@ import androidx.annotation.VisibleForTesting; import androidx.test.core.app.ApplicationProvider; import androidx.test.ext.junit.runners.AndroidJUnit4; -import org.junit.Assert; +import org.junit.After; import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; @@ -21,6 +22,10 @@ import de.dennisguse.opentracks.content.data.TrackPointsColumns; import de.dennisguse.opentracks.content.data.TracksColumns; import de.dennisguse.opentracks.content.data.WaypointsColumns; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertTrue; +import static org.junit.Assert.fail; + @RunWith(AndroidJUnit4.class) public class CustomSQLiteOpenHelperTest { @@ -88,6 +93,7 @@ public class CustomSQLiteOpenHelperTest { } @Before + @After public void setUp() { context.deleteDatabase(DATABASE_NAME); } @@ -95,13 +101,15 @@ public class CustomSQLiteOpenHelperTest { @Test public void onCreate() { try (SQLiteDatabase db = new CustomSQLiteOpenHelper(context, DATABASE_NAME).getWritableDatabase()) { - Assert.assertTrue(hasSqlCreate(db, TracksColumns.CREATE_TABLE)); + assertTrue(hasSqlCreate(db, TracksColumns.CREATE_TABLE)); - Assert.assertTrue(hasSqlCreate(db, TrackPointsColumns.CREATE_TABLE)); - Assert.assertTrue(hasSqlCreate(db, TrackPointsColumns.CREATE_TABLE_INDEX)); + assertTrue(hasSqlCreate(db, TrackPointsColumns.CREATE_TABLE)); + assertTrue(hasSqlCreate(db, TrackPointsColumns.CREATE_TABLE_INDEX)); - Assert.assertTrue(hasSqlCreate(db, WaypointsColumns.CREATE_TABLE)); - Assert.assertTrue(hasSqlCreate(db, WaypointsColumns.CREATE_TABLE_INDEX)); + assertTrue(hasSqlCreate(db, WaypointsColumns.CREATE_TABLE)); + assertTrue(hasSqlCreate(db, WaypointsColumns.CREATE_TABLE_INDEX)); + } catch (Exception e) { + fail(); } } @@ -127,18 +135,18 @@ public class CustomSQLiteOpenHelperTest { } // then - verify table structure - Assert.assertEquals(3, tableByUpgrade.size()); - Assert.assertEquals(tableByUpgrade.size(), tablesByCreate.size()); + assertEquals(3, tableByUpgrade.size()); + assertEquals(tableByUpgrade.size(), tablesByCreate.size()); - Assert.assertEquals(tablesByCreate.get(TracksColumns.TABLE_NAME), tableByUpgrade.get(TracksColumns.TABLE_NAME)); - Assert.assertEquals(tablesByCreate.get(TrackPointsColumns.TABLE_NAME), tableByUpgrade.get(TrackPointsColumns.TABLE_NAME)); - Assert.assertEquals(tablesByCreate.get(WaypointsColumns.TABLE_NAME), tableByUpgrade.get(WaypointsColumns.TABLE_NAME)); + assertEquals(tablesByCreate.get(TracksColumns.TABLE_NAME), tableByUpgrade.get(TracksColumns.TABLE_NAME)); + assertEquals(tablesByCreate.get(TrackPointsColumns.TABLE_NAME), tableByUpgrade.get(TrackPointsColumns.TABLE_NAME)); + assertEquals(tablesByCreate.get(WaypointsColumns.TABLE_NAME), tableByUpgrade.get(WaypointsColumns.TABLE_NAME)); // then - verify custom indices - Assert.assertEquals(3, indicesByCreate.size()); - Assert.assertEquals(indicesByUpgrade.get(TracksColumns.TABLE_NAME), indicesByCreate.get(TracksColumns.TABLE_NAME)); - Assert.assertEquals(indicesByUpgrade.get(TrackPointsColumns.TABLE_NAME), indicesByCreate.get(TrackPointsColumns.TABLE_NAME)); - Assert.assertEquals(indicesByUpgrade.get(WaypointsColumns.TABLE_NAME), indicesByCreate.get(WaypointsColumns.TABLE_NAME)); + assertEquals(3, indicesByCreate.size()); + assertEquals(indicesByUpgrade.get(TracksColumns.TABLE_NAME), indicesByCreate.get(TracksColumns.TABLE_NAME)); + assertEquals(indicesByUpgrade.get(TrackPointsColumns.TABLE_NAME), indicesByCreate.get(TrackPointsColumns.TABLE_NAME)); + assertEquals(indicesByUpgrade.get(WaypointsColumns.TABLE_NAME), indicesByCreate.get(WaypointsColumns.TABLE_NAME)); } @Test @@ -155,12 +163,23 @@ public class CustomSQLiteOpenHelperTest { } // then - verify table structure - Assert.assertEquals(TRACKS_CREATE_TABLE_V23, tablesByDowngrade.get("tracks")); - Assert.assertEquals(TRACKPOINTS_CREATE_TABLE_V23, tablesByDowngrade.get("trackpoints")); - Assert.assertEquals(WAYPOINTS_CREATE_TABLE_V23, tablesByDowngrade.get("waypoints")); + assertEquals(TRACKS_CREATE_TABLE_V23, tablesByDowngrade.get("tracks")); + assertEquals(TRACKPOINTS_CREATE_TABLE_V23, tablesByDowngrade.get("trackpoints")); + assertEquals(WAYPOINTS_CREATE_TABLE_V23, tablesByDowngrade.get("waypoints")); // then - verify custom indices - Assert.assertEquals(0, indicesByDowngrade.size()); + assertEquals(0, indicesByDowngrade.size()); + } + + @Test + public void track_uuid_unique() { + try (SQLiteDatabase db = new CustomSQLiteOpenHelper(context, DATABASE_NAME).getWritableDatabase()) { + db.execSQL("INSERT INTO tracks (uuid) VALUES (0x00)"); + db.execSQL("INSERT INTO tracks (uuid) VALUES (0x00)"); + fail("unique constraint not enforced"); + } catch (SQLiteConstraintException e) { + assertTrue(e.getMessage().contains("UNIQUE constraint failed: tracks.uuid")); + } } private void createVersion23() { diff --git a/src/androidTest/java/de/dennisguse/opentracks/io/file/importer/AbstractTestFileTrackImporter.java b/src/androidTest/java/de/dennisguse/opentracks/io/file/importer/AbstractTestFileTrackImporter.java index 4b5910b34..9b441dd47 100644 --- a/src/androidTest/java/de/dennisguse/opentracks/io/file/importer/AbstractTestFileTrackImporter.java +++ b/src/androidTest/java/de/dennisguse/opentracks/io/file/importer/AbstractTestFileTrackImporter.java @@ -29,13 +29,13 @@ import org.mockito.ArgumentCaptor; import org.mockito.Mock; import java.text.SimpleDateFormat; +import java.time.ZoneOffset; import java.util.Locale; import java.util.SimpleTimeZone; +import java.util.TimeZone; import de.dennisguse.opentracks.content.data.Track; import de.dennisguse.opentracks.content.data.TrackPoint; -import de.dennisguse.opentracks.content.data.TracksColumns; -import de.dennisguse.opentracks.content.data.Waypoint; import de.dennisguse.opentracks.content.data.WaypointsColumns; import de.dennisguse.opentracks.content.provider.ContentProviderUtils; @@ -64,11 +64,11 @@ public abstract class AbstractTestFileTrackImporter { static final SimpleDateFormat DATE_FORMAT_0 = new SimpleDateFormat("yyyy-MM-dd'T'hh:mm:ss'Z'", Locale.US); static final SimpleDateFormat DATE_FORMAT_1 = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSS", Locale.US); - static final long TRACK_ID_0 = 1; + static final long TRACK_POINT_ID_0 = 1; static final long TRACK_POINT_ID_1 = 2; static final long TRACK_POINT_ID_3 = 4; - static final Uri TRACK_ID_0_URI = ContentUris.appendId(TracksColumns.CONTENT_URI.buildUpon(), TRACK_ID_0).build(); + private static final long WAYPOINT_ID_0 = 1; private static final Uri WAYPOINT_ID_O_URI = ContentUris.appendId(WaypointsColumns.CONTENT_URI.buildUpon(), WAYPOINT_ID_0).build(); @@ -76,7 +76,7 @@ public abstract class AbstractTestFileTrackImporter { static { // We can't omit the timezones in the test, otherwise it'll use the local timezone and fail depending on where the test runner is. - SimpleTimeZone utc = new SimpleTimeZone(0, "UTC"); + TimeZone utc = SimpleTimeZone.getTimeZone(ZoneOffset.UTC); DATE_FORMAT_0.setTimeZone(utc); DATE_FORMAT_1.setTimeZone(utc); } @@ -114,7 +114,7 @@ public abstract class AbstractTestFileTrackImporter { protected void expectTrackUpdate(ArgumentCaptor trackCaptor, boolean lastTrack, long trackId) { contentProviderUtils.updateTrack(trackCaptor.capture()); - when(contentProviderUtils.insertWaypoint((Waypoint) any())).thenReturn(WAYPOINT_ID_O_URI); + when(contentProviderUtils.insertWaypoint(any())).thenReturn(WAYPOINT_ID_O_URI); if (lastTrack) { // Return null to not add waypoints when(contentProviderUtils.getTrack(trackId)).thenReturn(null); 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 5217678ad..069573d51 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 @@ -31,6 +31,7 @@ import de.dennisguse.opentracks.stats.TrackStatistics; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertNull; /** * Export a track to {@link TrackFileFormat} and verify that the import is identical. @@ -109,10 +110,11 @@ public class ExportImportTest { // 1. export ByteArrayOutputStream outputStream = new ByteArrayOutputStream(); trackExporter.writeTrack(context, outputStream); + contentProviderUtils.deleteTrack(context, trackId); // 2. import InputStream inputStream = new ByteArrayInputStream(outputStream.toByteArray()); - AbstractFileTrackImporter trackImporter = new KmlFileTrackImporter(context, -1L); + AbstractFileTrackImporter trackImporter = new KmlFileTrackImporter(context); importTrackId = trackImporter.importFile(inputStream); // then @@ -123,6 +125,7 @@ public class ExportImportTest { assertEquals(track.getDescription(), importedTrack.getDescription()); assertEquals(track.getName(), importedTrack.getName()); assertEquals(track.getIcon(), importedTrack.getIcon()); + assertEquals(track.getUuid(), importedTrack.getUuid()); // 2. waypoints assertWaypoints(); @@ -143,10 +146,11 @@ public class ExportImportTest { // 1. export ByteArrayOutputStream outputStream = new ByteArrayOutputStream(); trackExporter.writeTrack(context, outputStream); + contentProviderUtils.deleteTrack(context, trackId); // 2. import InputStream inputStream = new ByteArrayInputStream(outputStream.toByteArray()); - AbstractFileTrackImporter trackImporter = new KmlFileTrackImporter(context, -1L); + AbstractFileTrackImporter trackImporter = new KmlFileTrackImporter(context); importTrackId = trackImporter.importFile(inputStream); // then @@ -165,6 +169,29 @@ public class ExportImportTest { assertTrackpoints(true, true, true); } + @LargeTest + @Test + public void kml_with_trackdetail_and_sensordata_duplicate_trackUUID() { + // given + Track track = contentProviderUtils.getTrack(trackId); + + TrackExporter trackExporter = TrackFileFormat.KML_WITH_TRACKDETAIL_AND_SENSORDATA.newTrackExporter(context, new Track[]{track}); + + // when + // 1. export + ByteArrayOutputStream outputStream = new ByteArrayOutputStream(); + trackExporter.writeTrack(context, outputStream); + + // 2. import + InputStream inputStream = new ByteArrayInputStream(outputStream.toByteArray()); + AbstractFileTrackImporter trackImporter = new KmlFileTrackImporter(context); + importTrackId = trackImporter.importFile(inputStream); + + // then + Track importedTrack = contentProviderUtils.getTrack(importTrackId); + assertNull(importedTrack); + } + @LargeTest @Test public void kmz_only_track() { @@ -205,6 +232,7 @@ public class ExportImportTest { // 1. export ByteArrayOutputStream outputStream = new ByteArrayOutputStream(); trackExporter.writeTrack(context, outputStream); + contentProviderUtils.deleteTrack(context, trackId); // 2. import InputStream inputStream = new ByteArrayInputStream(outputStream.toByteArray()); @@ -229,6 +257,30 @@ public class ExportImportTest { assertTrackpoints(false, true, true); } + @LargeTest + @Test + public void gpx_duplicate_trackUUID() { + // given + Track track = contentProviderUtils.getTrack(trackId); + + TrackExporter trackExporter = TrackFileFormat.GPX.newTrackExporter(context, new Track[]{track}); + + // when + // 1. export + ByteArrayOutputStream outputStream = new ByteArrayOutputStream(); + trackExporter.writeTrack(context, outputStream); + + // 2. import + InputStream inputStream = new ByteArrayInputStream(outputStream.toByteArray()); + AbstractFileTrackImporter trackImporter = new GpxFileTrackImporter(context, contentProviderUtils); + importTrackId = trackImporter.importFile(inputStream); + + // then + // 1. track + Track trackImported = contentProviderUtils.getTrack(importTrackId); + assertNull(trackImported); + } + private void assertWaypoints() { assertEquals(waypoints.size(), contentProviderUtils.getWaypointCount(importTrackId)); diff --git a/src/androidTest/java/de/dennisguse/opentracks/io/file/importer/GpxFileTrackImporterTest.java b/src/androidTest/java/de/dennisguse/opentracks/io/file/importer/GpxFileTrackImporterTest.java deleted file mode 100644 index a217da5ae..000000000 --- a/src/androidTest/java/de/dennisguse/opentracks/io/file/importer/GpxFileTrackImporterTest.java +++ /dev/null @@ -1,217 +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.importer; - -import org.junit.Assert; -import org.junit.Test; -import org.junit.runner.RunWith; -import org.mockito.ArgumentCaptor; -import org.mockito.junit.MockitoJUnitRunner; - -import java.io.ByteArrayInputStream; -import java.io.InputStream; - -import de.dennisguse.opentracks.content.data.Track; -import de.dennisguse.opentracks.content.data.TrackPoint; -import de.dennisguse.opentracks.util.PreferencesUtils; - -import static org.mockito.Mockito.any; -import static org.mockito.Mockito.anyLong; -import static org.mockito.Mockito.atLeastOnce; -import static org.mockito.Mockito.eq; -import static org.mockito.Mockito.verify; -import static org.mockito.Mockito.when; - -/** - * Tests for {@link GpxFileTrackImporter}. - * - * @author Steffen Horlacher - */ -@RunWith(MockitoJUnitRunner.class) -public class GpxFileTrackImporterTest extends AbstractTestFileTrackImporter { - - private static final String VALID_ONE_TRACK_ONE_SEGMENT_GPX = "" - + getNameAndDescription(TRACK_NAME_0, TRACK_DESCRIPTION_0) + "" - + getTrackPoint(0, TRACK_TIME_0) + getTrackPoint(1, TRACK_TIME_1) + ""; - private static final String VALID_ONE_TRACK_TWO_SEGMENTS_GPX = "" - + getNameAndDescription(TRACK_NAME_0, TRACK_DESCRIPTION_0) + "" - + getTrackPoint(0, TRACK_TIME_0) + getTrackPoint(1, TRACK_TIME_1) + "" - + getTrackPoint(2, TRACK_TIME_2) + getTrackPoint(3, TRACK_TIME_3) + ""; - private static final String VALID_ONE_TRACK_TWO_SEGMENTS_NO_TIME_GPX = "" - + getNameAndDescription(TRACK_NAME_0, TRACK_DESCRIPTION_0) + "" - + getTrackPoint(0, null) + getTrackPoint(1, null) + "" - + getTrackPoint(2, null) + getTrackPoint(3, null) + ""; - - private static final String INVALID_XML_GPX = VALID_ONE_TRACK_ONE_SEGMENT_GPX.substring(0, VALID_ONE_TRACK_ONE_SEGMENT_GPX.length() - 50); - private static final String INVALID_LOCATION_GPX = VALID_ONE_TRACK_ONE_SEGMENT_GPX.replaceAll(Double.toString(TRACK_LATITUDE), "1000.0"); - private static final String INVALID_TIME_GPX = VALID_ONE_TRACK_ONE_SEGMENT_GPX.replaceAll(TRACK_TIME_0, "invalid"); - private static final String INVALID_ALTITUDE_GPX = VALID_ONE_TRACK_ONE_SEGMENT_GPX.replaceAll(Double.toString(TRACK_ELEVATION), "invalid"); - private static final String INVALID_LATITUDE_GPX = VALID_ONE_TRACK_ONE_SEGMENT_GPX.replaceAll(Double.toString(TRACK_LATITUDE), "invalid"); - private static final String INVALID_LONGITUDE_GPX = VALID_ONE_TRACK_ONE_SEGMENT_GPX.replaceAll(Double.toString(TRACK_LONGITUDE), "invalid"); - - private static String getNameAndDescription(String name, String description) { - return "" + ""; - } - - private static String getTrackPoint(int index, String time) { - String latitude = Double.toString(TRACK_LATITUDE + index); - String longitude = Double.toString(TRACK_LONGITUDE + index); - String elevation = Double.toString(TRACK_ELEVATION + index); - StringBuilder buffer = new StringBuilder(); - buffer.append("").append(elevation).append(""); - if (time != null) { - buffer.append(""); - } - buffer.append(""); - return buffer.toString(); - } - - @Test - public void testOneTrackOneSegment() throws Exception { - // given - TrackPoint trackPoint0 = createTrackPoint(0, DATE_FORMAT_0.parse(TRACK_TIME_0).getTime()); - TrackPoint trackPoint1 = createTrackPoint(1, DATE_FORMAT_1.parse(TRACK_TIME_1).getTime()); - - when(contentProviderUtils.insertTrack((Track) any())).thenReturn(TRACK_ID_0_URI); - expectFirstTrackPoint(trackPoint0, TRACK_ID_0, TRACK_POINT_ID_0); - - // A flush happens at the end - when(contentProviderUtils.bulkInsertTrackPoint((TrackPoint[]) any(), eq(TRACK_ID_0))).thenReturn(1); - when(contentProviderUtils.getLastTrackPointId(TRACK_ID_0)).thenReturn(TRACK_POINT_ID_1); - when(contentProviderUtils.getTrack(PreferencesUtils.getRecordingTrackId(context))).thenReturn(null); - ArgumentCaptor trackCaptor = ArgumentCaptor.forClass(Track.class); - expectTrackUpdate(trackCaptor, true, TRACK_ID_0); - - // when - InputStream inputStream = new ByteArrayInputStream(VALID_ONE_TRACK_ONE_SEGMENT_GPX.getBytes()); - GpxFileTrackImporter gpxFileTrackImporter = new GpxFileTrackImporter(context, contentProviderUtils); - long trackId = gpxFileTrackImporter.importFile(inputStream); - - // then - Assert.assertEquals(TRACK_ID_0, trackId); - - verify(contentProviderUtils, atLeastOnce()).updateTrack(trackCaptor.capture()); - long time0 = DATE_FORMAT_0.parse(TRACK_TIME_0).getTime(); - long time1 = DATE_FORMAT_1.parse(TRACK_TIME_1).getTime(); - Assert.assertEquals(time1 - time0, trackCaptor.getValue().getTrackStatistics().getTotalTime()); - verifyTrack(trackCaptor.getValue(), TRACK_NAME_0, TRACK_DESCRIPTION_0, time0); - } - - @Test - public void testOneTrackTwoSegments() throws Exception { - // given - TrackPoint trackPoint0 = createTrackPoint(0, DATE_FORMAT_0.parse(TRACK_TIME_0).getTime()); - - when(contentProviderUtils.insertTrack((Track) any())).thenReturn(TRACK_ID_0_URI); - expectFirstTrackPoint(trackPoint0, TRACK_ID_0, TRACK_POINT_ID_0); - // A flush happens at the end - when(contentProviderUtils.bulkInsertTrackPoint((TrackPoint[]) any(), eq(TRACK_ID_0))).thenReturn(5); - when(contentProviderUtils.getLastTrackPointId(TRACK_ID_0)).thenReturn(TRACK_POINT_ID_3); - when(contentProviderUtils.getTrack(PreferencesUtils.getRecordingTrackId(context))).thenReturn(null); - - ArgumentCaptor trackCaptor = ArgumentCaptor.forClass(Track.class); - expectTrackUpdate(trackCaptor, true, TRACK_ID_0); - - // when - InputStream inputStream = new ByteArrayInputStream(VALID_ONE_TRACK_TWO_SEGMENTS_GPX.getBytes()); - GpxFileTrackImporter gpxFileTrackImporter = new GpxFileTrackImporter(context, contentProviderUtils); - long trackId = gpxFileTrackImporter.importFile(inputStream); - - // then - verify(contentProviderUtils, atLeastOnce()).updateTrack(trackCaptor.capture()); - Assert.assertEquals(TRACK_ID_0, trackId); - - long time0 = DATE_FORMAT_0.parse(TRACK_TIME_0).getTime(); - long time1 = DATE_FORMAT_1.parse(TRACK_TIME_1).getTime(); - long time2 = DATE_FORMAT_1.parse(TRACK_TIME_2).getTime(); - long time3 = DATE_FORMAT_1.parse(TRACK_TIME_3).getTime(); - Assert.assertEquals(time1 - time0 + time3 - time2, trackCaptor.getValue().getTrackStatistics().getTotalTime()); - - verifyTrack(trackCaptor.getValue(), TRACK_NAME_0, TRACK_DESCRIPTION_0, DATE_FORMAT_0.parse(TRACK_TIME_0).getTime()); - } - - @Test - public void testOneTrackTwoSegmentsNoTime() { - // given - when(contentProviderUtils.insertTrack((Track) any())).thenReturn(TRACK_ID_0_URI); - expectFirstTrackPoint(null, TRACK_ID_0, TRACK_POINT_ID_0); - - // A flush happens at the end - when(contentProviderUtils.bulkInsertTrackPoint((TrackPoint[]) any(), eq(TRACK_ID_0))).thenReturn(5); - when(contentProviderUtils.getLastTrackPointId(TRACK_ID_0)).thenReturn(TRACK_POINT_ID_3); - when(contentProviderUtils.getTrack(PreferencesUtils.getRecordingTrackId(context))).thenReturn(null); - - ArgumentCaptor trackCaptor = ArgumentCaptor.forClass(Track.class); - expectTrackUpdate(trackCaptor, true, TRACK_ID_0); - - // when - InputStream inputStream = new ByteArrayInputStream(VALID_ONE_TRACK_TWO_SEGMENTS_NO_TIME_GPX.getBytes()); - GpxFileTrackImporter gpxFileTrackImporter = new GpxFileTrackImporter(context, contentProviderUtils); - long trackId = gpxFileTrackImporter.importFile(inputStream); - - // then - verify(contentProviderUtils, atLeastOnce()).updateTrack(trackCaptor.capture()); - Assert.assertEquals(TRACK_ID_0, trackId); - - Assert.assertEquals(0, trackCaptor.getValue().getTrackStatistics().getTotalTime()); - verifyTrack(trackCaptor.getValue(), TRACK_NAME_0, TRACK_DESCRIPTION_0, -1L); - } - - @Test - public void testInvalidXml() { - testInvalidGpx(INVALID_XML_GPX); - } - - @Test - public void testInvalidLocation() { - testInvalidGpx(INVALID_LOCATION_GPX); - } - - @Test - public void testInvalidTime() { - testInvalidGpx(INVALID_TIME_GPX); - } - - @Test - public void testInvalidAltitude() { - testInvalidGpx(INVALID_ALTITUDE_GPX); - } - - @Test - public void testInvalidLatitude() { - testInvalidGpx(INVALID_LATITUDE_GPX); - } - - @Test - public void testInvalidLongitude() { - testInvalidGpx(INVALID_LONGITUDE_GPX); - } - - private void testInvalidGpx(String xml) { - when(contentProviderUtils.insertTrack((Track) any())).thenReturn(TRACK_ID_0_URI); - - // For the following, use StubReturn since we don't care whether they are invoked or not. - when(contentProviderUtils.bulkInsertTrackPoint((TrackPoint[]) any(), anyLong())).thenReturn(1); - when(contentProviderUtils.getTrack(PreferencesUtils.getRecordingTrackId(context))).thenReturn(null); - contentProviderUtils.deleteTrack(context, TRACK_ID_0); - - InputStream inputStream = new ByteArrayInputStream(xml.getBytes()); - GpxFileTrackImporter gpxFileTrackImporter = new GpxFileTrackImporter(context, contentProviderUtils); - long trackId = gpxFileTrackImporter.importFile(inputStream); - Assert.assertEquals(-1L, trackId); - } -} diff --git a/src/androidTest/java/de/dennisguse/opentracks/io/file/importer/KmlFileTrackImporterTest.java b/src/androidTest/java/de/dennisguse/opentracks/io/file/importer/KmlFileTrackImporterTest.java deleted file mode 100644 index bed02385e..000000000 --- a/src/androidTest/java/de/dennisguse/opentracks/io/file/importer/KmlFileTrackImporterTest.java +++ /dev/null @@ -1,133 +0,0 @@ -/* - * Copyright 2012 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.importer; - -import org.junit.Assert; -import org.junit.Test; -import org.junit.runner.RunWith; -import org.mockito.ArgumentCaptor; -import org.mockito.junit.MockitoJUnitRunner; - -import java.io.ByteArrayInputStream; -import java.io.InputStream; - -import de.dennisguse.opentracks.content.data.Track; -import de.dennisguse.opentracks.content.data.TrackPoint; -import de.dennisguse.opentracks.util.PreferencesUtils; - -import static org.mockito.Mockito.any; -import static org.mockito.Mockito.atLeastOnce; -import static org.mockito.Mockito.eq; -import static org.mockito.Mockito.verify; -import static org.mockito.Mockito.when; - -/** - * Tests for {@link KmlFileTrackImporter}. - * - * @author Jimmy Shih - */ -@RunWith(MockitoJUnitRunner.class) -public class KmlFileTrackImporterTest extends AbstractTestFileTrackImporter { - - private static final String VALID_ONE_TRACK_ONE_SEGMENT_GPX = "" - + getNameAndDescription(TRACK_NAME_0, TRACK_DESCRIPTION_0) + "" - + getTrackPoint(0, TRACK_TIME_0) + getTrackPoint(1, TRACK_TIME_1) - + ""; - private static final String VALID_ONE_TRACK_TWO_SEGMENTS_GPX = "" - + getNameAndDescription(TRACK_NAME_0, TRACK_DESCRIPTION_0) + "" - + getTrackPoint(0, TRACK_TIME_0) + getTrackPoint(1, TRACK_TIME_1) + "" - + getTrackPoint(2, TRACK_TIME_2) + getTrackPoint(3, TRACK_TIME_3) - + ""; - - private static String getNameAndDescription(String name, String description) { - return ""; - } - - private static String getTrackPoint(int index, String time) { - String latitude = Double.toString(TRACK_LATITUDE + index); - String longitude = Double.toString(TRACK_LONGITUDE + index); - String altitude = Double.toString(TRACK_ELEVATION + index); - return "" + time + "" + "" + longitude + " " + latitude + " " + altitude + ""; - } - - @Test - public void testOneTrackOneSegment() throws Exception { - // given - TrackPoint trackPoint0 = createTrackPoint(0, DATE_FORMAT_0.parse(TRACK_TIME_0).getTime()); - TrackPoint trackPointl1 = createTrackPoint(1, DATE_FORMAT_1.parse(TRACK_TIME_1).getTime()); - - contentProviderUtils.clearTrack(context, TRACK_ID_0); - expectFirstTrackPoint(trackPoint0, TRACK_ID_0, TRACK_POINT_ID_0); - - // A flush happens at the end - when(contentProviderUtils.bulkInsertTrackPoint((TrackPoint[]) any(), eq(TRACK_ID_0))).thenReturn(1); - when(contentProviderUtils.getLastTrackPointId(TRACK_ID_0)).thenReturn(TRACK_POINT_ID_1); - when(contentProviderUtils.getTrack(PreferencesUtils.getRecordingTrackId(context))).thenReturn(null); - - ArgumentCaptor trackCaptor = ArgumentCaptor.forClass(Track.class); - expectTrackUpdate(trackCaptor, true, TRACK_ID_0); - - // when - InputStream inputStream = new ByteArrayInputStream(VALID_ONE_TRACK_ONE_SEGMENT_GPX.getBytes()); - KmlFileTrackImporter kmlFileTrackImporter = new KmlFileTrackImporter(context, TRACK_ID_0, contentProviderUtils); - long trackId = kmlFileTrackImporter.importFile(inputStream); - - // then - verify(contentProviderUtils, atLeastOnce()).updateTrack(trackCaptor.capture()); - Assert.assertEquals(TRACK_ID_0, trackId); - - long time0 = DATE_FORMAT_0.parse(TRACK_TIME_0).getTime(); - long time1 = DATE_FORMAT_1.parse(TRACK_TIME_1).getTime(); - Assert.assertEquals(time1 - time0, trackCaptor.getValue().getTrackStatistics().getTotalTime()); - verifyTrack(trackCaptor.getValue(), TRACK_NAME_0, TRACK_DESCRIPTION_0, time0); - } - - @Test - public void testOneTrackTwoSegments() throws Exception { - // given - TrackPoint trackPoint0 = createTrackPoint(0, DATE_FORMAT_0.parse(TRACK_TIME_0).getTime()); - - contentProviderUtils.clearTrack(context, TRACK_ID_0); - expectFirstTrackPoint(trackPoint0, TRACK_ID_0, TRACK_POINT_ID_0); - - // A flush happens at the end - when(contentProviderUtils.bulkInsertTrackPoint((TrackPoint[]) any(), eq(TRACK_ID_0))).thenReturn(5); - when(contentProviderUtils.getLastTrackPointId(TRACK_ID_0)).thenReturn(TRACK_POINT_ID_3); - when(contentProviderUtils.getTrack(PreferencesUtils.getRecordingTrackId(context))).thenReturn(null); - - ArgumentCaptor trackCaptor = ArgumentCaptor.forClass(Track.class); - expectTrackUpdate(trackCaptor, true, TRACK_ID_0); - - // when - InputStream inputStream = new ByteArrayInputStream(VALID_ONE_TRACK_TWO_SEGMENTS_GPX.getBytes()); - KmlFileTrackImporter kmlFileTrackImporter = new KmlFileTrackImporter(context, TRACK_ID_0, contentProviderUtils); - long trackId = kmlFileTrackImporter.importFile(inputStream); - - // then - verify(contentProviderUtils, atLeastOnce()).updateTrack(trackCaptor.capture()); - Assert.assertEquals(TRACK_ID_0, trackId); - - long time0 = DATE_FORMAT_0.parse(TRACK_TIME_0).getTime(); - long time1 = DATE_FORMAT_1.parse(TRACK_TIME_1).getTime(); - long time2 = DATE_FORMAT_1.parse(TRACK_TIME_2).getTime(); - long time3 = DATE_FORMAT_1.parse(TRACK_TIME_3).getTime(); - Assert.assertEquals(time1 - time0 + time3 - time2, trackCaptor.getValue().getTrackStatistics().getTotalTime()); - - verifyTrack(trackCaptor.getValue(), TRACK_NAME_0, TRACK_DESCRIPTION_0, DATE_FORMAT_0.parse(TRACK_TIME_0).getTime()); - } -} diff --git a/src/main/java/de/dennisguse/opentracks/fragments/ImportProgressDialogFragment.java b/src/main/java/de/dennisguse/opentracks/fragments/ImportProgressDialogFragment.java index 346c6f15a..19638eec5 100644 --- a/src/main/java/de/dennisguse/opentracks/fragments/ImportProgressDialogFragment.java +++ b/src/main/java/de/dennisguse/opentracks/fragments/ImportProgressDialogFragment.java @@ -205,9 +205,9 @@ public class ImportProgressDialogFragment extends DialogFragment { if (TrackFileFormat.GPX.getExtension().equals(fileExtension)) { trackImporter = new GpxFileTrackImporter(context); } else if (TrackFileFormat.KML_WITH_TRACKDETAIL_AND_SENSORDATA.getExtension().equals(fileExtension)) { - trackImporter = new KmlFileTrackImporter(ImportProgressDialogFragment.this.getContext(), -1L); + trackImporter = new KmlFileTrackImporter(ImportProgressDialogFragment.this.getContext()); } else if (TrackFileFormat.KMZ_WITH_TRACKDETAIL_AND_SENSORDATA_AND_PICTURES.getExtension().equals(fileExtension)) { - trackImporter = new KmzTrackImporter(context, -1L, file.getUri()); + trackImporter = new KmzTrackImporter(context, file.getUri()); } else { Log.i(TAG, "Unsupported file format."); return false; diff --git a/src/main/java/de/dennisguse/opentracks/io/file/exporter/GpxTrackWriter.java b/src/main/java/de/dennisguse/opentracks/io/file/exporter/GpxTrackWriter.java index b340c2996..8756fd831 100644 --- a/src/main/java/de/dennisguse/opentracks/io/file/exporter/GpxTrackWriter.java +++ b/src/main/java/de/dennisguse/opentracks/io/file/exporter/GpxTrackWriter.java @@ -33,6 +33,7 @@ import de.dennisguse.opentracks.util.StringUtils; * * @author Sandor Dornbush */ +//TODO Export waypoints public class GpxTrackWriter implements TrackWriter { private static final NumberFormat ELEVATION_FORMAT = NumberFormat.getInstance(Locale.US); @@ -93,10 +94,15 @@ public class GpxTrackWriter implements TrackWriter { printWriter.println("xmlns=\"http://www.topografix.com/GPX/1/1\""); printWriter.println("xmlns:topografix=\"http://www.topografix.com/GPX/Private/TopoGrafix/0/1\""); printWriter.println("xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\""); + printWriter.println("xmlns:atom=\"http://www.w3.org/2005/Atom\""); + printWriter.println("xmlns:opentracks=\"http://opentracksapp.com/xmlschemas/v1\""); printWriter.println("xmlns:gpxtpx=\"http://www.garmin.com/xmlschemes/TrackPointExtension/v2\""); - printWriter.println("xsi:schemaLocation=\"http://www.topografix.com/GPX/1/1 http://www.topografix.com/GPX/1/1/gpx.xsd" + printWriter.println("xsi:schemaLocation=" + + "\"http://www.topografix.com/GPX/1/1 http://www.topografix.com/GPX/1/1/gpx.xsd" + " http://www.topografix.com/GPX/Private/TopoGrafix/0/1 http://www.topografix.com/GPX/Private/TopoGrafix/0/1/topografix.xsd" - + " http://www.garmin.com/xmlschemas/TrackPointExtension/v2 https://www8.garmin.com/xmlschemas/TrackPointExtensionv2.xsd\">"); + + " http://www.garmin.com/xmlschemas/TrackPointExtension/v2 https://www8.garmin.com/xmlschemas/TrackPointExtensionv2.xsd" + + " http://opentracksapp.com/xmlschemas/v1 http://opentracksapp.com/xmlschemas/OpenTracks_v1.xsd\">"); + printWriter.println(""); Track track = tracks[0]; @@ -127,17 +133,15 @@ public class GpxTrackWriter implements TrackWriter { public void writeWaypoint(Waypoint waypoint) { if (printWriter != null) { Location location = waypoint.getLocation(); - if (location != null) { - printWriter.println(""); - if (location.hasAltitude()) { - printWriter.println("" + ELEVATION_FORMAT.format(location.getAltitude()) + ""); - } - printWriter.println(""); - printWriter.println("" + StringUtils.formatCData(waypoint.getName()) + ""); - printWriter.println("" + StringUtils.formatCData(waypoint.getDescription()) + ""); - printWriter.println("" + StringUtils.formatCData(waypoint.getCategory()) + ""); - printWriter.println(""); + printWriter.println(""); + if (location.hasAltitude()) { + printWriter.println("" + ELEVATION_FORMAT.format(location.getAltitude()) + ""); } + printWriter.println(""); + printWriter.println("" + StringUtils.formatCData(waypoint.getName()) + ""); + printWriter.println("" + StringUtils.formatCData(waypoint.getDescription()) + ""); + printWriter.println("" + StringUtils.formatCData(waypoint.getCategory()) + ""); + printWriter.println(""); } } @@ -158,7 +162,11 @@ public class GpxTrackWriter implements TrackWriter { printWriter.println("" + StringUtils.formatCData(track.getName()) + ""); printWriter.println("" + StringUtils.formatCData(track.getDescription()) + ""); printWriter.println("" + StringUtils.formatCData(track.getCategory()) + ""); - printWriter.println("c0c0c0"); + + printWriter.println(""); + printWriter.println("c0c0c0"); + printWriter.println("" + track.getUuid() + ""); + printWriter.println(""); } } diff --git a/src/main/java/de/dennisguse/opentracks/io/file/exporter/KmlTrackWriter.java b/src/main/java/de/dennisguse/opentracks/io/file/exporter/KmlTrackWriter.java index 6faa04a5e..385dd28c4 100644 --- a/src/main/java/de/dennisguse/opentracks/io/file/exporter/KmlTrackWriter.java +++ b/src/main/java/de/dennisguse/opentracks/io/file/exporter/KmlTrackWriter.java @@ -108,7 +108,10 @@ public class KmlTrackWriter implements TrackWriter { printWriter.println(""); printWriter.println(""); + printWriter.println("xmlns:atom=\"http://www.w3.org/2005/Atom\""); + printWriter.println("xmlns:opentracks=\"http://opentracksapp.com/xmlschemas/v1\">"); + //TODO ADD xsi:schemaLocation here! + printWriter.println(""); printWriter.println("1"); printWriter.println("1"); @@ -204,6 +207,7 @@ public class KmlTrackWriter implements TrackWriter { printWriter.println("" + StringUtils.formatCData(track.getName()) + ""); printWriter.println("" + StringUtils.formatCData(track.getDescription()) + ""); printWriter.println("" + StringUtils.formatCData(track.getIcon()) + ""); + printWriter.println("" + track.getUuid() + ""); } printWriter.println("#" + TRACK_STYLE + ""); @@ -460,7 +464,7 @@ public class KmlTrackWriter implements TrackWriter { /** * Writes a simple array style. * - * @param name the name of the simple array. + * @param name the name of the simple array. * @param extendedDataType the extended data display name */ private void writeSimpleArrayStyle(String name, String extendedDataType) { 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 26f92b77c..feb0d4c5e 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 @@ -42,6 +42,7 @@ import de.dennisguse.opentracks.util.FileUtils; * @author Jimmy Shih */ public class KmzTrackExporter implements TrackExporter { + private static final String KMZ_IMAGES_DIR = "images"; public static final String KMZ_KML_FILE = "doc.kml"; @@ -49,7 +50,7 @@ public class KmzTrackExporter implements TrackExporter { private static final int BUFFER_SIZE = 4096; private final ContentProviderUtils contentProviderUtils; - private final TrackExporter fileTrackExporter; + private final FileTrackExporter fileTrackExporter; private final Track[] tracks; private final boolean exportPhotos; @@ -58,10 +59,10 @@ public class KmzTrackExporter implements TrackExporter { * Constructor. * * @param contentProviderUtils the content provider utils - * @param trackExporter the file track exporter + * @param trackExporter the file track exporter * @param tracks the tracks to export */ - public KmzTrackExporter(ContentProviderUtils contentProviderUtils, TrackExporter trackExporter, Track[] tracks, boolean exportPhotos) { + public KmzTrackExporter(ContentProviderUtils contentProviderUtils, FileTrackExporter trackExporter, Track[] tracks, boolean exportPhotos) { this.contentProviderUtils = contentProviderUtils; this.fileTrackExporter = trackExporter; this.tracks = tracks; 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 cad79754e..63b032cb1 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 @@ -17,6 +17,7 @@ package de.dennisguse.opentracks.io.file.importer; import android.content.Context; +import android.database.sqlite.SQLiteConstraintException; import android.net.Uri; import android.util.Log; @@ -62,11 +63,12 @@ abstract class AbstractFileTrackImporter extends DefaultHandler implements Track // The maximum number of buffered locations for bulk-insertion private static final int MAX_BUFFERED_LOCATIONS = 512; + private final Context context; - private final long importTrackId; private final ContentProviderUtils contentProviderUtils; private final int recordingDistanceInterval; + private long importTrackId = PreferencesUtils.RECORDING_TRACK_ID_DEFAULT; private final List trackIds = new ArrayList<>(); private final List waypoints = new ArrayList<>(); @@ -96,19 +98,17 @@ abstract class AbstractFileTrackImporter extends DefaultHandler implements Track // The SAX locator to get the current line information private Locator locator; - /** - * @param context the context - * @param importTrackId the track id to import to. -1L to import to a new track. - */ - @Deprecated - // Remove importTrackId - AbstractFileTrackImporter(Context context, long importTrackId, ContentProviderUtils contentProviderUtils) { + AbstractFileTrackImporter(Context context, ContentProviderUtils contentProviderUtils) { this.context = context; - this.importTrackId = importTrackId; this.contentProviderUtils = contentProviderUtils; this.recordingDistanceInterval = PreferencesUtils.getRecordingDistanceInterval(context); } + AbstractFileTrackImporter(Context context, ContentProviderUtils contentProviderUtils, long importTrackId) { + this(context, contentProviderUtils); + this.importTrackId = importTrackId; + } + @Override public void setDocumentLocator(Locator locator) { this.locator = locator; @@ -134,12 +134,10 @@ abstract class AbstractFileTrackImporter extends DefaultHandler implements Track saxParser.parse(inputStream, this); Log.d(TAG, "Total import time: " + (System.currentTimeMillis() - start) + "ms"); if (trackIds.size() != 1) { - Log.d(TAG, trackIds.size() + " tracks imported"); - cleanImport(); - return -1L; + throw new MultiTracksImportException(); } return trackIds.get(0); - } catch (IOException | SAXException | ParserConfigurationException e) { + } catch (MultiTracksImportException | IOException | SAXException | SQLiteConstraintException | ParserConfigurationException e) { Log.e(TAG, "Unable to import file", e); cleanImport(); return -1L; @@ -544,4 +542,9 @@ abstract class AbstractFileTrackImporter extends DefaultHandler implements Track // The number of buffered locations int numBufferedTrackPoints = 0; } + + @Deprecated + //TODO According to the reader AND writer, we support multiple tracks per file; we should implement it here also. + private static class MultiTracksImportException extends RuntimeException { + } } 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 44b678682..107a1679e 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 @@ -45,6 +45,7 @@ public class GpxFileTrackImporter extends AbstractFileTrackImporter { private static final String TAG_TRACK_SEGMENT = "trkseg"; private static final String TAG_TYPE = "type"; private static final String TAG_WAYPOINT = "wpt"; + private static final String TAG_ID = "opentracks:trackid"; private static final String ATTRIBUTE_LAT = "lat"; private static final String ATTRIBUTE_LON = "lon"; @@ -64,7 +65,7 @@ public class GpxFileTrackImporter extends AbstractFileTrackImporter { @VisibleForTesting GpxFileTrackImporter(Context context, ContentProviderUtils contentProviderUtils) { - super(context, -1L, contentProviderUtils); + super(context, contentProviderUtils); } @Override @@ -144,6 +145,10 @@ public class GpxFileTrackImporter extends AbstractFileTrackImporter { cadence = content.trim(); } break; + case TAG_ID: + if (content != null) { + uuid = content.trim(); + } } // Reset element content 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 d93bb8d60..83bb2fa41 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 @@ -57,6 +57,7 @@ public class KmlFileTrackImporter extends AbstractFileTrackImporter { private static final String TAG_STYLE_URL = "styleUrl"; private static final String TAG_VALUE = "value"; private static final String TAG_WHEN = "when"; + private static final String TAG_UUID = "opentracks:trackid"; private static final String ATTRIBUTE_NAME = "name"; @@ -68,23 +69,17 @@ public class KmlFileTrackImporter extends AbstractFileTrackImporter { private ArrayList heartRateList; private ArrayList powerList; - /** - * @param context the context - * @param importTrackId track id to import to. This should not be -1L so that images in the kmz file can be imported. - */ - @Deprecated //TODO Do not pass importTrackId here; get it from somewhere else - public KmlFileTrackImporter(Context context, long importTrackId) { - this(context, importTrackId, new ContentProviderUtils(context)); + public KmlFileTrackImporter(Context context) { + this(context, new ContentProviderUtils(context)); } @VisibleForTesting - KmlFileTrackImporter(Context context, long importTrackId, ContentProviderUtils contentProviderUtils) { - super(context, importTrackId, contentProviderUtils); + KmlFileTrackImporter(Context context, ContentProviderUtils contentProviderUtils) { + super(context, contentProviderUtils); } @Override - public void startElement(String uri, String localName, String tag, Attributes attributes) - throws SAXException { + public void startElement(String uri, String localName, String tag, Attributes attributes) throws SAXException { switch (tag) { case TAG_PLACEMARK: case TAG_PHOTO_OVERLAY: @@ -109,50 +104,70 @@ public class KmlFileTrackImporter extends AbstractFileTrackImporter { @Override public void endElement(String uri, String localName, String tag) throws SAXException { - //TODO Check if order is relevant (uses localname and tag); and convert to switch statement - if (tag.equals(TAG_KML)) { - onFileEnd(); - } else if (tag.equals(TAG_PLACEMARK) || tag.equals(TAG_PHOTO_OVERLAY)) { - // Note that a track is contained in a Placemark, calling onWaypointend is save since waypointType is not set for a track. - onWaypointEnd(); - } else if (localName.equals(TAG_COORDINATES)) { - onWaypointLocationEnd(); - } else if (tag.equals(TAG_GX_MULTI_TRACK)) { - onTrackEnd(); - } else if (tag.equals(TAG_GX_TRACK)) { - onTrackSegmentEnd(); - } else if (tag.equals(TAG_GX_COORD)) { - onTrackPointEnd(); - } else if (tag.equals(TAG_GX_VALUE)) { - onExtendedDataValueEnd(); - } else if (tag.equals(TAG_NAME)) { - if (content != null) { - name = content.trim(); - } - } else if (localName.equals(TAG_DESCRIPTION)) { - if (content != null) { - description = content.trim(); - } - } else if (localName.equals(TAG_ICON)) { - if (content != null) { - icon = content.trim(); - } - } else if (localName.equals(TAG_VALUE)) { - if (content != null) { - category = content.trim(); - } - } else if (localName.equals(TAG_WHEN)) { - if (content != null) { - time = content.trim(); - } - } else if (localName.equals(TAG_STYLE_URL)) { - if (content != null) { - waypointType = content.trim(); - } - } else if (localName.equals(TAG_HREF)) { - if (content != null) { - photoUrl = content.trim(); - } + switch (tag) { + case TAG_KML: + onFileEnd(); + break; + case TAG_PLACEMARK: + case TAG_PHOTO_OVERLAY: + // Note that a track is contained in a Placemark, calling onWaypointend is save since waypointType is not set for a track. + onWaypointEnd(); + break; + case TAG_COORDINATES: + onWaypointLocationEnd(); + break; + case TAG_GX_MULTI_TRACK: + onTrackEnd(); + break; + case TAG_GX_TRACK: + onTrackSegmentEnd(); + break; + case TAG_GX_COORD: + onTrackPointEnd(); + break; + case TAG_GX_VALUE: + onExtendedDataValueEnd(); + break; + case TAG_NAME: + if (content != null) { + name = content.trim(); + } + break; + case TAG_UUID: + if (content != null) { + uuid = content.trim(); + } + break; + case TAG_DESCRIPTION: + if (content != null) { + description = content.trim(); + } + break; + case TAG_ICON: + if (content != null) { + icon = content.trim(); + } + break; + case TAG_VALUE: + if (content != null) { + category = content.trim(); + } + break; + case TAG_WHEN: + if (content != null) { + time = content.trim(); + } + break; + case TAG_STYLE_URL: + if (content != null) { + waypointType = content.trim(); + } + break; + case TAG_HREF: + if (content != null) { + photoUrl = content.trim(); + } + break; } // Reset element content diff --git a/src/main/java/de/dennisguse/opentracks/io/file/importer/KmzTrackImporter.java b/src/main/java/de/dennisguse/opentracks/io/file/importer/KmzTrackImporter.java index 3d8e8545a..353745a30 100644 --- a/src/main/java/de/dennisguse/opentracks/io/file/importer/KmzTrackImporter.java +++ b/src/main/java/de/dennisguse/opentracks/io/file/importer/KmzTrackImporter.java @@ -52,18 +52,15 @@ public class KmzTrackImporter implements TrackImporter { private static final int BUFFER_SIZE = 4096; private final Context context; - private final long importTrackId; + private long importTrackId = PreferencesUtils.RECORDING_TRACK_ID_DEFAULT; private final Uri uriKmzFile; /** - * @param context the context - * @param importTrackId track id to import to. This should not be -1L so that images in the kmz file can be imported. - * @param uriFile URI of the kmz file. + * @param context the context + * @param uriFile URI of the kmz file. */ - @Deprecated //TODO Do not pass importTrackId here; get it from somewhere else - public KmzTrackImporter(Context context, long importTrackId, Uri uriFile) { + public KmzTrackImporter(Context context, Uri uriFile) { this.context = context; - this.importTrackId = importTrackId; this.uriKmzFile = uriFile; } @@ -244,7 +241,7 @@ public class KmzTrackImporter implements TrackImporter { * @return the imported track id or -1L */ private long parseKml(ZipInputStream zipInputStream) throws IOException { - KmlFileTrackImporter kmlFileTrackImporter = new KmlFileTrackImporter(context, importTrackId); + KmlFileTrackImporter kmlFileTrackImporter = new KmlFileTrackImporter(context); try (ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(getKml(zipInputStream))) { return kmlFileTrackImporter.importFile(byteArrayInputStream);