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.
This commit is contained in:
Dennis Guse
2020-07-06 20:20:46 +02:00
parent db1167ead5
commit f8afa9dbad
15 changed files with 246 additions and 482 deletions
+8
View File
@@ -0,0 +1,8 @@
<?xml version="1.0"?>
<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema"
targetNamespace="http://opentracksapp.com/xmlschemas/v1">
<xs:element name="trackuuid"
type="xs:string" />
</xs:schema>
@@ -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()));
}
}
@@ -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() {
@@ -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<Track> 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);
@@ -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));
@@ -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 = "<gpx><trk>"
+ getNameAndDescription(TRACK_NAME_0, TRACK_DESCRIPTION_0) + "<trkseg>"
+ getTrackPoint(0, TRACK_TIME_0) + getTrackPoint(1, TRACK_TIME_1) + "</trkseg></trk></gpx>";
private static final String VALID_ONE_TRACK_TWO_SEGMENTS_GPX = "<gpx><trk>"
+ getNameAndDescription(TRACK_NAME_0, TRACK_DESCRIPTION_0) + "<trkseg>"
+ getTrackPoint(0, TRACK_TIME_0) + getTrackPoint(1, TRACK_TIME_1) + "</trkseg><trkseg>"
+ getTrackPoint(2, TRACK_TIME_2) + getTrackPoint(3, TRACK_TIME_3) + "</trkseg></trk></gpx>";
private static final String VALID_ONE_TRACK_TWO_SEGMENTS_NO_TIME_GPX = "<gpx><trk>"
+ getNameAndDescription(TRACK_NAME_0, TRACK_DESCRIPTION_0) + "<trkseg>"
+ getTrackPoint(0, null) + getTrackPoint(1, null) + "</trkseg><trkseg>"
+ getTrackPoint(2, null) + getTrackPoint(3, null) + "</trkseg></trk></gpx>";
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 "<name><![CDATA[" + name + "]]></name>" + "<desc><![CDATA[" + description + "]]></desc>";
}
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("<trkpt lat=\"").append(latitude).append("\" lon=\"").append(longitude).append("\"><ele>").append(elevation).append("</ele>");
if (time != null) {
buffer.append("<time>").append(time).append("</time>");
}
buffer.append("</trkpt>");
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<Track> 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<Track> 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<Track> 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);
}
}
@@ -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 = "<kml xmlns:gx=\"http://www.google.com/kml/ext/2.2\"><Placemark>"
+ getNameAndDescription(TRACK_NAME_0, TRACK_DESCRIPTION_0) + "<gx:MultiTrack><gx:Track>"
+ getTrackPoint(0, TRACK_TIME_0) + getTrackPoint(1, TRACK_TIME_1)
+ "</gx:Track></gx:MultiTrack></Placemark></kml>";
private static final String VALID_ONE_TRACK_TWO_SEGMENTS_GPX = "<kml xmlns:gx=\"http://www.google.com/kml/ext/2.2\"><Placemark>"
+ getNameAndDescription(TRACK_NAME_0, TRACK_DESCRIPTION_0) + "<gx:MultiTrack><gx:Track>"
+ getTrackPoint(0, TRACK_TIME_0) + getTrackPoint(1, TRACK_TIME_1) + "</gx:Track><gx:Track>"
+ getTrackPoint(2, TRACK_TIME_2) + getTrackPoint(3, TRACK_TIME_3)
+ "</gx:Track></gx:MultiTrack></Placemark></kml>";
private static String getNameAndDescription(String name, String description) {
return "<name><![CDATA[" + name + "]]></name><description><![CDATA[" + description
+ "]]></description>";
}
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 "<when>" + time + "</when>" + "<gx:coord>" + longitude + " " + latitude + " " + altitude + "</gx:coord>";
}
@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<Track> 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<Track> 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());
}
}
@@ -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;
@@ -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("<metadata>");
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("<wpt " + formatLocation(location) + ">");
if (location.hasAltitude()) {
printWriter.println("<ele>" + ELEVATION_FORMAT.format(location.getAltitude()) + "</ele>");
}
printWriter.println("<time>" + StringUtils.formatDateTimeIso8601(location.getTime()) + "</time>");
printWriter.println("<name>" + StringUtils.formatCData(waypoint.getName()) + "</name>");
printWriter.println("<desc>" + StringUtils.formatCData(waypoint.getDescription()) + "</desc>");
printWriter.println("<type>" + StringUtils.formatCData(waypoint.getCategory()) + "</type>");
printWriter.println("</wpt>");
printWriter.println("<wpt " + formatLocation(location) + ">");
if (location.hasAltitude()) {
printWriter.println("<ele>" + ELEVATION_FORMAT.format(location.getAltitude()) + "</ele>");
}
printWriter.println("<time>" + StringUtils.formatDateTimeIso8601(location.getTime()) + "</time>");
printWriter.println("<name>" + StringUtils.formatCData(waypoint.getName()) + "</name>");
printWriter.println("<desc>" + StringUtils.formatCData(waypoint.getDescription()) + "</desc>");
printWriter.println("<type>" + StringUtils.formatCData(waypoint.getCategory()) + "</type>");
printWriter.println("</wpt>");
}
}
@@ -158,7 +162,11 @@ public class GpxTrackWriter implements TrackWriter {
printWriter.println("<name>" + StringUtils.formatCData(track.getName()) + "</name>");
printWriter.println("<desc>" + StringUtils.formatCData(track.getDescription()) + "</desc>");
printWriter.println("<type>" + StringUtils.formatCData(track.getCategory()) + "</type>");
printWriter.println("<extensions><topografix:color>c0c0c0</topografix:color></extensions>");
printWriter.println("<extensions>");
printWriter.println("<topografix:color>c0c0c0</topografix:color>");
printWriter.println("<opentracks:trackid>" + track.getUuid() + "</opentracks:trackid>");
printWriter.println("</extensions>");
}
}
@@ -108,7 +108,10 @@ public class KmlTrackWriter implements TrackWriter {
printWriter.println("<?xml version=\"1.0\" encoding=\"UTF-8\"?>");
printWriter.println("<kml xmlns=\"http://www.opengis.net/kml/2.2\"");
printWriter.println("xmlns:gx=\"http://www.google.com/kml/ext/2.2\"");
printWriter.println("xmlns:atom=\"http://www.w3.org/2005/Atom\">");
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("<Document>");
printWriter.println("<open>1</open>");
printWriter.println("<visibility>1</visibility>");
@@ -204,6 +207,7 @@ public class KmlTrackWriter implements TrackWriter {
printWriter.println("<name>" + StringUtils.formatCData(track.getName()) + "</name>");
printWriter.println("<description>" + StringUtils.formatCData(track.getDescription()) + "</description>");
printWriter.println("<icon>" + StringUtils.formatCData(track.getIcon()) + "</icon>");
printWriter.println("<opentracks:trackid>" + track.getUuid() + "</opentracks:trackid>");
}
printWriter.println("<styleUrl>#" + TRACK_STYLE + "</styleUrl>");
@@ -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) {
@@ -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;
@@ -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<Long> trackIds = new ArrayList<>();
private final List<Waypoint> 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 {
}
}
@@ -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
@@ -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<Float> heartRateList;
private ArrayList<Float> 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
@@ -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);