Marker is now a record.

This commit is contained in:
Dennis Guse
2025-11-29 16:44:33 +01:00
committed by Dennis Guse
parent 9c271972d2
commit 5b4b54694c
22 changed files with 515 additions and 402 deletions
@@ -21,7 +21,6 @@ import de.dennisguse.opentracks.data.models.Position;
import de.dennisguse.opentracks.data.models.Speed; import de.dennisguse.opentracks.data.models.Speed;
import de.dennisguse.opentracks.data.models.Statistics; import de.dennisguse.opentracks.data.models.Statistics;
import de.dennisguse.opentracks.data.models.Track; import de.dennisguse.opentracks.data.models.Track;
import de.dennisguse.opentracks.data.models.TrackBuilder;
import de.dennisguse.opentracks.data.models.TrackPoint; import de.dennisguse.opentracks.data.models.TrackPoint;
import de.dennisguse.opentracks.ui.markers.MarkerUtils; import de.dennisguse.opentracks.ui.markers.MarkerUtils;
import de.dennisguse.opentracks.util.AutoCloseableIterator; import de.dennisguse.opentracks.util.AutoCloseableIterator;
@@ -92,9 +91,9 @@ public class TestDataUtil {
); );
List<Marker> markers = List.of( List<Marker> markers = List.of(
new Marker(trackId, trackPoints.get(1), "Marker 1", "Marker description 1", "Marker category 3", "", null), new Marker(null, trackId, "Marker 1", "Marker description 1", "Marker category 3", "", trackPoints.get(1).getPosition(),null),
new Marker(trackId, trackPoints.get(4), "Marker 2", "Marker description 2", "Marker category 3", "", null), new Marker(null, trackId, "Marker 2", "Marker description 2", "Marker category 3", "", trackPoints.get(4).getPosition(), null),
new Marker(trackId, trackPoints.get(5), "Marker 3", "Marker description 3", "Marker category 3", "", null) new Marker(null, trackId, "Marker 3", "Marker description 3", "Marker category 3", "", trackPoints.get(5).getPosition(), null)
); );
return new TrackData(track, trackPoints, markers); return new TrackData(track, trackPoints, markers);
@@ -150,11 +149,23 @@ public class TestDataUtil {
} }
public static Marker createMarkerWithPhoto(Context context, Track.Id trackId, TrackPoint trackPoint) throws IOException { public static Marker createMarkerWithPhoto(Context context, Track.Id trackId, TrackPoint trackPoint) throws IOException {
return createMarkerWithPhoto(context, trackId, trackPoint, "Marker description");
}
public static Marker createMarkerWithPhoto(Context context, Track.Id trackId, TrackPoint trackPoint, String description) throws IOException {
File dstFile = new File(MarkerUtils.getImageUrl(context, trackId)); File dstFile = new File(MarkerUtils.getImageUrl(context, trackId));
dstFile.createNewFile(); dstFile.createNewFile();
Uri photoUri = FileUtils.getUriForFile(context, dstFile); Uri photoUri = FileUtils.getUriForFile(context, dstFile);
return new Marker(trackId, trackPoint, "Marker name", "Marker description", "Marker category", "", photoUri); return new Marker(
null,
trackId,
"Marker name",
description,
"Marker category",
"",
trackPoint.getPosition(),
photoUri);
} }
public static List<TrackPoint> getTrackPoints(ContentProviderUtils contentProviderUtils, Track.Id trackId) { public static List<TrackPoint> getTrackPoints(ContentProviderUtils contentProviderUtils, Track.Id trackId) {
@@ -57,6 +57,7 @@ import de.dennisguse.opentracks.data.models.AltitudeExtremities;
import de.dennisguse.opentracks.data.models.AltitudeGainLoss; import de.dennisguse.opentracks.data.models.AltitudeGainLoss;
import de.dennisguse.opentracks.data.models.Distance; import de.dennisguse.opentracks.data.models.Distance;
import de.dennisguse.opentracks.data.models.Marker; import de.dennisguse.opentracks.data.models.Marker;
import de.dennisguse.opentracks.data.models.MarkerBuilder;
import de.dennisguse.opentracks.data.models.Position; import de.dennisguse.opentracks.data.models.Position;
import de.dennisguse.opentracks.data.models.Speed; import de.dennisguse.opentracks.data.models.Speed;
import de.dennisguse.opentracks.data.models.Statistics; import de.dennisguse.opentracks.data.models.Statistics;
@@ -232,8 +233,16 @@ public class CustomContentProviderUtilsTest {
Track.Id trackId = new Track.Id(System.currentTimeMillis()); Track.Id trackId = new Track.Id(System.currentTimeMillis());
TestDataUtil.createTrackAndInsert(contentProviderUtils, trackId, 10); TestDataUtil.createTrackAndInsert(contentProviderUtils, trackId, 10);
Marker marker = new Marker(trackId, getLastValidTrackPoint(trackId)); contentProviderUtils.insertMarker(new Marker(
contentProviderUtils.insertMarker(marker); null,
trackId,
"",
"",
"",
"",
getLastValidTrackPoint(trackId).getPosition(),
null
));
assertCount(1, 10, 1); assertCount(1, 10, 1);
@@ -258,7 +267,7 @@ public class CustomContentProviderUtilsTest {
contentProviderUtils.insertMarker(marker); contentProviderUtils.insertMarker(marker);
assertCount(1, 10, 1); assertCount(1, 10, 1);
assertTrue(marker.hasPhoto()); assertNotNull(marker.photoUrl());
File dir = FileUtils.getPhotoDir(context, trackId); File dir = FileUtils.getPhotoDir(context, trackId);
assertTrue(dir.isDirectory()); assertTrue(dir.isDirectory());
assertEquals(1, dir.list().length); assertEquals(1, dir.list().length);
@@ -288,8 +297,16 @@ public class CustomContentProviderUtilsTest {
TestDataUtil.createTrackAndInsert(contentProviderUtils, trackId2, 10); TestDataUtil.createTrackAndInsert(contentProviderUtils, trackId2, 10);
TestDataUtil.createTrackAndInsert(contentProviderUtils, trackId3, 10); TestDataUtil.createTrackAndInsert(contentProviderUtils, trackId3, 10);
Marker marker = new Marker(trackId1, getLastValidTrackPoint(trackId2)); contentProviderUtils.insertMarker(new Marker(
contentProviderUtils.insertMarker(marker); null,
trackId1,
"",
"",
"",
"",
getLastValidTrackPoint(trackId1).getPosition(),
null
));
assertCount(3, 30, 1); assertCount(3, 30, 1);
@@ -328,7 +345,7 @@ public class CustomContentProviderUtilsTest {
// Check. // Check.
assertCount(3, 30, 2); assertCount(3, 30, 2);
assertTrue(marker1.hasPhoto()); assertNotNull(marker1.photoUrl());
assertTrue(dir1.isDirectory()); assertTrue(dir1.isDirectory());
assertEquals(1, dir1.list().length); assertEquals(1, dir1.list().length);
assertTrue(dir1.exists()); assertTrue(dir1.exists());
@@ -407,9 +424,6 @@ public class CustomContentProviderUtilsTest {
assertEquals(nameNew, contentProviderUtils.getTrack(trackId).name()); assertEquals(nameNew, contentProviderUtils.getTrack(trackId).name());
} }
/**
* Tests the method {@link ContentProviderUtils#createContentValues(Marker)}.
*/
@Test @Test
public void testCreateContentValues_marker() { public void testCreateContentValues_marker() {
Track.Id trackId = new Track.Id(System.currentTimeMillis()); Track.Id trackId = new Track.Id(System.currentTimeMillis());
@@ -432,15 +446,15 @@ public class CustomContentProviderUtilsTest {
)); ));
contentProviderUtils.insertTrack(trackBuilder.getTrack()); contentProviderUtils.insertTrack(trackBuilder.getTrack());
Marker marker = new Marker(trackId, track.second.get(0)); MarkerBuilder marker = new MarkerBuilder(trackId, track.second.get(0));
marker.setDescription(TEST_DESC); marker.setDescription(TEST_DESC);
contentProviderUtils.insertMarker(marker); contentProviderUtils.insertMarker(marker.getMarker());
ContentProviderUtils contentProviderUtils = new ContentProviderUtils(contentResolverMock); ContentProviderUtils contentProviderUtils = new ContentProviderUtils(contentResolverMock);
Marker.Id markerId = new Marker.Id(System.currentTimeMillis()); Marker.Id markerId = new Marker.Id(System.currentTimeMillis());
marker.setId(markerId); marker.setId(markerId);
ContentValues contentValues = contentProviderUtils.createContentValues(marker); ContentValues contentValues = contentProviderUtils.createContentValues(marker.getMarker(), trackId);
assertEquals(markerId.id(), contentValues.get(MarkerColumns._ID)); assertEquals(markerId.id(), contentValues.get(MarkerColumns._ID));
assertEquals((int) (TestDataUtil.INITIAL_LONGITUDE * 1000000), contentValues.get(MarkerColumns.LONGITUDE)); assertEquals((int) (TestDataUtil.INITIAL_LONGITUDE * 1000000), contentValues.get(MarkerColumns.LONGITUDE));
assertEquals(TEST_DESC, contentValues.get(MarkerColumns.DESCRIPTION)); assertEquals(TEST_DESC, contentValues.get(MarkerColumns.DESCRIPTION));
@@ -457,15 +471,19 @@ public class CustomContentProviderUtilsTest {
TestDataUtil.createTrackAndInsert(contentProviderUtils, trackId, 10); TestDataUtil.createTrackAndInsert(contentProviderUtils, trackId, 10);
// Insert at first. // Insert at first.
Marker marker1 = new Marker(trackId, getLastValidTrackPoint(trackId)); Marker.Id marker1Id = contentProviderUtils.insertMarker(new Marker(
marker1.setDescription(TEST_DESC); null,
contentProviderUtils.insertMarker(marker1); trackId,
"",
TEST_DESC,
"",
"",
getLastValidTrackPoint(trackId).getPosition(),
null
));
// Check insert was done. // Check insert was done.
assertEquals(1, TestDataUtil.getMarkers(contentProviderUtils, trackId).size()); assertEquals(1, TestDataUtil.getMarkers(contentProviderUtils, trackId).size());
// Get marker id that needs to delete.
Marker.Id marker1Id = contentProviderUtils.insertMarker(marker1);
// Delete // Delete
contentProviderUtils.deleteMarker(context, marker1Id); contentProviderUtils.deleteMarker(context, marker1Id);
@@ -495,7 +513,7 @@ public class CustomContentProviderUtilsTest {
Marker.Id marker1Id = contentProviderUtils.insertMarker(marker1); Marker.Id marker1Id = contentProviderUtils.insertMarker(marker1);
// Check marker has photo and it's in the external storage. // Check marker has photo and it's in the external storage.
assertTrue(marker1.hasPhoto()); assertNotNull(marker1.photoUrl());
File dir = FileUtils.getPhotoDir(context, trackId); File dir = FileUtils.getPhotoDir(context, trackId);
assertTrue(dir.isDirectory()); assertTrue(dir.isDirectory());
assertEquals(1, dir.list().length); assertEquals(1, dir.list().length);
@@ -534,20 +552,33 @@ public class CustomContentProviderUtilsTest {
// TestDataUtil.insertTrackWithLocations(contentProviderUtils, track); // TestDataUtil.insertTrackWithLocations(contentProviderUtils, track);
// Insert at first. // Insert at first.
Marker marker1 = new Marker(trackId, getLastValidTrackPoint(trackId)); Marker.Id marker1Id = contentProviderUtils.insertMarker(new Marker(
marker1.setDescription(MOCK_DESC); null,
Marker.Id marker1Id = contentProviderUtils.insertMarker(marker1); trackId,
"",
Marker marker2 = new Marker(trackId, getLastValidTrackPoint(trackId)); MOCK_DESC,
marker2.setDescription(MOCK_DESC); "",
Marker.Id marker2Id = contentProviderUtils.insertMarker(marker2); "",
getLastValidTrackPoint(trackId).getPosition(),
null
));
Marker.Id marker2Id = contentProviderUtils.insertMarker(new Marker(
null,
trackId,
"",
MOCK_DESC,
"",
"",
getLastValidTrackPoint(trackId).getPosition(),
null
));
// Delete // Delete
assertNotNull(contentProviderUtils.getMarker(marker1Id)); assertNotNull(contentProviderUtils.getMarker(marker1Id));
contentProviderUtils.deleteMarker(context, marker1Id); contentProviderUtils.deleteMarker(context, marker1Id);
assertNull(contentProviderUtils.getMarker(marker1Id)); assertNull(contentProviderUtils.getMarker(marker1Id));
assertEquals(MOCK_DESC, contentProviderUtils.getMarker(marker2Id).getDescription()); assertEquals(MOCK_DESC, contentProviderUtils.getMarker(marker2Id).description());
} }
/** /**
@@ -558,14 +589,18 @@ public class CustomContentProviderUtilsTest {
Track.Id trackId = new Track.Id(System.currentTimeMillis()); Track.Id trackId = new Track.Id(System.currentTimeMillis());
TestDataUtil.createTrackAndInsert(contentProviderUtils, trackId, 10); TestDataUtil.createTrackAndInsert(contentProviderUtils, trackId, 10);
Marker marker1 = new Marker(trackId, getLastValidTrackPoint(trackId)); for (int i = 0; i < 4; i++) {
Marker marker2 = new Marker(trackId, getLastValidTrackPoint(trackId)); contentProviderUtils.insertMarker(new Marker(
Marker marker3 = new Marker(trackId, getLastValidTrackPoint(trackId)); null,
Marker marker4 = new Marker(trackId, getLastValidTrackPoint(trackId)); trackId,
contentProviderUtils.insertMarker(marker1); "",
contentProviderUtils.insertMarker(marker2); MOCK_DESC,
contentProviderUtils.insertMarker(marker3); "",
contentProviderUtils.insertMarker(marker4); "",
getLastValidTrackPoint(trackId).getPosition(),
null
));
}
assertEquals(Integer.valueOf(4), contentProviderUtils.getNextMarkerNumber(trackId)); assertEquals(Integer.valueOf(4), contentProviderUtils.getNextMarkerNumber(trackId));
} }
@@ -579,11 +614,19 @@ public class CustomContentProviderUtilsTest {
Track.Id trackId = new Track.Id(System.currentTimeMillis()); Track.Id trackId = new Track.Id(System.currentTimeMillis());
TestDataUtil.createTrackAndInsert(contentProviderUtils, trackId, 10); TestDataUtil.createTrackAndInsert(contentProviderUtils, trackId, 10);
Marker marker = new Marker(trackId, getLastValidTrackPoint(trackId)); Marker.Id markerId = contentProviderUtils.insertMarker(
marker.setDescription(TEST_DESC); new Marker(
Marker.Id markerId = contentProviderUtils.insertMarker(marker); null,
trackId,
"",
TEST_DESC,
"",
"",
getLastValidTrackPoint(trackId).getPosition(),
null
));
assertEquals(TEST_DESC, contentProviderUtils.getMarker(markerId).getDescription()); assertEquals(TEST_DESC, contentProviderUtils.getMarker(markerId).description());
} }
/** /**
@@ -595,16 +638,31 @@ public class CustomContentProviderUtilsTest {
TestDataUtil.createTrackAndInsert(contentProviderUtils, trackId, 10); TestDataUtil.createTrackAndInsert(contentProviderUtils, trackId, 10);
// Insert at first. // Insert at first.
Marker marker = new Marker(trackId, getLastValidTrackPoint(trackId)); Marker.Id markerId = contentProviderUtils.insertMarker(
marker.setDescription(TEST_DESC); new Marker(
Marker.Id markerId = contentProviderUtils.insertMarker(marker); null,
trackId,
"",
TEST_DESC,
"",
"",
getLastValidTrackPoint(trackId).getPosition(),
null
));
// Update // Update
marker = contentProviderUtils.getMarker(markerId); contentProviderUtils.updateMarker(context, new Marker(
marker.setDescription(TEST_DESC_NEW); markerId,
contentProviderUtils.updateMarker(context, marker); trackId,
"",
TEST_DESC_NEW,
"",
"",
getLastValidTrackPoint(trackId).getPosition(),
null
));
assertEquals(TEST_DESC_NEW, contentProviderUtils.getMarker(markerId).getDescription()); assertEquals(TEST_DESC_NEW, contentProviderUtils.getMarker(markerId).description());
} }
/** /**
@@ -619,9 +677,7 @@ public class CustomContentProviderUtilsTest {
// Insert at first. // Insert at first.
TrackPoint trackPoint = getLastValidTrackPoint(trackId); TrackPoint trackPoint = getLastValidTrackPoint(trackId);
Marker marker = TestDataUtil.createMarkerWithPhoto(context, trackId, trackPoint); Marker.Id markerId = contentProviderUtils.insertMarker(TestDataUtil.createMarkerWithPhoto(context, trackId, trackPoint, TEST_DESC));
marker.setDescription(TEST_DESC);
Marker.Id markerId = contentProviderUtils.insertMarker(marker);
File dir = new File(FileUtils.getPhotoDir(context), "" + trackId.id()); File dir = new File(FileUtils.getPhotoDir(context), "" + trackId.id());
assertTrue(dir.exists()); assertTrue(dir.exists());
@@ -629,14 +685,13 @@ public class CustomContentProviderUtilsTest {
assertEquals(1, dir.list().length); assertEquals(1, dir.list().length);
// Update // Update
marker = contentProviderUtils.getMarker(markerId); MarkerBuilder marker = new MarkerBuilder(contentProviderUtils.getMarker(markerId));
marker.setName(TEST_NAME_NEW); marker.setName(TEST_NAME_NEW);
marker.setDescription(TEST_DESC_NEW); marker.setDescription(TEST_DESC_NEW);
contentProviderUtils.updateMarker(context, marker); contentProviderUtils.updateMarker(context, marker.getMarker());
assertEquals(TEST_NAME_NEW, contentProviderUtils.getMarker(markerId).getName()); assertEquals(TEST_NAME_NEW, contentProviderUtils.getMarker(markerId).name());
assertEquals(TEST_DESC_NEW, contentProviderUtils.getMarker(markerId).getDescription()); assertEquals(TEST_DESC_NEW, contentProviderUtils.getMarker(markerId).description());
assertTrue(marker.hasPhoto());
assertTrue(dir.exists()); assertTrue(dir.exists());
assertTrue(dir.isDirectory()); assertTrue(dir.isDirectory());
assertEquals(1, dir.list().length); assertEquals(1, dir.list().length);
@@ -654,8 +709,7 @@ public class CustomContentProviderUtilsTest {
// Insert at first. // Insert at first.
TrackPoint trackPoint = getLastValidTrackPoint(trackId); TrackPoint trackPoint = getLastValidTrackPoint(trackId);
Marker marker = TestDataUtil.createMarkerWithPhoto(context, trackId, trackPoint); Marker marker = TestDataUtil.createMarkerWithPhoto(context, trackId, trackPoint, TEST_DESC);
marker.setDescription(TEST_DESC);
Marker.Id markerId = contentProviderUtils.insertMarker(marker); Marker.Id markerId = contentProviderUtils.insertMarker(marker);
File dir = new File(FileUtils.getPhotoDir(context), "" + trackId.id()); File dir = new File(FileUtils.getPhotoDir(context), "" + trackId.id());
@@ -664,15 +718,14 @@ public class CustomContentProviderUtilsTest {
assertEquals(1, dir.list().length); assertEquals(1, dir.list().length);
// Update // Update
marker = contentProviderUtils.getMarker(markerId); MarkerBuilder markerBuilder = new MarkerBuilder(contentProviderUtils.getMarker(markerId));
marker.setName(TEST_NAME_NEW); markerBuilder.setName(TEST_NAME_NEW);
marker.setDescription(TEST_DESC_NEW); markerBuilder.setDescription(TEST_DESC_NEW);
marker.setPhotoUrl(null); markerBuilder.setPhotoUrl(null);
contentProviderUtils.updateMarker(context, marker); contentProviderUtils.updateMarker(context, markerBuilder.getMarker());
assertEquals(TEST_NAME_NEW, contentProviderUtils.getMarker(markerId).getName()); assertEquals(TEST_NAME_NEW, contentProviderUtils.getMarker(markerId).name());
assertEquals(TEST_DESC_NEW, contentProviderUtils.getMarker(markerId).getDescription()); assertEquals(TEST_DESC_NEW, contentProviderUtils.getMarker(markerId).description());
assertFalse(marker.hasPhoto());
assertFalse(dir.exists()); assertFalse(dir.exists());
} }
@@ -688,10 +741,8 @@ public class CustomContentProviderUtilsTest {
// Insert two markers with photos. // Insert two markers with photos.
TrackPoint trackPoint = getLastValidTrackPoint(trackId); TrackPoint trackPoint = getLastValidTrackPoint(trackId);
Marker marker = TestDataUtil.createMarkerWithPhoto(context, trackId, trackPoint); Marker marker = TestDataUtil.createMarkerWithPhoto(context, trackId, trackPoint, TEST_DESC);
marker.setDescription(TEST_DESC); Marker otherMarker = TestDataUtil.createMarkerWithPhoto(context, trackId, trackPoint, TEST_DESC);
Marker otherMarker = TestDataUtil.createMarkerWithPhoto(context, trackId, trackPoint);
otherMarker.setDescription(TEST_DESC);
Marker.Id markerId = contentProviderUtils.insertMarker(marker); Marker.Id markerId = contentProviderUtils.insertMarker(marker);
contentProviderUtils.insertMarker(otherMarker); contentProviderUtils.insertMarker(otherMarker);
@@ -701,12 +752,14 @@ public class CustomContentProviderUtilsTest {
assertEquals(2, dir.list().length); assertEquals(2, dir.list().length);
// Update one marker deleting photo. // Update one marker deleting photo.
marker = contentProviderUtils.getMarker(markerId); MarkerBuilder markerBuilder = new MarkerBuilder(contentProviderUtils.getMarker(markerId));
marker.setPhotoUrl(null); markerBuilder.setPhotoUrl(null);
contentProviderUtils.updateMarker(context, marker); contentProviderUtils.updateMarker(context, markerBuilder.getMarker());
assertEquals(TEST_DESC, contentProviderUtils.getMarker(markerId).getDescription()); // then
assertFalse(marker.hasPhoto()); marker = contentProviderUtils.getMarker(markerId);
assertEquals(TEST_DESC, contentProviderUtils.getMarker(markerId).description());
assertNull(marker.photoUrl());
assertTrue(dir.exists()); assertTrue(dir.exists());
assertTrue(dir.isDirectory()); assertTrue(dir.isDirectory());
assertEquals(1, dir.list().length); assertEquals(1, dir.list().length);
@@ -58,6 +58,7 @@ import de.dennisguse.opentracks.data.models.Cadence;
import de.dennisguse.opentracks.data.models.Distance; import de.dennisguse.opentracks.data.models.Distance;
import de.dennisguse.opentracks.data.models.HeartRate; import de.dennisguse.opentracks.data.models.HeartRate;
import de.dennisguse.opentracks.data.models.Marker; import de.dennisguse.opentracks.data.models.Marker;
import de.dennisguse.opentracks.data.models.MarkerBuilder;
import de.dennisguse.opentracks.data.models.Position; import de.dennisguse.opentracks.data.models.Position;
import de.dennisguse.opentracks.data.models.Power; import de.dennisguse.opentracks.data.models.Power;
import de.dennisguse.opentracks.data.models.Speed; import de.dennisguse.opentracks.data.models.Speed;
@@ -152,7 +153,17 @@ public class ExportImportTest {
Distance sensorDistance = Distance.of(10); // recording distance interval Distance sensorDistance = Distance.of(10); // recording distance interval
sendLocation(trackPointCreator, "2020-02-02T02:02:03Z", 3.1234567, 14.0014567, 10, 13, 15, 1020.25, 1f); sendLocation(trackPointCreator, "2020-02-02T02:02:03Z", 3.1234567, 14.0014567, 10, 13, 15, 1020.25, 1f);
contentProviderUtils.insertMarker(new Marker(trackId, service.getLastStoredTrackPointWithLocation(), "Marker 1", "Marker 1 desc", "Marker 1 category", null, null)); contentProviderUtils.insertMarker(
new Marker(
null,
trackId,
"Marker 1",
"Marker 1 desc",
"Marker 1 category",
null,
service.getLastStoredTrackPointWithLocation().getPosition(),
null
));
// A sensor-only TrackPoint // A sensor-only TrackPoint
trackPointCreator.setClock("2020-02-02T02:02:04Z"); trackPointCreator.setClock("2020-02-02T02:02:04Z");
@@ -166,7 +177,17 @@ public class ExportImportTest {
mockSensorData(trackPointCreator, 5f, Distance.of(2), 69f, 3f, 50f, null); // Distance will be added to next TrackPoint mockSensorData(trackPointCreator, 5f, Distance.of(2), 69f, 3f, 50f, null); // Distance will be added to next TrackPoint
sendLocation(trackPointCreator, "2020-02-02T02:02:17Z", 3.1234567, 14.0014567, 10, 13, 15, 1020.25, 0f); sendLocation(trackPointCreator, "2020-02-02T02:02:17Z", 3.1234567, 14.0014567, 10, 13, 15, 1020.25, 0f);
contentProviderUtils.insertMarker(new Marker(trackId, service.getLastStoredTrackPointWithLocation(), "Marker 2", "Marker 2 desc", "Marker 2 category", null, null)); contentProviderUtils.insertMarker(
new Marker(
null,
trackId,
"Marker 2",
"Marker 2 desc",
"Marker 2 category",
null,
service.getLastStoredTrackPointWithLocation().getPosition(),
null
));
trackPointCreator.setClock("2020-02-02T02:02:18Z"); trackPointCreator.setClock("2020-02-02T02:02:18Z");
trackPointCreator.getSensorManager().sensorDataSet = new SensorDataSet(trackPointCreator); trackPointCreator.getSensorManager().sensorDataSet = new SensorDataSet(trackPointCreator);
@@ -195,7 +216,7 @@ public class ExportImportTest {
contentProviderUtils.updateTrack(trackBuilder.getTrack()); contentProviderUtils.updateTrack(trackBuilder.getTrack());
trackPoints = TestDataUtil.getTrackPoints(contentProviderUtils, trackId); trackPoints = TestDataUtil.getTrackPoints(contentProviderUtils, trackId);
markers = TestDataUtil.getMarkers(contentProviderUtils, trackId); markers = TestDataUtil.getMarkers(contentProviderUtils, trackId);
} }
@LargeTest @LargeTest
@@ -533,20 +554,20 @@ public class ExportImportTest {
private void assertMarkers() { private void assertMarkers() {
List<Marker> importedMarkers = TestDataUtil.getMarkers(contentProviderUtils, importTrackId); List<Marker> importedMarkers = TestDataUtil.getMarkers(contentProviderUtils, importTrackId);
assertEquals(markers.size(), importedMarkers.size()); assertEquals(markers.size(), importedMarkers.size());
for (int i = 0; i < markers.size(); i++) { for (int i = 0; i < markers.size(); i++) {
Marker marker = markers.get(i); Marker marker = markers.get(i);
Marker importMarker = importedMarkers.get(i); Marker importMarker = importedMarkers.get(i);
assertEquals(marker.getCategory(), importMarker.getCategory()); assertEquals(marker.category(), importMarker.category());
assertEquals(marker.getDescription(), importMarker.getDescription()); assertEquals(marker.description(), importMarker.description());
// assertEquals(marker.getIcon(), importMarker.getIcon()); // TODO for KML // assertEquals(marker.getIcon(), importMarker.getIcon()); // TODO for KML
assertEquals(marker.getName(), importMarker.getName()); assertEquals(marker.name(), importMarker.name());
assertFalse(importMarker.hasPhoto()); assertNull(importMarker.photoUrl());
assertEquals(marker.getPosition().latitude(), importMarker.getPosition().latitude(), 0.001); assertEquals(marker.position().latitude(), importMarker.position().latitude(), 0.001);
assertEquals(marker.getPosition().longitude(), importMarker.getPosition().longitude(), 0.001); assertEquals(marker.position().longitude(), importMarker.position().longitude(), 0.001);
assertEquals(marker.getPosition().altitude().toM(), importMarker.getPosition().altitude().toM(), 0.1); assertEquals(marker.position().altitude().toM(), importMarker.position().altitude().toM(), 0.1);
} }
} }
@@ -50,7 +50,6 @@ import de.dennisguse.opentracks.data.models.Power;
import de.dennisguse.opentracks.data.models.Speed; import de.dennisguse.opentracks.data.models.Speed;
import de.dennisguse.opentracks.data.models.Statistics; import de.dennisguse.opentracks.data.models.Statistics;
import de.dennisguse.opentracks.data.models.Track; import de.dennisguse.opentracks.data.models.Track;
import de.dennisguse.opentracks.data.models.TrackBuilder;
import de.dennisguse.opentracks.data.models.TrackPoint; import de.dennisguse.opentracks.data.models.TrackPoint;
import de.dennisguse.opentracks.data.tables.MarkerColumns; import de.dennisguse.opentracks.data.tables.MarkerColumns;
import de.dennisguse.opentracks.data.tables.TrackPointsColumns; import de.dennisguse.opentracks.data.tables.TrackPointsColumns;
@@ -319,7 +318,7 @@ public class ContentProviderUtils {
int latitudeIndex = cursor.getColumnIndexOrThrow(MarkerColumns.LATITUDE); int latitudeIndex = cursor.getColumnIndexOrThrow(MarkerColumns.LATITUDE);
int timeIndex = cursor.getColumnIndexOrThrow(MarkerColumns.TIME); int timeIndex = cursor.getColumnIndexOrThrow(MarkerColumns.TIME);
int altitudeIndex = cursor.getColumnIndexOrThrow(MarkerColumns.ALTITUDE); int altitudeIndex = cursor.getColumnIndexOrThrow(MarkerColumns.ALTITUDE);
int accuracyIndex = cursor.getColumnIndexOrThrow(MarkerColumns.ACCURACY); int accuracyIndex = cursor.getColumnIndexOrThrow(MarkerColumns.HORIZONTAL_ACCURACY);
int bearingIndex = cursor.getColumnIndexOrThrow(MarkerColumns.BEARING); int bearingIndex = cursor.getColumnIndexOrThrow(MarkerColumns.BEARING);
int photoUrlIndex = cursor.getColumnIndexOrThrow(MarkerColumns.PHOTOURL); int photoUrlIndex = cursor.getColumnIndexOrThrow(MarkerColumns.PHOTOURL);
@@ -353,37 +352,27 @@ public class ContentProviderUtils {
bearing, bearing,
null); null);
Track.Id trackId = new Track.Id(cursor.getLong(trackIdIndex)); Uri photoUrl = null;
Marker marker = new Marker(trackId, position);
if (!cursor.isNull(idIndex)) {
marker.setId(new Marker.Id(cursor.getLong(idIndex)));
}
if (!cursor.isNull(nameIndex)) {
marker.setName(cursor.getString(nameIndex));
}
if (!cursor.isNull(descriptionIndex)) {
marker.setDescription(cursor.getString(descriptionIndex));
}
if (!cursor.isNull(categoryIndex)) {
marker.setCategory(cursor.getString(categoryIndex));
}
if (!cursor.isNull(iconIndex)) {
marker.setIcon(cursor.getString(iconIndex));
}
if (!cursor.isNull(photoUrlIndex)) { if (!cursor.isNull(photoUrlIndex)) {
String photoUrl = cursor.getString(photoUrlIndex); if (cursor.getString(photoUrlIndex).isEmpty()) {
if (photoUrl.isEmpty()) {
// Before v4.18.0: a marker without a picture as URL "" // Before v4.18.0: a marker without a picture as URL ""
// TODO Data should be migrated. // TODO Data should be migrated.
marker.setPhotoUrl(null); photoUrl = null;
} else { } else {
marker.setPhotoUrl(Uri.parse(cursor.getString(photoUrlIndex))); photoUrl = Uri.parse(cursor.getString(photoUrlIndex));
} }
} }
return new Marker(
return marker; new Marker.Id(cursor.getLong(idIndex)),
new Track.Id(cursor.getLong(trackIdIndex)),
!cursor.isNull(nameIndex) ? cursor.getString(nameIndex) : "",
!cursor.isNull(descriptionIndex) ? cursor.getString(descriptionIndex) : "",
!cursor.isNull(categoryIndex) ? cursor.getString(categoryIndex) : "",
!cursor.isNull(iconIndex) ? cursor.getString(iconIndex) : "",
position,
photoUrl
);
} }
public void deleteMarker(Context context, Marker.Id markerId) { public void deleteMarker(Context context, Marker.Id markerId) {
@@ -445,24 +434,26 @@ public class ContentProviderUtils {
* @return true if successful. * @return true if successful.
*/ */
public boolean updateMarker(Context context, Marker updateMarker) { public boolean updateMarker(Context context, Marker updateMarker) {
Marker savedMarker = getMarker(updateMarker.getId()); Marker savedMarker = getMarker(updateMarker.id());
if (!updateMarker.hasPhoto()) { if (updateMarker.photoUrl() == null) {
deleteMarkerPhoto(context, savedMarker); deleteMarkerPhoto(context, savedMarker);
} }
int rows = contentResolver.update(MarkerColumns.CONTENT_URI, createContentValues(updateMarker), MarkerColumns._ID + "=?", new String[]{Long.toString(updateMarker.getId().id())}); int rows = contentResolver.update(MarkerColumns.CONTENT_URI, createContentValues(updateMarker, updateMarker.trackId()), MarkerColumns._ID + "=?", new String[]{Long.toString(updateMarker.id().id())});
return rows == 1; return rows == 1;
} }
public Marker.Id insertMarker(@NonNull Marker marker) { public Marker.Id insertMarker(@NonNull Marker marker) {
marker.setId(null); assert marker.id() == null;
Uri uri = contentResolver.insert(MarkerColumns.CONTENT_URI, createContentValues(marker)); assert marker.trackId() != null;
Uri uri = contentResolver.insert(MarkerColumns.CONTENT_URI, createContentValues(marker, marker.trackId()));
return new Marker.Id(ContentUris.parseId(uri)); return new Marker.Id(ContentUris.parseId(uri));
} }
private void deleteMarkerPhoto(Context context, Marker marker) { private void deleteMarkerPhoto(Context context, Marker marker) {
if (marker != null && marker.hasPhoto()) { if (marker != null && marker.photoUrl() != null) {
Uri uri = marker.getPhotoUrl(); Uri uri = marker.photoUrl();
File file = MarkerUtils.buildInternalPhotoFile(context, marker.getTrackId(), uri); File file = MarkerUtils.buildInternalPhotoFile(context, marker.trackId(), uri);
if (file.exists()) { if (file.exists()) {
File parent = file.getParentFile(); File parent = file.getParentFile();
file.delete(); file.delete();
@@ -473,33 +464,33 @@ public class ContentProviderUtils {
} }
} }
ContentValues createContentValues(@NonNull Marker marker) { ContentValues createContentValues(@NonNull Marker marker, @NonNull Track.Id trackId) {
ContentValues values = new ContentValues(); ContentValues values = new ContentValues();
if (marker.getId() != null) { if (marker.id() != null) {
values.put(MarkerColumns._ID, marker.getId().id()); values.put(MarkerColumns._ID, marker.id().id());
} }
values.put(MarkerColumns.NAME, marker.getName()); values.put(MarkerColumns.TRACKID, trackId.id());
values.put(MarkerColumns.DESCRIPTION, marker.getDescription());
values.put(MarkerColumns.CATEGORY, marker.getCategory()); values.put(MarkerColumns.NAME, marker.name());
values.put(MarkerColumns.ICON, marker.getIcon()); values.put(MarkerColumns.DESCRIPTION, marker.description());
values.put(MarkerColumns.TRACKID, marker.getTrackId().id()); values.put(MarkerColumns.CATEGORY, marker.category());
values.put(MarkerColumns.LONGITUDE, (int) (marker.getPosition().longitude() * 1E6)); values.put(MarkerColumns.ICON, marker.icon());
values.put(MarkerColumns.LATITUDE, (int) (marker.getPosition().latitude() * 1E6));
values.put(MarkerColumns.TIME, marker.getTime().toEpochMilli()); values.put(MarkerColumns.LONGITUDE, (int) (marker.position().longitude() * 1E6));
if (marker.hasAltitude()) { values.put(MarkerColumns.LATITUDE, (int) (marker.position().latitude() * 1E6));
values.put(MarkerColumns.ALTITUDE, marker.getAltitude().toM()); values.put(MarkerColumns.TIME, marker.position().time().toEpochMilli());
if (marker.position().hasAltitude()) {
values.put(MarkerColumns.ALTITUDE, marker.position().altitude().toM());
} }
if (marker.hasAccuracy()) { if (marker.position().hasHorizontalAccuracy()) {
values.put(MarkerColumns.ACCURACY, marker.getAccuracy().toM()); values.put(MarkerColumns.HORIZONTAL_ACCURACY, marker.position().horizontalAccuracy().toM());
} }
if (marker.hasBearing()) { if (marker.position().hasBearing()) {
values.put(MarkerColumns.BEARING, marker.getBearing()); values.put(MarkerColumns.BEARING, marker.position().bearing());
} }
if (marker.hasPhoto()) { values.put(MarkerColumns.PHOTOURL, marker.photoUrl() != null ? marker.photoUrl().toString() : null);
values.put(MarkerColumns.PHOTOURL, marker.getPhotoUrl().toString());
}
return values; return values;
} }
@@ -603,7 +594,7 @@ public class ContentProviderUtils {
public int bulkInsertMarkers(List<Marker> markers, Track.Id trackId) { public int bulkInsertMarkers(List<Marker> markers, Track.Id trackId) {
ContentValues[] values = new ContentValues[markers.size()]; ContentValues[] values = new ContentValues[markers.size()];
for (int i = 0; i < markers.size(); i++) { for (int i = 0; i < markers.size(); i++) {
values[i] = createContentValues(markers.get(i)); values[i] = createContentValues(markers.get(i), trackId);
} }
return contentResolver.bulkInsert(MarkerColumns.CONTENT_URI, values); return contentResolver.bulkInsert(MarkerColumns.CONTENT_URI, values);
} }
@@ -1,19 +1,3 @@
/*
* Copyright 2009 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.data.models; package de.dennisguse.opentracks.data.models;
import android.net.Uri; import android.net.Uri;
@@ -23,151 +7,32 @@ import android.os.Parcelable;
import androidx.annotation.NonNull; import androidx.annotation.NonNull;
import androidx.annotation.Nullable; import androidx.annotation.Nullable;
import java.time.Instant;
import java.util.Objects;
/** /**
* NOTE: A marker is indirectly (via it's {@link Position}) assigned to one {@link TrackPoint} via position.time. * * NOTE: A marker is indirectly (via it's {@link Position}) assigned to one {@link TrackPoint} via position.time.
*
* @author Leif Hendrik Wilden
* @author Rodrigo Damazio
*/ */
//TODO All data should be final; no default values. public record Marker(
public final class Marker {
private Id id; @NonNull
private String name = ""; Id id,
private String description = "";
private String category = "";
private String icon = "";
private Track.Id trackId;
//Some data might not be used. @NonNull
private final Position position; Track.Id trackId,
private Uri photoUrl = null; @NonNull
String name,
@NonNull
String description,
@NonNull
String category,
@NonNull
String icon,
public Marker(@Nullable Track.Id trackId, @NonNull TrackPoint trackPoint) { //Some data might not be used.
this.trackId = trackId; Position position,
if (!trackPoint.hasLocation())
throw new RuntimeException("Marker requires a trackpoint with a location.");
this.position = trackPoint.getPosition();
}
public Marker(@Nullable Track.Id trackId, @NonNull Position position) {
this.trackId = trackId;
Objects.requireNonNull(position);
this.position = position;
}
public Marker(@Nullable Track.Id trackId, @NonNull TrackPoint trackPoint, String name, String description, String category, String icon, Uri photoUrl) {
this(trackId, trackPoint);
this.name = name;
this.description = description;
this.category = category;
this.icon = icon;
this.photoUrl = photoUrl;
}
/**
* May be null if the Marker was not loaded from the database.
*/
@Nullable
public Id getId() {
return id;
}
public void setId(Id id) {
this.id = id;
}
public Instant getTime() {
return position.time();
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getDescription() {
return description;
}
public void setDescription(String description) {
this.description = description;
}
public String getCategory() {
return category;
}
public void setCategory(String category) {
this.category = category;
}
public String getIcon() {
return icon;
}
public void setIcon(String icon) {
this.icon = icon;
}
public Track.Id getTrackId() {
return trackId;
}
@Deprecated
public void setTrackId(@NonNull Track.Id trackId) {
this.trackId = trackId;
}
public Position getPosition() {
return position;
}
public boolean hasAccuracy() {
return position.hasHorizontalAccuracy();
}
public Distance getAccuracy() {
return position.horizontalAccuracy();
}
public boolean hasAltitude() {
return position.hasAltitude();
}
public Altitude getAltitude() {
return position.altitude();
}
public boolean hasBearing() {
return position.hasBearing();
}
public Float getBearing() {
return position.bearing();
}
public Uri getPhotoUrl() {
return photoUrl;
}
public void setPhotoUrl(Uri photoUrl) {
this.photoUrl = photoUrl;
}
public boolean hasPhoto() {
return photoUrl != null;
}
@Nullable
Uri photoUrl
) {
public record Id(long id) implements Parcelable { public record Id(long id) implements Parcelable {
@NonNull @NonNull
@@ -0,0 +1,148 @@
/*
* Copyright 2009 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.data.models;
import android.net.Uri;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.annotation.VisibleForTesting;
import java.time.Instant;
import java.util.Objects;
/**
* NOTE: A marker is indirectly (via it's {@link Position}) assigned to one {@link TrackPoint} via position.time.
*
* @author Leif Hendrik Wilden
* @author Rodrigo Damazio
*/
//TODO All data should be final; no default values.
public final class MarkerBuilder {
private Marker.Id id;
private String name;
private String description;
private String category;
private String icon;
private final Track.Id trackId;
//Some data might not be used.
private final Position position;
private Uri photoUrl = null;
@VisibleForTesting
public MarkerBuilder(@Nullable Track.Id trackId, @NonNull TrackPoint trackPoint) {
this.id = null;
this.trackId = trackId;
this.name = "";
this.description = "";
this.category = "";
this.icon = "";
this.photoUrl = null;
if (!trackPoint.hasLocation())
throw new RuntimeException("Marker requires a trackpoint with a location.");
this.position = trackPoint.getPosition();
}
public MarkerBuilder(@Nullable Track.Id trackId, @NonNull TrackPoint trackPoint, String name, String description, String category, String icon, Uri photoUrl) {
this(trackId, trackPoint);
this.name = name;
this.description = description;
this.category = category;
this.icon = icon;
this.photoUrl = photoUrl;
}
public MarkerBuilder(Marker marker) {
this.id = marker.id();
this.trackId = marker.trackId();
this.name = marker.name();
this.description = marker.description();
this.category = marker.category();
this.icon = marker.icon();
this.position = marker.position();
this.photoUrl = marker.photoUrl();
}
/**
* May be null if the Marker was not loaded from the database.
*/
@Nullable
public Marker.Id getId() {
return id;
}
//TODO Remove
@VisibleForTesting
@Deprecated
public void setId(Marker.Id id) {
this.id = id;
}
public void setName(String name) {
this.name = name;
}
public String getDescription() {
return description;
}
public void setDescription(String description) {
this.description = description;
}
public void setCategory(String category) {
this.category = category;
}
public void setIcon(String icon) {
this.icon = icon;
}
public Track.Id getTrackId() {
return trackId;
}
public Uri getPhotoUrl() {
return photoUrl;
}
public void setPhotoUrl(Uri photoUrl) {
this.photoUrl = photoUrl;
}
public boolean hasPhoto() {
return photoUrl != null;
}
public Marker getMarker() {
return new Marker(
id,
trackId,
name,
description,
category,
icon,
position,
photoUrl
);
}
}
@@ -39,13 +39,14 @@ public interface MarkerColumns extends BaseColumns {
String NAME = "name"; // marker name String NAME = "name"; // marker name
String DESCRIPTION = "description"; // marker description String DESCRIPTION = "description"; // marker description
String CATEGORY = "category"; // marker category String CATEGORY = "category"; // marker category
@Deprecated //TODO Remove: it is always: position
String ICON = "icon"; // marker icon String ICON = "icon"; // marker icon
String TRACKID = "trackid"; // track id String TRACKID = "trackid"; // track id
String LONGITUDE = "longitude"; // longitude String LONGITUDE = "longitude"; // longitude
String LATITUDE = "latitude"; // latitude String LATITUDE = "latitude"; // latitude
String TIME = "time"; // time String TIME = "time"; // time
String ALTITUDE = "elevation"; // altitude //TODO RENAME column String ALTITUDE = "elevation"; // altitude //TODO RENAME column
String ACCURACY = "accuracy"; // accuracy String HORIZONTAL_ACCURACY = "accuracy"; // accuracy
String BEARING = "bearing"; // bearing String BEARING = "bearing"; // bearing
String PHOTOURL = "photoUrl"; // url for the photo String PHOTOURL = "photoUrl"; // url for the photo
@@ -61,7 +62,7 @@ public interface MarkerColumns extends BaseColumns {
+ LATITUDE + " INTEGER, " + LATITUDE + " INTEGER, "
+ TIME + " INTEGER, " + TIME + " INTEGER, "
+ ALTITUDE + " FLOAT, " + ALTITUDE + " FLOAT, "
+ ACCURACY + " FLOAT, " + HORIZONTAL_ACCURACY + " FLOAT, "
+ BEARING + " FLOAT, " + BEARING + " FLOAT, "
+ PHOTOURL + " TEXT, " + PHOTOURL + " TEXT, "
+ "FOREIGN KEY (" + TRACKID + ") REFERENCES " + TracksColumns.TABLE_NAME + "(" + TracksColumns._ID + ") ON UPDATE CASCADE ON DELETE CASCADE" + "FOREIGN KEY (" + TRACKID + ") REFERENCES " + TracksColumns.TABLE_NAME + "(" + TracksColumns._ID + ") ON UPDATE CASCADE ON DELETE CASCADE"
@@ -250,14 +250,14 @@ public class GPXTrackExporter implements TrackExporter {
} }
private void writeMarker(ZoneOffset zoneOffset, Marker marker) { private void writeMarker(ZoneOffset zoneOffset, Marker marker) {
printWriter.println("<wpt " + formatLocation(marker.getPosition()) + ">"); printWriter.println("<wpt " + formatLocation(marker.position()) + ">");
if (marker.hasAltitude()) { if (marker.position().hasAltitude()) {
printWriter.println("<ele>" + ALTITUDE_FORMAT.format(marker.getAltitude().toM()) + "</ele>"); printWriter.println("<ele>" + ALTITUDE_FORMAT.format(marker.position().altitude().toM()) + "</ele>");
} }
printWriter.println("<time>" + StringUtils.formatDateTimeIso8601(marker.getTime(), zoneOffset) + "</time>"); printWriter.println("<time>" + StringUtils.formatDateTimeIso8601(marker.position().time(), zoneOffset) + "</time>");
printWriter.println("<name>" + StringUtils.formatCData(marker.getName()) + "</name>"); printWriter.println("<name>" + StringUtils.formatCData(marker.name()) + "</name>");
printWriter.println("<desc>" + StringUtils.formatCData(marker.getDescription()) + "</desc>"); printWriter.println("<desc>" + StringUtils.formatCData(marker.description()) + "</desc>");
printWriter.println("<type>" + StringUtils.formatCData(marker.getCategory()) + "</type>"); //TODO This is localized; may be better to export in English only. See #1608 printWriter.println("<type>" + StringUtils.formatCData(marker.category()) + "</type>"); //TODO This is localized; may be better to export in English only. See #1608
printWriter.println("</wpt>"); printWriter.println("</wpt>");
} }
@@ -266,11 +266,11 @@ public class KMLTrackExporter implements TrackExporter {
} }
private void writeMarker(Marker marker, ZoneOffset zoneOffset) { private void writeMarker(Marker marker, ZoneOffset zoneOffset) {
boolean existsPhoto = MarkerUtils.buildInternalPhotoFile(context, marker.getTrackId(), marker.getPhotoUrl()) != null; boolean existsPhoto = MarkerUtils.buildInternalPhotoFile(context, marker.trackId(), marker.photoUrl()) != null;
if (marker.hasPhoto() && exportPhotos && existsPhoto) { if (marker.photoUrl() != null && exportPhotos && existsPhoto) {
writePhotoOverlay(marker, zoneOffset); writePhotoOverlay(marker, zoneOffset);
} else { } else {
writePlacemark(marker.getName(), marker.getCategory(), marker.getDescription(), marker.getPosition(), marker.getTime(), zoneOffset); writePlacemark(marker.name(), marker.category(), marker.description(), marker.position(), marker.position().time(), zoneOffset);
} }
} }
@@ -428,17 +428,17 @@ public class KMLTrackExporter implements TrackExporter {
private void writePhotoOverlay(Marker marker, ZoneOffset zoneOffset) { private void writePhotoOverlay(Marker marker, ZoneOffset zoneOffset) {
printWriter.println("<PhotoOverlay>"); printWriter.println("<PhotoOverlay>");
printWriter.println("<name>" + StringUtils.formatCData(marker.getName()) + "</name>"); printWriter.println("<name>" + StringUtils.formatCData(marker.name()) + "</name>");
printWriter.println("<description>" + StringUtils.formatCData(marker.getDescription()) + "</description>"); printWriter.println("<description>" + StringUtils.formatCData(marker.description()) + "</description>");
printWriter.print("<Camera>"); printWriter.print("<Camera>");
printWriter.print("<longitude>" + marker.getPosition().longitude() + "</longitude>"); printWriter.print("<longitude>" + marker.position().longitude() + "</longitude>");
printWriter.print("<latitude>" + marker.getPosition().latitude() + "</latitude>"); printWriter.print("<latitude>" + marker.position().latitude() + "</latitude>");
printWriter.print("<altitude>20</altitude>"); printWriter.print("<altitude>20</altitude>");
printWriter.print("<tilt>90</tilt>"); printWriter.print("<tilt>90</tilt>");
printWriter.println("</Camera>"); printWriter.println("</Camera>");
printWriter.println("<TimeStamp><when>" + getTime(zoneOffset, marker.getTime()) + "</when></TimeStamp>"); printWriter.println("<TimeStamp><when>" + getTime(zoneOffset, marker.position().time()) + "</when></TimeStamp>");
printWriter.println("<styleUrl>#" + MARKER_STYLE + "</styleUrl>"); printWriter.println("<styleUrl>#" + MARKER_STYLE + "</styleUrl>");
writeTypeLocalized(marker.getCategory()); writeTypeLocalized(marker.category());
if (exportPhotos) { if (exportPhotos) {
printWriter.println("<Icon><href>" + KMZTrackExporter.buildKmzImageFilePath(marker) + "</href></Icon>"); printWriter.println("<Icon><href>" + KMZTrackExporter.buildKmzImageFilePath(marker) + "</href></Icon>");
@@ -452,7 +452,7 @@ public class KMLTrackExporter implements TrackExporter {
printWriter.print("<topFov>45</topFov>"); printWriter.print("<topFov>45</topFov>");
printWriter.println("</ViewVolume>"); printWriter.println("</ViewVolume>");
printWriter.println("<Point>"); printWriter.println("<Point>");
printWriter.println("<coordinates>" + getCoordinates(marker.getPosition(), ",") + "</coordinates>"); printWriter.println("<coordinates>" + getCoordinates(marker.position(), ",") + "</coordinates>");
printWriter.println("</Point>"); printWriter.println("</Point>");
printWriter.println("</PhotoOverlay>"); printWriter.println("</PhotoOverlay>");
} }
@@ -95,8 +95,8 @@ public class KMZTrackExporter implements TrackExporter {
if (Thread.interrupted()) throw new InterruptedException(); if (Thread.interrupted()) throw new InterruptedException();
Marker marker = markerIterator.next(); Marker marker = markerIterator.next();
if (marker.hasPhoto()) { if (marker.photoUrl() != null) {
Uri uriPhoto = marker.getPhotoUrl(); Uri uriPhoto = marker.photoUrl();
boolean existsPhoto = MarkerUtils.buildInternalPhotoFile(context, track.id(), uriPhoto) != null; boolean existsPhoto = MarkerUtils.buildInternalPhotoFile(context, track.id(), uriPhoto) != null;
if (existsPhoto) { if (existsPhoto) {
addImage(context, zipOutputStream, uriPhoto, marker); addImage(context, zipOutputStream, uriPhoto, marker);
@@ -135,8 +135,8 @@ public class KMZTrackExporter implements TrackExporter {
* Builds and returns the path for the image that will be saved inside KMZ_IMAGES_DIR for the marker. * Builds and returns the path for the image that will be saved inside KMZ_IMAGES_DIR for the marker.
*/ */
public static String buildKmzImageFilePath(Marker marker) { public static String buildKmzImageFilePath(Marker marker) {
String ext = FileUtils.getExtension(marker.getPhotoUrl()); String ext = FileUtils.getExtension(marker.photoUrl());
ext = ext == null ? "" : "." + ext; ext = ext == null ? "" : "." + ext;
return KMZ_IMAGES_DIR + File.separatorChar + FileUtils.sanitizeFileName(marker.getId().id() + ext); return KMZ_IMAGES_DIR + File.separatorChar + FileUtils.sanitizeFileName(marker.id().id() + ext);
} }
} }
@@ -34,6 +34,7 @@ import java.util.Locale;
import de.dennisguse.opentracks.data.models.Altitude; import de.dennisguse.opentracks.data.models.Altitude;
import de.dennisguse.opentracks.data.models.Distance; import de.dennisguse.opentracks.data.models.Distance;
import de.dennisguse.opentracks.data.models.Marker; import de.dennisguse.opentracks.data.models.Marker;
import de.dennisguse.opentracks.data.models.MarkerBuilder;
import de.dennisguse.opentracks.data.models.Position; import de.dennisguse.opentracks.data.models.Position;
import de.dennisguse.opentracks.data.models.Speed; import de.dennisguse.opentracks.data.models.Speed;
import de.dennisguse.opentracks.data.models.Track; import de.dennisguse.opentracks.data.models.Track;
@@ -429,22 +430,16 @@ public class GPXTrackImporter extends DefaultHandler implements XMLImporter.Trac
Log.w(TAG, "Marker with invalid coordinates ignored: " + trackPoint.getPosition()); Log.w(TAG, "Marker with invalid coordinates ignored: " + trackPoint.getPosition());
return; return;
} }
Marker marker = new Marker(null, trackPoint); markers.add(new Marker(
null,
if (name != null) { null,
marker.setName(name); name != null ? name : "",
} description != null ? description : "",
if (description != null) { markerType != null ? markerType : "",
marker.setDescription(description); null,
} trackPoint.getPosition(),
if (markerType != null) { photoUrl
marker.setCategory(markerType); ));
}
if (photoUrl != null) {
marker.setPhotoUrl(photoUrl);
}
markers.add(marker);
} }
private String createErrorMessage(String message) { private String createErrorMessage(String message) {
@@ -35,6 +35,7 @@ import java.util.Locale;
import de.dennisguse.opentracks.data.models.Distance; import de.dennisguse.opentracks.data.models.Distance;
import de.dennisguse.opentracks.data.models.Marker; import de.dennisguse.opentracks.data.models.Marker;
import de.dennisguse.opentracks.data.models.MarkerBuilder;
import de.dennisguse.opentracks.data.models.Position; import de.dennisguse.opentracks.data.models.Position;
import de.dennisguse.opentracks.data.models.Speed; import de.dennisguse.opentracks.data.models.Speed;
import de.dennisguse.opentracks.data.models.Track; import de.dennisguse.opentracks.data.models.Track;
@@ -263,12 +264,16 @@ public class KMLTrackImporter extends DefaultHandler implements XMLImporter.Trac
} }
position = position.with(whenList.get(0)); position = position.with(whenList.get(0));
Marker marker = new Marker(null, new TrackPoint(TrackPoint.Type.TRACKPOINT, position)); //TODO Creating marker without need markers.add(new Marker(
marker.setName(name != null ? name : ""); null,
marker.setDescription(description != null ? description : ""); null,
marker.setCategory(activityTypeLocalized != null ? activityTypeLocalized : ""); name != null ? name : "",
marker.setPhotoUrl(photoUrl); description != null ? description : "",
markers.add(marker); activityTypeLocalized != null ? activityTypeLocalized : "",
null,
position,
photoUrl
));
name = null; name = null;
description = null; description = null;
@@ -36,6 +36,7 @@ import java.util.zip.ZipInputStream;
import de.dennisguse.opentracks.R; import de.dennisguse.opentracks.R;
import de.dennisguse.opentracks.data.ContentProviderUtils; import de.dennisguse.opentracks.data.ContentProviderUtils;
import de.dennisguse.opentracks.data.models.Marker; import de.dennisguse.opentracks.data.models.Marker;
import de.dennisguse.opentracks.data.models.MarkerBuilder;
import de.dennisguse.opentracks.data.models.Track; import de.dennisguse.opentracks.data.models.Track;
import de.dennisguse.opentracks.util.AutoCloseableIterator; import de.dennisguse.opentracks.util.AutoCloseableIterator;
import de.dennisguse.opentracks.util.FileUtils; import de.dennisguse.opentracks.util.FileUtils;
@@ -191,8 +192,8 @@ public class KMZTrackImporter {
List<String> photosName = new ArrayList<>(); List<String> photosName = new ArrayList<>();
try (AutoCloseableIterator<Marker> markerIterator = new ContentProviderUtils(context).getMarkerIterator(trackId)) { try (AutoCloseableIterator<Marker> markerIterator = new ContentProviderUtils(context).getMarkerIterator(trackId)) {
markerIterator.forEachRemaining(marker -> { markerIterator.forEachRemaining(marker -> {
if (marker.hasPhoto()) { if (marker.photoUrl() != null) {
String photoUrl = Uri.decode(marker.getPhotoUrl().toString()); //TODO Why Uri.decode()? String photoUrl = Uri.decode(marker.photoUrl().toString()); //TODO Why Uri.decode()?
photosName.add(photoUrl.substring(photoUrl.lastIndexOf(File.separatorChar) + 1)); photosName.add(photoUrl.substring(photoUrl.lastIndexOf(File.separatorChar) + 1));
} }
}); });
@@ -23,6 +23,7 @@ import de.dennisguse.opentracks.data.ContentProviderUtils;
import de.dennisguse.opentracks.data.models.ActivityType; import de.dennisguse.opentracks.data.models.ActivityType;
import de.dennisguse.opentracks.data.models.Distance; import de.dennisguse.opentracks.data.models.Distance;
import de.dennisguse.opentracks.data.models.Marker; import de.dennisguse.opentracks.data.models.Marker;
import de.dennisguse.opentracks.data.models.MarkerBuilder;
import de.dennisguse.opentracks.data.models.Speed; import de.dennisguse.opentracks.data.models.Speed;
import de.dennisguse.opentracks.data.models.Track; import de.dennisguse.opentracks.data.models.Track;
import de.dennisguse.opentracks.data.models.TrackBuilder; import de.dennisguse.opentracks.data.models.TrackBuilder;
@@ -58,7 +59,7 @@ public class TrackImporter {
// Current track // Current track
private TrackData trackData; private TrackData trackData;
private final List<TrackPoint> trackPoints = new LinkedList<>(); private final List<TrackPoint> trackPoints = new LinkedList<>();
private final List<Marker> markers = new LinkedList<>(); private final List<MarkerBuilder> markers = new LinkedList<>();
public TrackImporter(Context context, ContentProviderUtils contentProviderUtils, Distance maxRecordingDistance, boolean preventReimport) { public TrackImporter(Context context, ContentProviderUtils contentProviderUtils, Distance maxRecordingDistance, boolean preventReimport) {
this.context = context; this.context = context;
@@ -86,7 +87,7 @@ public class TrackImporter {
} }
void addMarkers(List<Marker> markers) { void addMarkers(List<Marker> markers) {
this.markers.addAll(markers); this.markers.addAll(markers.stream().map(MarkerBuilder::new).toList());
} }
void setTrackData(Context context, String name, String uuid, String description, String activityTypeLocalized, String activityTypeId, @Nullable ZoneOffset zoneOffset) { void setTrackData(Context context, String name, String uuid, String description, String activityTypeLocalized, String activityTypeId, @Nullable ZoneOffset zoneOffset) {
@@ -165,10 +166,8 @@ public class TrackImporter {
// Store Markers // Store Markers
updateMarkers(trackId); updateMarkers(trackId);
for (Marker marker : markers)
marker.setTrackId(trackId); //TODO Should happen in bulkInsertMarkers
contentProviderUtils.bulkInsertMarkers(markers, trackId); contentProviderUtils.bulkInsertMarkers(markers.stream().map(it -> it.getMarker()).toList(), trackId);
//Clear up. //Clear up.
trackPoints.clear(); trackPoints.clear();
@@ -37,6 +37,7 @@ import java.time.Duration;
import de.dennisguse.opentracks.data.ContentProviderUtils; import de.dennisguse.opentracks.data.ContentProviderUtils;
import de.dennisguse.opentracks.data.models.Distance; import de.dennisguse.opentracks.data.models.Distance;
import de.dennisguse.opentracks.data.models.Marker; import de.dennisguse.opentracks.data.models.Marker;
import de.dennisguse.opentracks.data.models.MarkerBuilder;
import de.dennisguse.opentracks.data.models.Track; import de.dennisguse.opentracks.data.models.Track;
import de.dennisguse.opentracks.data.models.TrackPoint; import de.dennisguse.opentracks.data.models.TrackPoint;
import de.dennisguse.opentracks.sensors.GpsStatusValue; import de.dennisguse.opentracks.sensors.GpsStatusValue;
@@ -249,7 +250,16 @@ public class TrackRecordingService extends Service implements TrackPointCreator.
if (trackPoint == null) { if (trackPoint == null) {
return null; return null;
} }
Marker marker = new Marker(recordingStatus.trackId(), trackPoint); Marker marker = new Marker(
null,
recordingStatus.trackId(),
"",
"",
"",
"",
trackPoint.getPosition(),
null
);
return new ContentProviderUtils(this).insertMarker(marker); return new ContentProviderUtils(this).insertMarker(marker);
} }
@@ -94,14 +94,14 @@ public class ShareUtils {
Log.e(TAG, "MarkerId " + markerId.id() + " could not be resolved."); Log.e(TAG, "MarkerId " + markerId.id() + " could not be resolved.");
continue; continue;
} }
if (marker.getPhotoUrl() == null) { if (marker.photoUrl() == null) {
Log.e(TAG, "MarkerId " + markerId.id() + " has no picture."); Log.e(TAG, "MarkerId " + markerId.id() + " has no picture.");
continue; continue;
} }
mime = context.getContentResolver().getType(marker.getPhotoUrl()); mime = context.getContentResolver().getType(marker.photoUrl());
uris.add(marker.getPhotoUrl()); uris.add(marker.photoUrl());
} }
if (uris.isEmpty()) { if (uris.isEmpty()) {
@@ -67,14 +67,14 @@ public class MarkerDetailActivity extends AbstractActivity implements DeleteMark
ContentProviderUtils contentProviderUtils = new ContentProviderUtils(this); ContentProviderUtils contentProviderUtils = new ContentProviderUtils(this);
Marker marker = contentProviderUtils.getMarker(markerId); Marker marker = contentProviderUtils.getMarker(markerId);
markerAutoCloseableIterator = contentProviderUtils.getMarkerIterator(marker.getTrackId()); markerAutoCloseableIterator = contentProviderUtils.getMarkerIterator(marker.trackId());
if (markerAutoCloseableIterator == null) { if (markerAutoCloseableIterator == null) {
finish(); finish();
} }
int markerIndex = -1; int markerIndex = -1;
while (markerAutoCloseableIterator.hasNext()) { while (markerAutoCloseableIterator.hasNext()) {
if (markerId.equals(markerAutoCloseableIterator.next().getId())) { if (markerId.equals(markerAutoCloseableIterator.next().id())) {
markerIndex = markerAutoCloseableIterator.getPosition(); markerIndex = markerAutoCloseableIterator.getPosition();
break; break;
} }
@@ -124,7 +124,7 @@ public class MarkerDetailActivity extends AbstractActivity implements DeleteMark
@NonNull @NonNull
public Fragment createFragment(int position) { public Fragment createFragment(int position) {
markerAutoCloseableIterator.moveToPosition(position); markerAutoCloseableIterator.moveToPosition(position);
return MarkerDetailFragment.newInstance(markerAutoCloseableIterator.next().getId()); return MarkerDetailFragment.newInstance(markerAutoCloseableIterator.next().id());
} }
@Override @Override
@@ -135,7 +135,7 @@ public class MarkerDetailActivity extends AbstractActivity implements DeleteMark
@Nullable @Nullable
public CharSequence getPageTitle(int position) { public CharSequence getPageTitle(int position) {
markerAutoCloseableIterator.moveToPosition(position); markerAutoCloseableIterator.moveToPosition(position);
return getString(R.string.marker_detail_title, position + 1, getItemCount(), markerAutoCloseableIterator.next().getName()); return getString(R.string.marker_detail_title, position + 1, getItemCount(), markerAutoCloseableIterator.next().name());
} }
} }
} }
@@ -111,8 +111,8 @@ public class MarkerDetailFragment extends Fragment {
} }
if (item.getItemId() == R.id.marker_detail_share) { if (item.getItemId() == R.id.marker_detail_share) {
if (marker.hasPhoto()) { if (marker.photoUrl() != null) {
Intent intent = ShareUtils.newShareFileIntent(getContext(), marker.getId()); Intent intent = ShareUtils.newShareFileIntent(getContext(), marker.id());
intent = Intent.createChooser(intent, null); intent = Intent.createChooser(intent, null);
startActivity(intent); startActivity(intent);
} }
@@ -180,15 +180,15 @@ public class MarkerDetailFragment extends Fragment {
} }
private void updateUi() { private void updateUi() {
if (marker.hasPhoto()) { if (marker.photoUrl() != null) {
viewBinding.markerDetailMarkerPhoto.setImageURI(marker.getPhotoUrl()); viewBinding.markerDetailMarkerPhoto.setImageURI(marker.photoUrl());
} else { } else {
viewBinding.markerDetailMarkerPhoto.setImageDrawable(MarkerUtils.getDefaultPhoto(getContext())); viewBinding.markerDetailMarkerPhoto.setImageDrawable(MarkerUtils.getDefaultPhoto(getContext()));
} }
viewBinding.markerDetailMarkerCategory.setText(StringUtils.getCategory(marker.getCategory())); viewBinding.markerDetailMarkerCategory.setText(StringUtils.getCategory(marker.category()));
viewBinding.markerDetailMarkerDescription.setText(marker.getDescription()); viewBinding.markerDetailMarkerDescription.setText(marker.description());
viewBinding.markerDetailMarkerLocation.setText(StringUtils.formatCoordinate(getContext(), marker.getPosition())); viewBinding.markerDetailMarkerLocation.setText(StringUtils.formatCoordinate(getContext(), marker.position()));
} }
} }
@@ -47,6 +47,7 @@ import java.time.Instant;
import de.dennisguse.opentracks.AbstractActivity; import de.dennisguse.opentracks.AbstractActivity;
import de.dennisguse.opentracks.R; import de.dennisguse.opentracks.R;
import de.dennisguse.opentracks.data.models.Marker; import de.dennisguse.opentracks.data.models.Marker;
import de.dennisguse.opentracks.data.models.MarkerBuilder;
import de.dennisguse.opentracks.data.models.Position; import de.dennisguse.opentracks.data.models.Position;
import de.dennisguse.opentracks.data.models.Track; import de.dennisguse.opentracks.data.models.Track;
import de.dennisguse.opentracks.data.models.TrackPoint; import de.dennisguse.opentracks.data.models.TrackPoint;
@@ -181,11 +182,11 @@ public class MarkerEditActivity extends AbstractActivity {
private void loadMarkerData(Marker data) { private void loadMarkerData(Marker data) {
marker = data; marker = data;
viewBinding.markerEditName.setText(marker.getName()); viewBinding.markerEditName.setText(marker.name());
viewBinding.markerEditMarkerType.setText(marker.getCategory()); viewBinding.markerEditMarkerType.setText(marker.category());
viewBinding.markerEditDescription.setText(marker.getDescription()); viewBinding.markerEditDescription.setText(marker.description());
if (marker.hasPhoto()) { if (marker.photoUrl() != null) {
setMarkerImageView(marker.getPhotoUrl()); setMarkerImageView(marker.photoUrl());
} else { } else {
viewBinding.markerEditPhoto.setImageDrawable(null); viewBinding.markerEditPhoto.setImageDrawable(null);
} }
@@ -247,7 +248,7 @@ public class MarkerEditActivity extends AbstractActivity {
* If a photo is set then one's options are shown, otherwise another ones are shown. * If a photo is set then one's options are shown, otherwise another ones are shown.
*/ */
private void hideAndShowOptions() { private void hideAndShowOptions() {
boolean isPhotoSet = (marker != null && marker.hasPhoto()); boolean isPhotoSet = (marker != null && marker.photoUrl() != null);
if (insertPhotoMenuItem != null && insertGalleryImgMenuItem != null) { if (insertPhotoMenuItem != null && insertGalleryImgMenuItem != null) {
insertPhotoMenuItem.setVisible(!isPhotoSet); insertPhotoMenuItem.setVisible(!isPhotoSet);
insertGalleryImgMenuItem.setVisible(!isPhotoSet); insertGalleryImgMenuItem.setVisible(!isPhotoSet);
@@ -259,7 +260,7 @@ public class MarkerEditActivity extends AbstractActivity {
* Returns the trackId; either from track or marker. * Returns the trackId; either from track or marker.
*/ */
private Track.Id getTrackId() { private Track.Id getTrackId() {
return trackId == null ? marker.getTrackId() : trackId; return trackId == null ? marker.trackId() : trackId;
} }
private void setMarkerImageView(@NonNull Uri uri) { private void setMarkerImageView(@NonNull Uri uri) {
@@ -20,6 +20,7 @@ import java.util.NoSuchElementException;
import de.dennisguse.opentracks.R; import de.dennisguse.opentracks.R;
import de.dennisguse.opentracks.data.ContentProviderUtils; import de.dennisguse.opentracks.data.ContentProviderUtils;
import de.dennisguse.opentracks.data.models.Marker; import de.dennisguse.opentracks.data.models.Marker;
import de.dennisguse.opentracks.data.models.MarkerBuilder;
import de.dennisguse.opentracks.data.models.Track; import de.dennisguse.opentracks.data.models.Track;
import de.dennisguse.opentracks.data.models.TrackPoint; import de.dennisguse.opentracks.data.models.TrackPoint;
import de.dennisguse.opentracks.util.FileUtils; import de.dennisguse.opentracks.util.FileUtils;
@@ -40,8 +41,8 @@ public class MarkerEditViewModel extends AndroidViewModel {
markerData = new MutableLiveData<>(); markerData = new MutableLiveData<>();
Marker marker = new ContentProviderUtils(getApplication()).getMarker(markerId); Marker marker = new ContentProviderUtils(getApplication()).getMarker(markerId);
if (marker.hasPhoto()) { if (marker.photoUrl() != null) {
photoOriginalUri = marker.getPhotoUrl(); photoOriginalUri = marker.photoUrl();
} }
markerData.postValue(marker); markerData.postValue(marker);
@@ -63,20 +64,20 @@ public class MarkerEditViewModel extends AndroidViewModel {
return; return;
} }
File photoFile = MarkerUtils.getPhotoFileIfExists(getApplication(), markerData.getValue().getTrackId(), photoUri); File photoFile = MarkerUtils.getPhotoFileIfExists(getApplication(), markerData.getValue().trackId(), photoUri);
if (photoFile != null) { if (photoFile != null) {
FileUtils.deleteDirectoryRecurse(photoFile); FileUtils.deleteDirectoryRecurse(photoFile);
} }
} }
private void deletePhoto(Marker marker) { private void deletePhoto(Marker marker) {
if (marker.hasPhoto()) { if (marker.photoUrl() != null) {
deletePhoto(marker.getPhotoUrl()); deletePhoto(marker.photoUrl());
} }
} }
public void onPhotoDelete(String name, String category, String description) { public void onPhotoDelete(String name, String category, String description) {
Marker marker = getMarker(); MarkerBuilder marker = new MarkerBuilder(getMarker());
if (marker.hasPhoto()) { if (marker.hasPhoto()) {
if (!marker.getPhotoUrl().equals(photoOriginalUri)) { if (!marker.getPhotoUrl().equals(photoOriginalUri)) {
deletePhoto(marker.getPhotoUrl()); deletePhoto(marker.getPhotoUrl());
@@ -85,21 +86,21 @@ public class MarkerEditViewModel extends AndroidViewModel {
marker.setName(name); marker.setName(name);
marker.setCategory(category); marker.setCategory(category);
marker.setDescription(description); marker.setDescription(description);
markerData.postValue(marker); markerData.postValue(marker.getMarker());
} }
} }
public void onNewCameraPhoto(@NonNull Uri photoUri, String name, String category, String description) { public void onNewCameraPhoto(@NonNull Uri photoUri, String name, String category, String description) {
Marker marker = getMarker(); MarkerBuilder marker = new MarkerBuilder(getMarker());
marker.setPhotoUrl(photoUri); marker.setPhotoUrl(photoUri);
marker.setName(name); marker.setName(name);
marker.setCategory(category); marker.setCategory(category);
marker.setDescription(description); marker.setDescription(description);
markerData.postValue(marker); markerData.postValue(marker.getMarker());
} }
public void onNewGalleryPhoto(@NonNull Uri srcUri, String name, String category, String description) { public void onNewGalleryPhoto(@NonNull Uri srcUri, String name, String category, String description) {
Marker marker = getMarker(); MarkerBuilder marker = new MarkerBuilder(getMarker());
try (ParcelFileDescriptor parcelFd = getApplication().getContentResolver().openFileDescriptor(srcUri, "r")) { try (ParcelFileDescriptor parcelFd = getApplication().getContentResolver().openFileDescriptor(srcUri, "r")) {
FileDescriptor srcFd = parcelFd.getFileDescriptor(); FileDescriptor srcFd = parcelFd.getFileDescriptor();
@@ -111,7 +112,8 @@ public class MarkerEditViewModel extends AndroidViewModel {
marker.setName(name); marker.setName(name);
marker.setCategory(category); marker.setCategory(category);
marker.setDescription(description); marker.setDescription(description);
markerData.postValue(marker);
markerData.postValue(marker.getMarker());
} catch (IOException e) { } catch (IOException e) {
Log.e(TAG, e.getMessage()); Log.e(TAG, e.getMessage());
Toast.makeText(getApplication(), R.string.marker_add_canceled, Toast.LENGTH_LONG).show(); Toast.makeText(getApplication(), R.string.marker_add_canceled, Toast.LENGTH_LONG).show();
@@ -119,15 +121,15 @@ public class MarkerEditViewModel extends AndroidViewModel {
} }
public void onDone(String name, String category, String description) { public void onDone(String name, String category, String description) {
Marker marker = getMarker(); MarkerBuilder marker = new MarkerBuilder(getMarker());
marker.setName(name); marker.setName(name);
marker.setCategory(category); marker.setCategory(category);
marker.setDescription(description); marker.setDescription(description);
if (marker.getId() == null) { if (marker.getId() == null) {
new ContentProviderUtils(getApplication()).insertMarker(marker); new ContentProviderUtils(getApplication()).insertMarker(marker.getMarker());
} else { } else {
new ContentProviderUtils(getApplication()).updateMarker(getApplication(), marker); new ContentProviderUtils(getApplication()).updateMarker(getApplication(), marker.getMarker());
} }
if (photoOriginalUri != null && (!marker.hasPhoto() || !photoOriginalUri.equals(marker.getPhotoUrl()))) { if (photoOriginalUri != null && (!marker.hasPhoto() || !photoOriginalUri.equals(marker.getPhotoUrl()))) {
@@ -143,7 +145,15 @@ public class MarkerEditViewModel extends AndroidViewModel {
String name = getApplication().getString(R.string.marker_name_format, nextMarkerNumber + 1); String name = getApplication().getString(R.string.marker_name_format, nextMarkerNumber + 1);
String icon = getApplication().getString(R.string.marker_icon_url); String icon = getApplication().getString(R.string.marker_icon_url);
Marker marker = new Marker(trackId, trackPoint, name, "", "", icon, null); Marker marker = new Marker(
null,
trackId,
name,
"",
icon,
"",
trackPoint.getPosition(),
null);
if (markerData == null) { if (markerData == null) {
markerData = new MutableLiveData<>(); markerData = new MutableLiveData<>();
@@ -158,7 +168,7 @@ public class MarkerEditViewModel extends AndroidViewModel {
// it's new marker -> clean all photos. // it's new marker -> clean all photos.
deletePhoto(marker); deletePhoto(marker);
deletePhoto(photoOriginalUri); deletePhoto(photoOriginalUri);
} else if (photoOriginalUri == null || (marker.hasPhoto() && !marker.getPhotoUrl().equals(photoOriginalUri))) { } else if (photoOriginalUri == null || (marker.photoUrl() != null && !marker.photoUrl().equals(photoOriginalUri))) {
// it's an edit marker -> delete photo if it was empty or it was changed (leaving the original in that case). // it's an edit marker -> delete photo if it was empty or it was changed (leaving the original in that case).
deletePhoto(marker); deletePhoto(marker);
} }
@@ -137,7 +137,7 @@ public class MarkerListAdapter extends RecyclerView.Adapter<RecyclerView.ViewHol
public void setAllSelected(boolean isSelected) { public void setAllSelected(boolean isSelected) {
if (isSelected) { if (isSelected) {
for (Marker marker : markers) { for (Marker marker : markers) {
selection.put((int) marker.getId().id(), true); selection.put((int) marker.id().id(), true);
} }
} else { } else {
selection.clear(); selection.clear();
@@ -179,24 +179,24 @@ public class MarkerListAdapter extends RecyclerView.Adapter<RecyclerView.ViewHol
public void bind(Marker marker) { public void bind(Marker marker) {
this.marker = marker; this.marker = marker;
viewBinding.markerListItemPhoto.setVisibility(marker.hasPhoto() ? View.VISIBLE : View.GONE); viewBinding.markerListItemPhoto.setVisibility(marker.photoUrl() != null ? View.VISIBLE : View.GONE);
if (marker.hasPhoto()) { if (marker.photoUrl() != null) {
int height = ThemeUtils.getPhotoHeight(context); int height = ThemeUtils.getPhotoHeight(context);
ViewGroup.LayoutParams params = viewBinding.markerListItemPhoto.getLayoutParams(); ViewGroup.LayoutParams params = viewBinding.markerListItemPhoto.getLayoutParams();
params.height = height; params.height = height;
viewBinding.markerListItemPhoto.setLayoutParams(params); viewBinding.markerListItemPhoto.setLayoutParams(params);
asyncLoadPhoto(viewBinding.markerListItemPhoto, marker.getPhotoUrl(), marker.getId()); asyncLoadPhoto(viewBinding.markerListItemPhoto, marker.photoUrl(), marker.id());
} }
viewBinding.markerListItemName.setText(marker.getName()); viewBinding.markerListItemName.setText(marker.name());
{ {
ZoneOffset timeZone = new ContentProviderUtils(context).getTrack(marker.getTrackId()).zoneOffset(); ZoneOffset timeZone = new ContentProviderUtils(context).getTrack(marker.trackId()).zoneOffset();
ListItemUtils.setDateAndTime(context, viewBinding.markerListItemDate, viewBinding.markerListItemTime, marker.getTime(), timeZone); ListItemUtils.setDateAndTime(context, viewBinding.markerListItemDate, viewBinding.markerListItemTime, marker.position().time(), timeZone);
} }
String categoryDescription = StringUtils.getCategoryDescription(marker.getCategory(), marker.getDescription()); String categoryDescription = StringUtils.getCategoryDescription(marker.category(), marker.description());
viewBinding.markerListItemTimeDistance.setText(categoryDescription); viewBinding.markerListItemTimeDistance.setText(categoryDescription);
setSelected(selection.get((int) getId())); setSelected(selection.get((int) getId()));
@@ -208,7 +208,7 @@ public class MarkerListAdapter extends RecyclerView.Adapter<RecyclerView.ViewHol
} }
public long getId() { public long getId() {
return marker.getId().id(); return marker.id().id();
} }
@Override @Override
@@ -218,7 +218,7 @@ public class MarkerListAdapter extends RecyclerView.Adapter<RecyclerView.ViewHol
actionMode.invalidate(); actionMode.invalidate();
} else { } else {
Intent intent = IntentUtils.newIntent(context, MarkerDetailActivity.class) Intent intent = IntentUtils.newIntent(context, MarkerDetailActivity.class)
.putExtra(MarkerDetailActivity.EXTRA_MARKER_ID, marker.getId()); .putExtra(MarkerDetailActivity.EXTRA_MARKER_ID, marker.id());
context.startActivity(intent); context.startActivity(intent);
} }
} }
@@ -20,11 +20,13 @@ import android.content.Context;
import android.content.Intent; import android.content.Intent;
import android.content.UriPermission; import android.content.UriPermission;
import android.net.Uri; import android.net.Uri;
import android.text.TextUtils;
import android.util.Log; import android.util.Log;
import androidx.documentfile.provider.DocumentFile; import androidx.documentfile.provider.DocumentFile;
import de.dennisguse.opentracks.data.models.Marker; import de.dennisguse.opentracks.data.models.Marker;
import de.dennisguse.opentracks.data.models.MarkerBuilder;
/** /**
* Utilities for creating intents. * Utilities for creating intents.
@@ -55,9 +57,9 @@ public class IntentUtils {
*/ */
public static void showCoordinateOnMap(Context context, Marker marker) { public static void showCoordinateOnMap(Context context, Marker marker) {
//SEE https://developer.android.com/guide/components/intents-common.html#Maps //SEE https://developer.android.com/guide/components/intents-common.html#Maps
String uri = "geo:0,0?q=" + marker.getPosition().latitude() + "," + marker.getPosition().longitude(); String uri = "geo:0,0?q=" + marker.position().latitude() + "," + marker.position().longitude();
if (marker.getName() != null && !marker.getName().isEmpty()) { if (!TextUtils.isEmpty(marker.name())) {
uri += "(" + marker.getName() + ")"; uri += "(" + marker.name() + ")";
} }
Intent intent = new Intent(Intent.ACTION_VIEW); Intent intent = new Intent(Intent.ACTION_VIEW);
intent.setData(Uri.parse(uri)); intent.setData(Uri.parse(uri));