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.Statistics;
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.ui.markers.MarkerUtils;
import de.dennisguse.opentracks.util.AutoCloseableIterator;
@@ -92,9 +91,9 @@ public class TestDataUtil {
);
List<Marker> markers = List.of(
new Marker(trackId, trackPoints.get(1), "Marker 1", "Marker description 1", "Marker category 3", "", null),
new Marker(trackId, trackPoints.get(4), "Marker 2", "Marker description 2", "Marker category 3", "", null),
new Marker(trackId, trackPoints.get(5), "Marker 3", "Marker description 3", "Marker category 3", "", null)
new Marker(null, trackId, "Marker 1", "Marker description 1", "Marker category 3", "", trackPoints.get(1).getPosition(),null),
new Marker(null, trackId, "Marker 2", "Marker description 2", "Marker category 3", "", trackPoints.get(4).getPosition(), null),
new Marker(null, trackId, "Marker 3", "Marker description 3", "Marker category 3", "", trackPoints.get(5).getPosition(), null)
);
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 {
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));
dstFile.createNewFile();
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) {
@@ -57,6 +57,7 @@ import de.dennisguse.opentracks.data.models.AltitudeExtremities;
import de.dennisguse.opentracks.data.models.AltitudeGainLoss;
import de.dennisguse.opentracks.data.models.Distance;
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.Speed;
import de.dennisguse.opentracks.data.models.Statistics;
@@ -232,8 +233,16 @@ public class CustomContentProviderUtilsTest {
Track.Id trackId = new Track.Id(System.currentTimeMillis());
TestDataUtil.createTrackAndInsert(contentProviderUtils, trackId, 10);
Marker marker = new Marker(trackId, getLastValidTrackPoint(trackId));
contentProviderUtils.insertMarker(marker);
contentProviderUtils.insertMarker(new Marker(
null,
trackId,
"",
"",
"",
"",
getLastValidTrackPoint(trackId).getPosition(),
null
));
assertCount(1, 10, 1);
@@ -258,7 +267,7 @@ public class CustomContentProviderUtilsTest {
contentProviderUtils.insertMarker(marker);
assertCount(1, 10, 1);
assertTrue(marker.hasPhoto());
assertNotNull(marker.photoUrl());
File dir = FileUtils.getPhotoDir(context, trackId);
assertTrue(dir.isDirectory());
assertEquals(1, dir.list().length);
@@ -288,8 +297,16 @@ public class CustomContentProviderUtilsTest {
TestDataUtil.createTrackAndInsert(contentProviderUtils, trackId2, 10);
TestDataUtil.createTrackAndInsert(contentProviderUtils, trackId3, 10);
Marker marker = new Marker(trackId1, getLastValidTrackPoint(trackId2));
contentProviderUtils.insertMarker(marker);
contentProviderUtils.insertMarker(new Marker(
null,
trackId1,
"",
"",
"",
"",
getLastValidTrackPoint(trackId1).getPosition(),
null
));
assertCount(3, 30, 1);
@@ -328,7 +345,7 @@ public class CustomContentProviderUtilsTest {
// Check.
assertCount(3, 30, 2);
assertTrue(marker1.hasPhoto());
assertNotNull(marker1.photoUrl());
assertTrue(dir1.isDirectory());
assertEquals(1, dir1.list().length);
assertTrue(dir1.exists());
@@ -407,9 +424,6 @@ public class CustomContentProviderUtilsTest {
assertEquals(nameNew, contentProviderUtils.getTrack(trackId).name());
}
/**
* Tests the method {@link ContentProviderUtils#createContentValues(Marker)}.
*/
@Test
public void testCreateContentValues_marker() {
Track.Id trackId = new Track.Id(System.currentTimeMillis());
@@ -432,15 +446,15 @@ public class CustomContentProviderUtilsTest {
));
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);
contentProviderUtils.insertMarker(marker);
contentProviderUtils.insertMarker(marker.getMarker());
ContentProviderUtils contentProviderUtils = new ContentProviderUtils(contentResolverMock);
Marker.Id markerId = new Marker.Id(System.currentTimeMillis());
marker.setId(markerId);
ContentValues contentValues = contentProviderUtils.createContentValues(marker);
ContentValues contentValues = contentProviderUtils.createContentValues(marker.getMarker(), trackId);
assertEquals(markerId.id(), contentValues.get(MarkerColumns._ID));
assertEquals((int) (TestDataUtil.INITIAL_LONGITUDE * 1000000), contentValues.get(MarkerColumns.LONGITUDE));
assertEquals(TEST_DESC, contentValues.get(MarkerColumns.DESCRIPTION));
@@ -457,15 +471,19 @@ public class CustomContentProviderUtilsTest {
TestDataUtil.createTrackAndInsert(contentProviderUtils, trackId, 10);
// Insert at first.
Marker marker1 = new Marker(trackId, getLastValidTrackPoint(trackId));
marker1.setDescription(TEST_DESC);
contentProviderUtils.insertMarker(marker1);
Marker.Id marker1Id = contentProviderUtils.insertMarker(new Marker(
null,
trackId,
"",
TEST_DESC,
"",
"",
getLastValidTrackPoint(trackId).getPosition(),
null
));
// Check insert was done.
assertEquals(1, TestDataUtil.getMarkers(contentProviderUtils, trackId).size());
// Get marker id that needs to delete.
Marker.Id marker1Id = contentProviderUtils.insertMarker(marker1);
assertEquals(1, TestDataUtil.getMarkers(contentProviderUtils, trackId).size());
// Delete
contentProviderUtils.deleteMarker(context, marker1Id);
@@ -495,7 +513,7 @@ public class CustomContentProviderUtilsTest {
Marker.Id marker1Id = contentProviderUtils.insertMarker(marker1);
// Check marker has photo and it's in the external storage.
assertTrue(marker1.hasPhoto());
assertNotNull(marker1.photoUrl());
File dir = FileUtils.getPhotoDir(context, trackId);
assertTrue(dir.isDirectory());
assertEquals(1, dir.list().length);
@@ -534,20 +552,33 @@ public class CustomContentProviderUtilsTest {
// TestDataUtil.insertTrackWithLocations(contentProviderUtils, track);
// Insert at first.
Marker marker1 = new Marker(trackId, getLastValidTrackPoint(trackId));
marker1.setDescription(MOCK_DESC);
Marker.Id marker1Id = contentProviderUtils.insertMarker(marker1);
Marker marker2 = new Marker(trackId, getLastValidTrackPoint(trackId));
marker2.setDescription(MOCK_DESC);
Marker.Id marker2Id = contentProviderUtils.insertMarker(marker2);
Marker.Id marker1Id = contentProviderUtils.insertMarker(new Marker(
null,
trackId,
"",
MOCK_DESC,
"",
"",
getLastValidTrackPoint(trackId).getPosition(),
null
));
Marker.Id marker2Id = contentProviderUtils.insertMarker(new Marker(
null,
trackId,
"",
MOCK_DESC,
"",
"",
getLastValidTrackPoint(trackId).getPosition(),
null
));
// Delete
assertNotNull(contentProviderUtils.getMarker(marker1Id));
contentProviderUtils.deleteMarker(context, 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());
TestDataUtil.createTrackAndInsert(contentProviderUtils, trackId, 10);
Marker marker1 = new Marker(trackId, getLastValidTrackPoint(trackId));
Marker marker2 = new Marker(trackId, getLastValidTrackPoint(trackId));
Marker marker3 = new Marker(trackId, getLastValidTrackPoint(trackId));
Marker marker4 = new Marker(trackId, getLastValidTrackPoint(trackId));
contentProviderUtils.insertMarker(marker1);
contentProviderUtils.insertMarker(marker2);
contentProviderUtils.insertMarker(marker3);
contentProviderUtils.insertMarker(marker4);
for (int i = 0; i < 4; i++) {
contentProviderUtils.insertMarker(new Marker(
null,
trackId,
"",
MOCK_DESC,
"",
"",
getLastValidTrackPoint(trackId).getPosition(),
null
));
}
assertEquals(Integer.valueOf(4), contentProviderUtils.getNextMarkerNumber(trackId));
}
@@ -579,11 +614,19 @@ public class CustomContentProviderUtilsTest {
Track.Id trackId = new Track.Id(System.currentTimeMillis());
TestDataUtil.createTrackAndInsert(contentProviderUtils, trackId, 10);
Marker marker = new Marker(trackId, getLastValidTrackPoint(trackId));
marker.setDescription(TEST_DESC);
Marker.Id markerId = contentProviderUtils.insertMarker(marker);
Marker.Id markerId = contentProviderUtils.insertMarker(
new 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);
// Insert at first.
Marker marker = new Marker(trackId, getLastValidTrackPoint(trackId));
marker.setDescription(TEST_DESC);
Marker.Id markerId = contentProviderUtils.insertMarker(marker);
Marker.Id markerId = contentProviderUtils.insertMarker(
new Marker(
null,
trackId,
"",
TEST_DESC,
"",
"",
getLastValidTrackPoint(trackId).getPosition(),
null
));
// Update
marker = contentProviderUtils.getMarker(markerId);
marker.setDescription(TEST_DESC_NEW);
contentProviderUtils.updateMarker(context, marker);
contentProviderUtils.updateMarker(context, new Marker(
markerId,
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.
TrackPoint trackPoint = getLastValidTrackPoint(trackId);
Marker marker = TestDataUtil.createMarkerWithPhoto(context, trackId, trackPoint);
marker.setDescription(TEST_DESC);
Marker.Id markerId = contentProviderUtils.insertMarker(marker);
Marker.Id markerId = contentProviderUtils.insertMarker(TestDataUtil.createMarkerWithPhoto(context, trackId, trackPoint, TEST_DESC));
File dir = new File(FileUtils.getPhotoDir(context), "" + trackId.id());
assertTrue(dir.exists());
@@ -629,14 +685,13 @@ public class CustomContentProviderUtilsTest {
assertEquals(1, dir.list().length);
// Update
marker = contentProviderUtils.getMarker(markerId);
MarkerBuilder marker = new MarkerBuilder(contentProviderUtils.getMarker(markerId));
marker.setName(TEST_NAME_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_DESC_NEW, contentProviderUtils.getMarker(markerId).getDescription());
assertTrue(marker.hasPhoto());
assertEquals(TEST_NAME_NEW, contentProviderUtils.getMarker(markerId).name());
assertEquals(TEST_DESC_NEW, contentProviderUtils.getMarker(markerId).description());
assertTrue(dir.exists());
assertTrue(dir.isDirectory());
assertEquals(1, dir.list().length);
@@ -654,8 +709,7 @@ public class CustomContentProviderUtilsTest {
// Insert at first.
TrackPoint trackPoint = getLastValidTrackPoint(trackId);
Marker marker = TestDataUtil.createMarkerWithPhoto(context, trackId, trackPoint);
marker.setDescription(TEST_DESC);
Marker marker = TestDataUtil.createMarkerWithPhoto(context, trackId, trackPoint, TEST_DESC);
Marker.Id markerId = contentProviderUtils.insertMarker(marker);
File dir = new File(FileUtils.getPhotoDir(context), "" + trackId.id());
@@ -664,15 +718,14 @@ public class CustomContentProviderUtilsTest {
assertEquals(1, dir.list().length);
// Update
marker = contentProviderUtils.getMarker(markerId);
marker.setName(TEST_NAME_NEW);
marker.setDescription(TEST_DESC_NEW);
marker.setPhotoUrl(null);
contentProviderUtils.updateMarker(context, marker);
MarkerBuilder markerBuilder = new MarkerBuilder(contentProviderUtils.getMarker(markerId));
markerBuilder.setName(TEST_NAME_NEW);
markerBuilder.setDescription(TEST_DESC_NEW);
markerBuilder.setPhotoUrl(null);
contentProviderUtils.updateMarker(context, markerBuilder.getMarker());
assertEquals(TEST_NAME_NEW, contentProviderUtils.getMarker(markerId).getName());
assertEquals(TEST_DESC_NEW, contentProviderUtils.getMarker(markerId).getDescription());
assertFalse(marker.hasPhoto());
assertEquals(TEST_NAME_NEW, contentProviderUtils.getMarker(markerId).name());
assertEquals(TEST_DESC_NEW, contentProviderUtils.getMarker(markerId).description());
assertFalse(dir.exists());
}
@@ -688,10 +741,8 @@ public class CustomContentProviderUtilsTest {
// Insert two markers with photos.
TrackPoint trackPoint = getLastValidTrackPoint(trackId);
Marker marker = TestDataUtil.createMarkerWithPhoto(context, trackId, trackPoint);
marker.setDescription(TEST_DESC);
Marker otherMarker = TestDataUtil.createMarkerWithPhoto(context, trackId, trackPoint);
otherMarker.setDescription(TEST_DESC);
Marker marker = TestDataUtil.createMarkerWithPhoto(context, trackId, trackPoint, TEST_DESC);
Marker otherMarker = TestDataUtil.createMarkerWithPhoto(context, trackId, trackPoint, TEST_DESC);
Marker.Id markerId = contentProviderUtils.insertMarker(marker);
contentProviderUtils.insertMarker(otherMarker);
@@ -701,12 +752,14 @@ public class CustomContentProviderUtilsTest {
assertEquals(2, dir.list().length);
// Update one marker deleting photo.
marker = contentProviderUtils.getMarker(markerId);
marker.setPhotoUrl(null);
contentProviderUtils.updateMarker(context, marker);
MarkerBuilder markerBuilder = new MarkerBuilder(contentProviderUtils.getMarker(markerId));
markerBuilder.setPhotoUrl(null);
contentProviderUtils.updateMarker(context, markerBuilder.getMarker());
assertEquals(TEST_DESC, contentProviderUtils.getMarker(markerId).getDescription());
assertFalse(marker.hasPhoto());
// then
marker = contentProviderUtils.getMarker(markerId);
assertEquals(TEST_DESC, contentProviderUtils.getMarker(markerId).description());
assertNull(marker.photoUrl());
assertTrue(dir.exists());
assertTrue(dir.isDirectory());
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.HeartRate;
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.Power;
import de.dennisguse.opentracks.data.models.Speed;
@@ -152,7 +153,17 @@ public class ExportImportTest {
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);
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
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
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.getSensorManager().sensorDataSet = new SensorDataSet(trackPointCreator);
@@ -195,7 +216,7 @@ public class ExportImportTest {
contentProviderUtils.updateTrack(trackBuilder.getTrack());
trackPoints = TestDataUtil.getTrackPoints(contentProviderUtils, trackId);
markers = TestDataUtil.getMarkers(contentProviderUtils, trackId);
markers = TestDataUtil.getMarkers(contentProviderUtils, trackId);
}
@LargeTest
@@ -533,20 +554,20 @@ public class ExportImportTest {
private void assertMarkers() {
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++) {
Marker marker = markers.get(i);
Marker importMarker = importedMarkers.get(i);
assertEquals(marker.getCategory(), importMarker.getCategory());
assertEquals(marker.getDescription(), importMarker.getDescription());
assertEquals(marker.category(), importMarker.category());
assertEquals(marker.description(), importMarker.description());
// assertEquals(marker.getIcon(), importMarker.getIcon()); // TODO for KML
assertEquals(marker.getName(), importMarker.getName());
assertFalse(importMarker.hasPhoto());
assertEquals(marker.name(), importMarker.name());
assertNull(importMarker.photoUrl());
assertEquals(marker.getPosition().latitude(), importMarker.getPosition().latitude(), 0.001);
assertEquals(marker.getPosition().longitude(), importMarker.getPosition().longitude(), 0.001);
assertEquals(marker.getPosition().altitude().toM(), importMarker.getPosition().altitude().toM(), 0.1);
assertEquals(marker.position().latitude(), importMarker.position().latitude(), 0.001);
assertEquals(marker.position().longitude(), importMarker.position().longitude(), 0.001);
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.Statistics;
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.tables.MarkerColumns;
import de.dennisguse.opentracks.data.tables.TrackPointsColumns;
@@ -319,7 +318,7 @@ public class ContentProviderUtils {
int latitudeIndex = cursor.getColumnIndexOrThrow(MarkerColumns.LATITUDE);
int timeIndex = cursor.getColumnIndexOrThrow(MarkerColumns.TIME);
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 photoUrlIndex = cursor.getColumnIndexOrThrow(MarkerColumns.PHOTOURL);
@@ -353,37 +352,27 @@ public class ContentProviderUtils {
bearing,
null);
Track.Id trackId = new Track.Id(cursor.getLong(trackIdIndex));
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));
}
Uri photoUrl = null;
if (!cursor.isNull(photoUrlIndex)) {
String photoUrl = cursor.getString(photoUrlIndex);
if (photoUrl.isEmpty()) {
if (cursor.getString(photoUrlIndex).isEmpty()) {
// Before v4.18.0: a marker without a picture as URL ""
// TODO Data should be migrated.
marker.setPhotoUrl(null);
photoUrl = null;
} else {
marker.setPhotoUrl(Uri.parse(cursor.getString(photoUrlIndex)));
photoUrl = Uri.parse(cursor.getString(photoUrlIndex));
}
}
return marker;
return new 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) {
@@ -445,24 +434,26 @@ public class ContentProviderUtils {
* @return true if successful.
*/
public boolean updateMarker(Context context, Marker updateMarker) {
Marker savedMarker = getMarker(updateMarker.getId());
if (!updateMarker.hasPhoto()) {
Marker savedMarker = getMarker(updateMarker.id());
if (updateMarker.photoUrl() == null) {
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;
}
public Marker.Id insertMarker(@NonNull Marker marker) {
marker.setId(null);
Uri uri = contentResolver.insert(MarkerColumns.CONTENT_URI, createContentValues(marker));
assert marker.id() == null;
assert marker.trackId() != null;
Uri uri = contentResolver.insert(MarkerColumns.CONTENT_URI, createContentValues(marker, marker.trackId()));
return new Marker.Id(ContentUris.parseId(uri));
}
private void deleteMarkerPhoto(Context context, Marker marker) {
if (marker != null && marker.hasPhoto()) {
Uri uri = marker.getPhotoUrl();
File file = MarkerUtils.buildInternalPhotoFile(context, marker.getTrackId(), uri);
if (marker != null && marker.photoUrl() != null) {
Uri uri = marker.photoUrl();
File file = MarkerUtils.buildInternalPhotoFile(context, marker.trackId(), uri);
if (file.exists()) {
File parent = file.getParentFile();
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();
if (marker.getId() != null) {
values.put(MarkerColumns._ID, marker.getId().id());
if (marker.id() != null) {
values.put(MarkerColumns._ID, marker.id().id());
}
values.put(MarkerColumns.NAME, marker.getName());
values.put(MarkerColumns.DESCRIPTION, marker.getDescription());
values.put(MarkerColumns.CATEGORY, marker.getCategory());
values.put(MarkerColumns.ICON, marker.getIcon());
values.put(MarkerColumns.TRACKID, marker.getTrackId().id());
values.put(MarkerColumns.LONGITUDE, (int) (marker.getPosition().longitude() * 1E6));
values.put(MarkerColumns.LATITUDE, (int) (marker.getPosition().latitude() * 1E6));
values.put(MarkerColumns.TIME, marker.getTime().toEpochMilli());
if (marker.hasAltitude()) {
values.put(MarkerColumns.ALTITUDE, marker.getAltitude().toM());
values.put(MarkerColumns.TRACKID, trackId.id());
values.put(MarkerColumns.NAME, marker.name());
values.put(MarkerColumns.DESCRIPTION, marker.description());
values.put(MarkerColumns.CATEGORY, marker.category());
values.put(MarkerColumns.ICON, marker.icon());
values.put(MarkerColumns.LONGITUDE, (int) (marker.position().longitude() * 1E6));
values.put(MarkerColumns.LATITUDE, (int) (marker.position().latitude() * 1E6));
values.put(MarkerColumns.TIME, marker.position().time().toEpochMilli());
if (marker.position().hasAltitude()) {
values.put(MarkerColumns.ALTITUDE, marker.position().altitude().toM());
}
if (marker.hasAccuracy()) {
values.put(MarkerColumns.ACCURACY, marker.getAccuracy().toM());
if (marker.position().hasHorizontalAccuracy()) {
values.put(MarkerColumns.HORIZONTAL_ACCURACY, marker.position().horizontalAccuracy().toM());
}
if (marker.hasBearing()) {
values.put(MarkerColumns.BEARING, marker.getBearing());
if (marker.position().hasBearing()) {
values.put(MarkerColumns.BEARING, marker.position().bearing());
}
if (marker.hasPhoto()) {
values.put(MarkerColumns.PHOTOURL, marker.getPhotoUrl().toString());
}
values.put(MarkerColumns.PHOTOURL, marker.photoUrl() != null ? marker.photoUrl().toString() : null);
return values;
}
@@ -603,7 +594,7 @@ public class ContentProviderUtils {
public int bulkInsertMarkers(List<Marker> markers, Track.Id trackId) {
ContentValues[] values = new ContentValues[markers.size()];
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);
}
@@ -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;
import android.net.Uri;
@@ -23,151 +7,32 @@ import android.os.Parcelable;
import androidx.annotation.NonNull;
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.
*
* @author Leif Hendrik Wilden
* @author Rodrigo Damazio
* * NOTE: A marker is indirectly (via it's {@link Position}) assigned to one {@link TrackPoint} via position.time.
*/
//TODO All data should be final; no default values.
public final class Marker {
public record Marker(
private Id id;
private String name = "";
private String description = "";
private String category = "";
private String icon = "";
private Track.Id trackId;
@NonNull
Id id,
//Some data might not be used.
private final Position position;
@NonNull
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) {
this.trackId = trackId;
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;
}
//Some data might not be used.
Position position,
@Nullable
Uri photoUrl
) {
public record Id(long id) implements Parcelable {
@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 DESCRIPTION = "description"; // marker description
String CATEGORY = "category"; // marker category
@Deprecated //TODO Remove: it is always: position
String ICON = "icon"; // marker icon
String TRACKID = "trackid"; // track id
String LONGITUDE = "longitude"; // longitude
String LATITUDE = "latitude"; // latitude
String TIME = "time"; // time
String ALTITUDE = "elevation"; // altitude //TODO RENAME column
String ACCURACY = "accuracy"; // accuracy
String HORIZONTAL_ACCURACY = "accuracy"; // accuracy
String BEARING = "bearing"; // bearing
String PHOTOURL = "photoUrl"; // url for the photo
@@ -61,7 +62,7 @@ public interface MarkerColumns extends BaseColumns {
+ LATITUDE + " INTEGER, "
+ TIME + " INTEGER, "
+ ALTITUDE + " FLOAT, "
+ ACCURACY + " FLOAT, "
+ HORIZONTAL_ACCURACY + " FLOAT, "
+ BEARING + " FLOAT, "
+ PHOTOURL + " TEXT, "
+ "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) {
printWriter.println("<wpt " + formatLocation(marker.getPosition()) + ">");
if (marker.hasAltitude()) {
printWriter.println("<ele>" + ALTITUDE_FORMAT.format(marker.getAltitude().toM()) + "</ele>");
printWriter.println("<wpt " + formatLocation(marker.position()) + ">");
if (marker.position().hasAltitude()) {
printWriter.println("<ele>" + ALTITUDE_FORMAT.format(marker.position().altitude().toM()) + "</ele>");
}
printWriter.println("<time>" + StringUtils.formatDateTimeIso8601(marker.getTime(), zoneOffset) + "</time>");
printWriter.println("<name>" + StringUtils.formatCData(marker.getName()) + "</name>");
printWriter.println("<desc>" + StringUtils.formatCData(marker.getDescription()) + "</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("<time>" + StringUtils.formatDateTimeIso8601(marker.position().time(), zoneOffset) + "</time>");
printWriter.println("<name>" + StringUtils.formatCData(marker.name()) + "</name>");
printWriter.println("<desc>" + StringUtils.formatCData(marker.description()) + "</desc>");
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>");
}
@@ -266,11 +266,11 @@ public class KMLTrackExporter implements TrackExporter {
}
private void writeMarker(Marker marker, ZoneOffset zoneOffset) {
boolean existsPhoto = MarkerUtils.buildInternalPhotoFile(context, marker.getTrackId(), marker.getPhotoUrl()) != null;
if (marker.hasPhoto() && exportPhotos && existsPhoto) {
boolean existsPhoto = MarkerUtils.buildInternalPhotoFile(context, marker.trackId(), marker.photoUrl()) != null;
if (marker.photoUrl() != null && exportPhotos && existsPhoto) {
writePhotoOverlay(marker, zoneOffset);
} 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) {
printWriter.println("<PhotoOverlay>");
printWriter.println("<name>" + StringUtils.formatCData(marker.getName()) + "</name>");
printWriter.println("<description>" + StringUtils.formatCData(marker.getDescription()) + "</description>");
printWriter.println("<name>" + StringUtils.formatCData(marker.name()) + "</name>");
printWriter.println("<description>" + StringUtils.formatCData(marker.description()) + "</description>");
printWriter.print("<Camera>");
printWriter.print("<longitude>" + marker.getPosition().longitude() + "</longitude>");
printWriter.print("<latitude>" + marker.getPosition().latitude() + "</latitude>");
printWriter.print("<longitude>" + marker.position().longitude() + "</longitude>");
printWriter.print("<latitude>" + marker.position().latitude() + "</latitude>");
printWriter.print("<altitude>20</altitude>");
printWriter.print("<tilt>90</tilt>");
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>");
writeTypeLocalized(marker.getCategory());
writeTypeLocalized(marker.category());
if (exportPhotos) {
printWriter.println("<Icon><href>" + KMZTrackExporter.buildKmzImageFilePath(marker) + "</href></Icon>");
@@ -452,7 +452,7 @@ public class KMLTrackExporter implements TrackExporter {
printWriter.print("<topFov>45</topFov>");
printWriter.println("</ViewVolume>");
printWriter.println("<Point>");
printWriter.println("<coordinates>" + getCoordinates(marker.getPosition(), ",") + "</coordinates>");
printWriter.println("<coordinates>" + getCoordinates(marker.position(), ",") + "</coordinates>");
printWriter.println("</Point>");
printWriter.println("</PhotoOverlay>");
}
@@ -95,8 +95,8 @@ public class KMZTrackExporter implements TrackExporter {
if (Thread.interrupted()) throw new InterruptedException();
Marker marker = markerIterator.next();
if (marker.hasPhoto()) {
Uri uriPhoto = marker.getPhotoUrl();
if (marker.photoUrl() != null) {
Uri uriPhoto = marker.photoUrl();
boolean existsPhoto = MarkerUtils.buildInternalPhotoFile(context, track.id(), uriPhoto) != null;
if (existsPhoto) {
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.
*/
public static String buildKmzImageFilePath(Marker marker) {
String ext = FileUtils.getExtension(marker.getPhotoUrl());
String ext = FileUtils.getExtension(marker.photoUrl());
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.Distance;
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.Speed;
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());
return;
}
Marker marker = new Marker(null, trackPoint);
if (name != null) {
marker.setName(name);
}
if (description != null) {
marker.setDescription(description);
}
if (markerType != null) {
marker.setCategory(markerType);
}
if (photoUrl != null) {
marker.setPhotoUrl(photoUrl);
}
markers.add(marker);
markers.add(new Marker(
null,
null,
name != null ? name : "",
description != null ? description : "",
markerType != null ? markerType : "",
null,
trackPoint.getPosition(),
photoUrl
));
}
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.Marker;
import de.dennisguse.opentracks.data.models.MarkerBuilder;
import de.dennisguse.opentracks.data.models.Position;
import de.dennisguse.opentracks.data.models.Speed;
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));
Marker marker = new Marker(null, new TrackPoint(TrackPoint.Type.TRACKPOINT, position)); //TODO Creating marker without need
marker.setName(name != null ? name : "");
marker.setDescription(description != null ? description : "");
marker.setCategory(activityTypeLocalized != null ? activityTypeLocalized : "");
marker.setPhotoUrl(photoUrl);
markers.add(marker);
markers.add(new Marker(
null,
null,
name != null ? name : "",
description != null ? description : "",
activityTypeLocalized != null ? activityTypeLocalized : "",
null,
position,
photoUrl
));
name = null;
description = null;
@@ -36,6 +36,7 @@ import java.util.zip.ZipInputStream;
import de.dennisguse.opentracks.R;
import de.dennisguse.opentracks.data.ContentProviderUtils;
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.util.AutoCloseableIterator;
import de.dennisguse.opentracks.util.FileUtils;
@@ -191,8 +192,8 @@ public class KMZTrackImporter {
List<String> photosName = new ArrayList<>();
try (AutoCloseableIterator<Marker> markerIterator = new ContentProviderUtils(context).getMarkerIterator(trackId)) {
markerIterator.forEachRemaining(marker -> {
if (marker.hasPhoto()) {
String photoUrl = Uri.decode(marker.getPhotoUrl().toString()); //TODO Why Uri.decode()?
if (marker.photoUrl() != null) {
String photoUrl = Uri.decode(marker.photoUrl().toString()); //TODO Why Uri.decode()?
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.Distance;
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.Track;
import de.dennisguse.opentracks.data.models.TrackBuilder;
@@ -58,7 +59,7 @@ public class TrackImporter {
// Current track
private TrackData trackData;
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) {
this.context = context;
@@ -86,7 +87,7 @@ public class TrackImporter {
}
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) {
@@ -165,10 +166,8 @@ public class TrackImporter {
// Store Markers
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.
trackPoints.clear();
@@ -37,6 +37,7 @@ import java.time.Duration;
import de.dennisguse.opentracks.data.ContentProviderUtils;
import de.dennisguse.opentracks.data.models.Distance;
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.TrackPoint;
import de.dennisguse.opentracks.sensors.GpsStatusValue;
@@ -249,7 +250,16 @@ public class TrackRecordingService extends Service implements TrackPointCreator.
if (trackPoint == 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);
}
@@ -94,14 +94,14 @@ public class ShareUtils {
Log.e(TAG, "MarkerId " + markerId.id() + " could not be resolved.");
continue;
}
if (marker.getPhotoUrl() == null) {
if (marker.photoUrl() == null) {
Log.e(TAG, "MarkerId " + markerId.id() + " has no picture.");
continue;
}
mime = context.getContentResolver().getType(marker.getPhotoUrl());
mime = context.getContentResolver().getType(marker.photoUrl());
uris.add(marker.getPhotoUrl());
uris.add(marker.photoUrl());
}
if (uris.isEmpty()) {
@@ -67,14 +67,14 @@ public class MarkerDetailActivity extends AbstractActivity implements DeleteMark
ContentProviderUtils contentProviderUtils = new ContentProviderUtils(this);
Marker marker = contentProviderUtils.getMarker(markerId);
markerAutoCloseableIterator = contentProviderUtils.getMarkerIterator(marker.getTrackId());
markerAutoCloseableIterator = contentProviderUtils.getMarkerIterator(marker.trackId());
if (markerAutoCloseableIterator == null) {
finish();
}
int markerIndex = -1;
while (markerAutoCloseableIterator.hasNext()) {
if (markerId.equals(markerAutoCloseableIterator.next().getId())) {
if (markerId.equals(markerAutoCloseableIterator.next().id())) {
markerIndex = markerAutoCloseableIterator.getPosition();
break;
}
@@ -124,7 +124,7 @@ public class MarkerDetailActivity extends AbstractActivity implements DeleteMark
@NonNull
public Fragment createFragment(int position) {
markerAutoCloseableIterator.moveToPosition(position);
return MarkerDetailFragment.newInstance(markerAutoCloseableIterator.next().getId());
return MarkerDetailFragment.newInstance(markerAutoCloseableIterator.next().id());
}
@Override
@@ -135,7 +135,7 @@ public class MarkerDetailActivity extends AbstractActivity implements DeleteMark
@Nullable
public CharSequence getPageTitle(int 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 (marker.hasPhoto()) {
Intent intent = ShareUtils.newShareFileIntent(getContext(), marker.getId());
if (marker.photoUrl() != null) {
Intent intent = ShareUtils.newShareFileIntent(getContext(), marker.id());
intent = Intent.createChooser(intent, null);
startActivity(intent);
}
@@ -180,15 +180,15 @@ public class MarkerDetailFragment extends Fragment {
}
private void updateUi() {
if (marker.hasPhoto()) {
viewBinding.markerDetailMarkerPhoto.setImageURI(marker.getPhotoUrl());
if (marker.photoUrl() != null) {
viewBinding.markerDetailMarkerPhoto.setImageURI(marker.photoUrl());
} else {
viewBinding.markerDetailMarkerPhoto.setImageDrawable(MarkerUtils.getDefaultPhoto(getContext()));
}
viewBinding.markerDetailMarkerCategory.setText(StringUtils.getCategory(marker.getCategory()));
viewBinding.markerDetailMarkerDescription.setText(marker.getDescription());
viewBinding.markerDetailMarkerCategory.setText(StringUtils.getCategory(marker.category()));
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.R;
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.Track;
import de.dennisguse.opentracks.data.models.TrackPoint;
@@ -181,11 +182,11 @@ public class MarkerEditActivity extends AbstractActivity {
private void loadMarkerData(Marker data) {
marker = data;
viewBinding.markerEditName.setText(marker.getName());
viewBinding.markerEditMarkerType.setText(marker.getCategory());
viewBinding.markerEditDescription.setText(marker.getDescription());
if (marker.hasPhoto()) {
setMarkerImageView(marker.getPhotoUrl());
viewBinding.markerEditName.setText(marker.name());
viewBinding.markerEditMarkerType.setText(marker.category());
viewBinding.markerEditDescription.setText(marker.description());
if (marker.photoUrl() != null) {
setMarkerImageView(marker.photoUrl());
} else {
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.
*/
private void hideAndShowOptions() {
boolean isPhotoSet = (marker != null && marker.hasPhoto());
boolean isPhotoSet = (marker != null && marker.photoUrl() != null);
if (insertPhotoMenuItem != null && insertGalleryImgMenuItem != null) {
insertPhotoMenuItem.setVisible(!isPhotoSet);
insertGalleryImgMenuItem.setVisible(!isPhotoSet);
@@ -259,7 +260,7 @@ public class MarkerEditActivity extends AbstractActivity {
* Returns the trackId; either from track or marker.
*/
private Track.Id getTrackId() {
return trackId == null ? marker.getTrackId() : trackId;
return trackId == null ? marker.trackId() : trackId;
}
private void setMarkerImageView(@NonNull Uri uri) {
@@ -20,6 +20,7 @@ import java.util.NoSuchElementException;
import de.dennisguse.opentracks.R;
import de.dennisguse.opentracks.data.ContentProviderUtils;
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.TrackPoint;
import de.dennisguse.opentracks.util.FileUtils;
@@ -40,8 +41,8 @@ public class MarkerEditViewModel extends AndroidViewModel {
markerData = new MutableLiveData<>();
Marker marker = new ContentProviderUtils(getApplication()).getMarker(markerId);
if (marker.hasPhoto()) {
photoOriginalUri = marker.getPhotoUrl();
if (marker.photoUrl() != null) {
photoOriginalUri = marker.photoUrl();
}
markerData.postValue(marker);
@@ -63,20 +64,20 @@ public class MarkerEditViewModel extends AndroidViewModel {
return;
}
File photoFile = MarkerUtils.getPhotoFileIfExists(getApplication(), markerData.getValue().getTrackId(), photoUri);
File photoFile = MarkerUtils.getPhotoFileIfExists(getApplication(), markerData.getValue().trackId(), photoUri);
if (photoFile != null) {
FileUtils.deleteDirectoryRecurse(photoFile);
}
}
private void deletePhoto(Marker marker) {
if (marker.hasPhoto()) {
deletePhoto(marker.getPhotoUrl());
if (marker.photoUrl() != null) {
deletePhoto(marker.photoUrl());
}
}
public void onPhotoDelete(String name, String category, String description) {
Marker marker = getMarker();
MarkerBuilder marker = new MarkerBuilder(getMarker());
if (marker.hasPhoto()) {
if (!marker.getPhotoUrl().equals(photoOriginalUri)) {
deletePhoto(marker.getPhotoUrl());
@@ -85,21 +86,21 @@ public class MarkerEditViewModel extends AndroidViewModel {
marker.setName(name);
marker.setCategory(category);
marker.setDescription(description);
markerData.postValue(marker);
markerData.postValue(marker.getMarker());
}
}
public void onNewCameraPhoto(@NonNull Uri photoUri, String name, String category, String description) {
Marker marker = getMarker();
MarkerBuilder marker = new MarkerBuilder(getMarker());
marker.setPhotoUrl(photoUri);
marker.setName(name);
marker.setCategory(category);
marker.setDescription(description);
markerData.postValue(marker);
markerData.postValue(marker.getMarker());
}
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")) {
FileDescriptor srcFd = parcelFd.getFileDescriptor();
@@ -111,7 +112,8 @@ public class MarkerEditViewModel extends AndroidViewModel {
marker.setName(name);
marker.setCategory(category);
marker.setDescription(description);
markerData.postValue(marker);
markerData.postValue(marker.getMarker());
} catch (IOException e) {
Log.e(TAG, e.getMessage());
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) {
Marker marker = getMarker();
MarkerBuilder marker = new MarkerBuilder(getMarker());
marker.setName(name);
marker.setCategory(category);
marker.setDescription(description);
if (marker.getId() == null) {
new ContentProviderUtils(getApplication()).insertMarker(marker);
new ContentProviderUtils(getApplication()).insertMarker(marker.getMarker());
} else {
new ContentProviderUtils(getApplication()).updateMarker(getApplication(), marker);
new ContentProviderUtils(getApplication()).updateMarker(getApplication(), marker.getMarker());
}
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 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) {
markerData = new MutableLiveData<>();
@@ -158,7 +168,7 @@ public class MarkerEditViewModel extends AndroidViewModel {
// it's new marker -> clean all photos.
deletePhoto(marker);
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).
deletePhoto(marker);
}
@@ -137,7 +137,7 @@ public class MarkerListAdapter extends RecyclerView.Adapter<RecyclerView.ViewHol
public void setAllSelected(boolean isSelected) {
if (isSelected) {
for (Marker marker : markers) {
selection.put((int) marker.getId().id(), true);
selection.put((int) marker.id().id(), true);
}
} else {
selection.clear();
@@ -179,24 +179,24 @@ public class MarkerListAdapter extends RecyclerView.Adapter<RecyclerView.ViewHol
public void bind(Marker marker) {
this.marker = marker;
viewBinding.markerListItemPhoto.setVisibility(marker.hasPhoto() ? View.VISIBLE : View.GONE);
if (marker.hasPhoto()) {
viewBinding.markerListItemPhoto.setVisibility(marker.photoUrl() != null ? View.VISIBLE : View.GONE);
if (marker.photoUrl() != null) {
int height = ThemeUtils.getPhotoHeight(context);
ViewGroup.LayoutParams params = viewBinding.markerListItemPhoto.getLayoutParams();
params.height = height;
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();
ListItemUtils.setDateAndTime(context, viewBinding.markerListItemDate, viewBinding.markerListItemTime, marker.getTime(), timeZone);
ZoneOffset timeZone = new ContentProviderUtils(context).getTrack(marker.trackId()).zoneOffset();
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);
setSelected(selection.get((int) getId()));
@@ -208,7 +208,7 @@ public class MarkerListAdapter extends RecyclerView.Adapter<RecyclerView.ViewHol
}
public long getId() {
return marker.getId().id();
return marker.id().id();
}
@Override
@@ -218,7 +218,7 @@ public class MarkerListAdapter extends RecyclerView.Adapter<RecyclerView.ViewHol
actionMode.invalidate();
} else {
Intent intent = IntentUtils.newIntent(context, MarkerDetailActivity.class)
.putExtra(MarkerDetailActivity.EXTRA_MARKER_ID, marker.getId());
.putExtra(MarkerDetailActivity.EXTRA_MARKER_ID, marker.id());
context.startActivity(intent);
}
}
@@ -20,11 +20,13 @@ import android.content.Context;
import android.content.Intent;
import android.content.UriPermission;
import android.net.Uri;
import android.text.TextUtils;
import android.util.Log;
import androidx.documentfile.provider.DocumentFile;
import de.dennisguse.opentracks.data.models.Marker;
import de.dennisguse.opentracks.data.models.MarkerBuilder;
/**
* Utilities for creating intents.
@@ -55,9 +57,9 @@ public class IntentUtils {
*/
public static void showCoordinateOnMap(Context context, Marker marker) {
//SEE https://developer.android.com/guide/components/intents-common.html#Maps
String uri = "geo:0,0?q=" + marker.getPosition().latitude() + "," + marker.getPosition().longitude();
if (marker.getName() != null && !marker.getName().isEmpty()) {
uri += "(" + marker.getName() + ")";
String uri = "geo:0,0?q=" + marker.position().latitude() + "," + marker.position().longitude();
if (!TextUtils.isEmpty(marker.name())) {
uri += "(" + marker.name() + ")";
}
Intent intent = new Intent(Intent.ACTION_VIEW);
intent.setData(Uri.parse(uri));