Cleanup: Marker stores URI to photo (instead of String).

This commit is contained in:
Dennis Guse
2025-02-21 22:50:24 +01:00
parent b9ed5e28e0
commit 1b33184835
17 changed files with 45 additions and 78 deletions
@@ -145,14 +145,13 @@ public class TestDataUtil {
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);
String photoUrl = photoUri.toString();
//TODO Use TrackStatisticsUpdater //TODO Use TrackStatisticsUpdater
TrackStatistics stats = new TrackStatistics(); TrackStatistics stats = new TrackStatistics();
stats.setTotalDistance(Distance.of(0)); stats.setTotalDistance(Distance.of(0));
stats.setTotalTime(Duration.ofMillis(0)); stats.setTotalTime(Duration.ofMillis(0));
return new Marker("Marker name", "Marker description", "Marker category", "", trackId, trackPoint, photoUrl); return new Marker("Marker name", "Marker description", "Marker category", "", trackId, trackPoint, photoUri);
} }
public static List<TrackPoint> getTrackPoints(ContentProviderUtils contentProviderUtils, Track.Id trackId) { public static List<TrackPoint> getTrackPoints(ContentProviderUtils contentProviderUtils, Track.Id trackId) {
@@ -429,40 +429,6 @@ public class CustomContentProviderUtilsTest {
assertEquals(TEST_DESC, contentValues.get(MarkerColumns.DESCRIPTION)); assertEquals(TEST_DESC, contentValues.get(MarkerColumns.DESCRIPTION));
} }
/**
* Tests the method {@link ContentProviderUtils#createMarker(Cursor)}.
*/
@Test
public void testCreateMarker() {
int startColumnIndex = 1;
int columnIndex = startColumnIndex;
when(cursorMock.getColumnIndexOrThrow(MarkerColumns._ID)).thenReturn(columnIndex++);
when(cursorMock.getColumnIndexOrThrow(MarkerColumns.NAME)).thenReturn(columnIndex++);
when(cursorMock.getColumnIndexOrThrow(MarkerColumns.TRACKID)).thenReturn(columnIndex++);
columnIndex = startColumnIndex;
// Id
when(cursorMock.isNull(columnIndex++)).thenReturn(false);
// Name
when(cursorMock.isNull(columnIndex++)).thenReturn(false);
// trackIdIndex
when(cursorMock.isNull(columnIndex++)).thenReturn(false);
long id = System.currentTimeMillis();
columnIndex = startColumnIndex;
// Id
when(cursorMock.getLong(columnIndex++)).thenReturn(id);
// Name
String name = NAME_PREFIX + id;
when(cursorMock.getString(columnIndex++)).thenReturn(name);
// trackIdIndex
long trackId = 11L;
when(cursorMock.getLong(columnIndex++)).thenReturn(trackId);
Marker marker = contentProviderUtils.createMarker(cursorMock);
assertEquals(id, marker.getId().id());
assertEquals(name, marker.getName());
assertEquals(trackId, marker.getTrackId().id());
}
/** /**
* Tests the method * Tests the method
* {@link ContentProviderUtils#deleteMarker(Context, Marker.Id)} * {@link ContentProviderUtils#deleteMarker(Context, Marker.Id)}
@@ -1,6 +1,7 @@
package de.dennisguse.opentracks.io.file.importer; package de.dennisguse.opentracks.io.file.importer;
import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNotNull; import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertNull; import static org.junit.Assert.assertNull;
@@ -142,7 +143,7 @@ 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, 14, 10, 13, 15, 10, 1f); sendLocation(trackPointCreator, "2020-02-02T02:02:03Z", 3, 14, 10, 13, 15, 10, 1f);
contentProviderUtils.insertMarker(new Marker("Marker 1", "Marker 1 desc", "Marker 1 category", null, trackId, service.getLastStoredTrackPointWithLocation(), "")); contentProviderUtils.insertMarker(new Marker("Marker 1", "Marker 1 desc", "Marker 1 category", null, trackId, service.getLastStoredTrackPointWithLocation(), null));
// A sensor-only TrackPoint // A sensor-only TrackPoint
trackPointCreator.setClock("2020-02-02T02:02:04Z"); trackPointCreator.setClock("2020-02-02T02:02:04Z");
@@ -157,7 +158,7 @@ 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, 14.001, 10, 13, 15, 10, 0f); sendLocation(trackPointCreator, "2020-02-02T02:02:17Z", 3, 14.001, 10, 13, 15, 10, 0f);
contentProviderUtils.insertMarker(new Marker("Marker 2", "Marker 2 desc", "Marker 2 category", null, trackId, service.getLastStoredTrackPointWithLocation(), "")); contentProviderUtils.insertMarker(new Marker("Marker 2", "Marker 2 desc", "Marker 2 category", null, trackId, service.getLastStoredTrackPointWithLocation(), 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);
@@ -540,7 +541,7 @@ public class ExportImportTest {
assertEquals(marker.getDescription(), importMarker.getDescription()); assertEquals(marker.getDescription(), importMarker.getDescription());
// assertEquals(marker.getIcon(), importMarker.getIcon()); // TODO for KML // assertEquals(marker.getIcon(), importMarker.getIcon()); // TODO for KML
assertEquals(marker.getName(), importMarker.getName()); assertEquals(marker.getName(), importMarker.getName());
assertEquals("", importMarker.getPhotoUrl()); assertFalse(importMarker.hasPhoto());
assertEquals(marker.getLocation().getLatitude(), importMarker.getLocation().getLatitude(), 0.001); assertEquals(marker.getLocation().getLatitude(), importMarker.getLocation().getLatitude(), 0.001);
assertEquals(marker.getLocation().getLongitude(), importMarker.getLocation().getLongitude(), 0.001); assertEquals(marker.getLocation().getLongitude(), importMarker.getLocation().getLongitude(), 0.001);
@@ -418,7 +418,7 @@ public class ContentProviderUtils {
marker.setIcon(cursor.getString(iconIndex)); marker.setIcon(cursor.getString(iconIndex));
} }
if (!cursor.isNull(photoUrlIndex)) { if (!cursor.isNull(photoUrlIndex)) {
marker.setPhotoUrl(cursor.getString(photoUrlIndex)); marker.setPhotoUrl(Uri.parse(cursor.getString(photoUrlIndex)));
} }
return marker; return marker;
} }
@@ -496,7 +496,7 @@ public class ContentProviderUtils {
private void deleteMarkerPhoto(Context context, Marker marker) { private void deleteMarkerPhoto(Context context, Marker marker) {
if (marker != null && marker.hasPhoto()) { if (marker != null && marker.hasPhoto()) {
Uri uri = marker.getPhotoURI(); Uri uri = marker.getPhotoUrl();
File file = MarkerUtils.buildInternalPhotoFile(context, marker.getTrackId(), uri); File file = MarkerUtils.buildInternalPhotoFile(context, marker.getTrackId(), uri);
if (file.exists()) { if (file.exists()) {
File parent = file.getParentFile(); File parent = file.getParentFile();
@@ -545,7 +545,9 @@ public class ContentProviderUtils {
values.put(MarkerColumns.BEARING, marker.getBearing()); values.put(MarkerColumns.BEARING, marker.getBearing());
} }
values.put(MarkerColumns.PHOTOURL, marker.getPhotoUrl()); if (marker.hasPhoto()) {
values.put(MarkerColumns.PHOTOURL, marker.getPhotoUrl().toString());
}
return values; return values;
} }
@@ -24,7 +24,6 @@ import android.os.Parcelable;
import androidx.annotation.NonNull; import androidx.annotation.NonNull;
import androidx.annotation.Nullable; import androidx.annotation.Nullable;
import java.time.Duration;
import java.time.Instant; import java.time.Instant;
/** /**
@@ -51,8 +50,7 @@ public final class Marker {
private Altitude altitude; private Altitude altitude;
private Float bearing; private Float bearing;
@Deprecated //TODO Make an URI instead of String private Uri photoUrl = null;
private String photoUrl = "";
public Marker(@Nullable Track.Id trackId, Instant time) { public Marker(@Nullable Track.Id trackId, Instant time) {
this.trackId = trackId; this.trackId = trackId;
@@ -71,7 +69,7 @@ public final class Marker {
} }
@Deprecated @Deprecated
public Marker(String name, String description, String category, String icon, @NonNull Track.Id trackId, @NonNull TrackPoint trackPoint, String photoUrl) { public Marker(String name, String description, String category, String icon, @NonNull Track.Id trackId, @NonNull TrackPoint trackPoint, Uri photoUrl) {
this(trackId, trackPoint); this(trackId, trackPoint);
this.name = name; this.name = name;
this.description = description; this.description = description;
@@ -222,20 +220,16 @@ public final class Marker {
this.bearing = bearing; this.bearing = bearing;
} }
public String getPhotoUrl() { public Uri getPhotoUrl() {
return photoUrl; return photoUrl;
} }
public void setPhotoUrl(String photoUrl) { public void setPhotoUrl(Uri photoUrl) {
this.photoUrl = photoUrl; this.photoUrl = photoUrl;
} }
public Uri getPhotoURI() {
return Uri.parse(photoUrl);
}
public boolean hasPhoto() { public boolean hasPhoto() {
return photoUrl != null && !photoUrl.isEmpty(); return photoUrl != null;
} }
public record Id(long id) implements Parcelable { public record Id(long id) implements Parcelable {
@@ -270,7 +270,7 @@ 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.getPhotoURI()) != null; boolean existsPhoto = MarkerUtils.buildInternalPhotoFile(context, marker.getTrackId(), marker.getPhotoUrl()) != null;
if (marker.hasPhoto() && exportPhotos && existsPhoto) { if (marker.hasPhoto() && exportPhotos && existsPhoto) {
float heading = getHeading(marker.getTrackId(), marker.getLocation()); float heading = getHeading(marker.getTrackId(), marker.getLocation());
writePhotoOverlay(marker, heading, zoneOffset); writePhotoOverlay(marker, heading, zoneOffset);
@@ -97,7 +97,7 @@ public class KmzTrackExporter implements TrackExporter {
} }
Marker marker = contentProviderUtils.createMarker(cursor); Marker marker = contentProviderUtils.createMarker(cursor);
if (marker.hasPhoto()) { if (marker.hasPhoto()) {
Uri uriPhoto = marker.getPhotoURI(); Uri uriPhoto = marker.getPhotoUrl();
boolean existsPhoto = MarkerUtils.buildInternalPhotoFile(context, track.getId(), uriPhoto) != null; boolean existsPhoto = MarkerUtils.buildInternalPhotoFile(context, track.getId(), uriPhoto) != null;
if (existsPhoto) { if (existsPhoto) {
addImage(context, zipOutputStream, uriPhoto, marker); addImage(context, zipOutputStream, uriPhoto, marker);
@@ -17,6 +17,7 @@
package de.dennisguse.opentracks.io.file.importer; package de.dennisguse.opentracks.io.file.importer;
import android.content.Context; import android.content.Context;
import android.net.Uri;
import android.util.Log; import android.util.Log;
import org.xml.sax.Attributes; import org.xml.sax.Attributes;
@@ -109,7 +110,7 @@ public class GpxTrackImporter extends DefaultHandler implements XMLImporter.Trac
private String cadence; private String cadence;
private String power; private String power;
private String markerType; private String markerType;
private String photoUrl; private Uri photoUrl;
private String uuid; private String uuid;
private String gain; private String gain;
private String loss; private String loss;
@@ -18,6 +18,7 @@ package de.dennisguse.opentracks.io.file.importer;
import android.content.Context; import android.content.Context;
import android.location.Location; import android.location.Location;
import android.net.Uri;
import android.util.Log; import android.util.Log;
import org.xml.sax.Attributes; import org.xml.sax.Attributes;
@@ -121,7 +122,7 @@ public class KmlTrackImporter extends DefaultHandler implements XMLImporter.Trac
private String longitude; private String longitude;
private String altitude; private String altitude;
private String markerType; private String markerType;
private String photoUrl; private Uri photoUrl;
private String uuid; private String uuid;
private final TrackImporter trackImporter; private final TrackImporter trackImporter;
@@ -223,7 +224,7 @@ public class KmlTrackImporter extends DefaultHandler implements XMLImporter.Trac
} }
case TAG_HREF -> { case TAG_HREF -> {
if (content != null) { if (content != null) {
photoUrl = content.trim(); photoUrl = Uri.parse(content.trim());
} }
} }
} }
@@ -191,7 +191,7 @@ public class KmzTrackImporter {
List<String> photosName = new ArrayList<>(); List<String> photosName = new ArrayList<>();
for (Marker marker : markers) { for (Marker marker : markers) {
if (marker.hasPhoto()) { if (marker.hasPhoto()) {
String photoUrl = Uri.decode(marker.getPhotoUrl()); String photoUrl = Uri.decode(marker.getPhotoUrl().toString()); //TODO Why Uri.decode()?
photosName.add(photoUrl.substring(photoUrl.lastIndexOf(File.separatorChar) + 1)); photosName.add(photoUrl.substring(photoUrl.lastIndexOf(File.separatorChar) + 1));
} }
} }
@@ -233,12 +233,11 @@ public class TrackImporter {
* *
* @param externalPhotoUrl the file name * @param externalPhotoUrl the file name
*/ */
private String getInternalPhotoUrl(@NonNull Track.Id trackId, @NonNull String externalPhotoUrl) { private Uri getInternalPhotoUrl(@NonNull Track.Id trackId, @NonNull Uri externalPhotoUrl) {
String importFileName = KmzTrackImporter.importNameForFilename(externalPhotoUrl); String importFileName = KmzTrackImporter.importNameForFilename(externalPhotoUrl.toString());
File file = MarkerUtils.buildInternalPhotoFile(context, trackId, Uri.parse(importFileName)); File file = MarkerUtils.buildInternalPhotoFile(context, trackId, Uri.parse(importFileName));
if (file != null) { if (file != null) {
Uri photoUri = FileUtils.getUriForFile(context, file); return FileUtils.getUriForFile(context, file);
return "" + photoUri;
} }
return null; return null;
@@ -93,14 +93,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.getPhotoURI() == null) { if (marker.getPhotoUrl() == 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.getPhotoURI()); mime = context.getContentResolver().getType(marker.getPhotoUrl());
uris.add(marker.getPhotoURI()); uris.add(marker.getPhotoUrl());
} }
if (uris.isEmpty()) { if (uris.isEmpty()) {
@@ -235,7 +235,7 @@ public class MarkerDetailFragment extends Fragment {
boolean hasPhoto = marker.hasPhoto(); boolean hasPhoto = marker.hasPhoto();
if (hasPhoto) { if (hasPhoto) {
handler.removeCallbacks(hideText); handler.removeCallbacks(hideText);
viewBinding.markerDetailMarkerPhoto.setImageURI(marker.getPhotoURI()); viewBinding.markerDetailMarkerPhoto.setImageURI(marker.getPhotoUrl());
handler.postDelayed(hideText, HIDE_TEXT_DELAY.toMillis()); handler.postDelayed(hideText, HIDE_TEXT_DELAY.toMillis());
} else { } else {
viewBinding.markerDetailMarkerPhoto.setImageResource(MarkerUtils.ICON_ID); viewBinding.markerDetailMarkerPhoto.setImageResource(MarkerUtils.ICON_ID);
@@ -183,7 +183,7 @@ public class MarkerEditActivity extends AbstractActivity {
viewBinding.markerEditMarkerType.setText(marker.getCategory()); viewBinding.markerEditMarkerType.setText(marker.getCategory());
viewBinding.markerEditDescription.setText(marker.getDescription()); viewBinding.markerEditDescription.setText(marker.getDescription());
if (marker.hasPhoto()) { if (marker.hasPhoto()) {
setMarkerImageView(marker.getPhotoURI()); setMarkerImageView(marker.getPhotoUrl());
} else { } else {
viewBinding.markerEditPhoto.setImageDrawable(null); viewBinding.markerEditPhoto.setImageDrawable(null);
} }
@@ -41,7 +41,7 @@ public class MarkerEditViewModel extends AndroidViewModel {
Marker marker = new ContentProviderUtils(getApplication()).getMarker(markerId); Marker marker = new ContentProviderUtils(getApplication()).getMarker(markerId);
if (marker.hasPhoto()) { if (marker.hasPhoto()) {
photoOriginalUri = marker.getPhotoURI(); photoOriginalUri = marker.getPhotoUrl();
} }
markerData.postValue(marker); markerData.postValue(marker);
@@ -71,15 +71,15 @@ public class MarkerEditViewModel extends AndroidViewModel {
private void deletePhoto(Marker marker) { private void deletePhoto(Marker marker) {
if (marker.hasPhoto()) { if (marker.hasPhoto()) {
deletePhoto(marker.getPhotoURI()); deletePhoto(marker.getPhotoUrl());
} }
} }
public void onPhotoDelete(String name, String category, String description) { public void onPhotoDelete(String name, String category, String description) {
Marker marker = getMarker(); Marker marker = getMarker();
if (marker.hasPhoto()) { if (marker.hasPhoto()) {
if (!marker.getPhotoURI().equals(photoOriginalUri)) { if (!marker.getPhotoUrl().equals(photoOriginalUri)) {
deletePhoto(marker.getPhotoURI()); deletePhoto(marker.getPhotoUrl());
} }
marker.setPhotoUrl(null); marker.setPhotoUrl(null);
marker.setName(name); marker.setName(name);
@@ -91,7 +91,7 @@ public class MarkerEditViewModel extends AndroidViewModel {
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(); Marker marker = getMarker();
marker.setPhotoUrl(photoUri.toString()); marker.setPhotoUrl(photoUri);
marker.setName(name); marker.setName(name);
marker.setCategory(category); marker.setCategory(category);
marker.setDescription(description); marker.setDescription(description);
@@ -107,7 +107,7 @@ public class MarkerEditViewModel extends AndroidViewModel {
FileUtils.copy(srcFd, dstFile); FileUtils.copy(srcFd, dstFile);
Uri photoUri = FileUtils.getUriForFile(getApplication(), dstFile); Uri photoUri = FileUtils.getUriForFile(getApplication(), dstFile);
marker.setPhotoUrl(photoUri.toString()); marker.setPhotoUrl(photoUri);
marker.setName(name); marker.setName(name);
marker.setCategory(category); marker.setCategory(category);
marker.setDescription(description); marker.setDescription(description);
@@ -130,7 +130,7 @@ public class MarkerEditViewModel extends AndroidViewModel {
new ContentProviderUtils(getApplication()).updateMarker(getApplication(), marker); new ContentProviderUtils(getApplication()).updateMarker(getApplication(), marker);
} }
if (photoOriginalUri != null && (!marker.hasPhoto() || !photoOriginalUri.equals(marker.getPhotoURI()))) { if (photoOriginalUri != null && (!marker.hasPhoto() || !photoOriginalUri.equals(marker.getPhotoUrl()))) {
deletePhoto(photoOriginalUri); deletePhoto(photoOriginalUri);
} }
} }
@@ -143,7 +143,7 @@ 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(name, "", "", icon, trackId, trackPoint, ""); Marker marker = new Marker(name, "", "", icon, trackId, trackPoint, null);
if (markerData == null) { if (markerData == null) {
markerData = new MutableLiveData<>(); markerData = new MutableLiveData<>();
@@ -158,7 +158,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.getPhotoURI().equals(photoOriginalUri))) { } else if (photoOriginalUri == null || (marker.hasPhoto() && !marker.getPhotoUrl().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);
} }
@@ -190,7 +190,7 @@ public class MarkerListAdapter extends RecyclerView.Adapter<RecyclerView.ViewHol
viewBinding.markerListItemPhoto.setLayoutParams(params); viewBinding.markerListItemPhoto.setLayoutParams(params);
viewBinding.markerListItemTextGradient.setLayoutParams(params); viewBinding.markerListItemTextGradient.setLayoutParams(params);
asyncLoadPhoto(viewBinding.markerListItemPhoto, marker.getPhotoURI(), marker.getId()); asyncLoadPhoto(viewBinding.markerListItemPhoto, marker.getPhotoUrl(), marker.getId());
} }
viewBinding.markerListItemName.setText(marker.getName()); viewBinding.markerListItemName.setText(marker.getName());
@@ -109,6 +109,10 @@ public class FileUtils {
return getExtension(file.getName()); return getExtension(file.getName());
} }
public static String getExtension(Uri uri) {
return getExtension(uri.toString());
}
/** /**
* Builds a filename with the given base and the given extension, possibly adding a suffix to ensure the file doesn't exist. * Builds a filename with the given base and the given extension, possibly adding a suffix to ensure the file doesn't exist.
* *