From 4ae9d24960da44dfcd7448d276fe51ef8c6decfb Mon Sep 17 00:00:00 2001 From: Dennis Guse Date: Wed, 26 Nov 2025 21:42:11 +0100 Subject: [PATCH] Implemented MarkerIterator. --- .../data/CachedTrackPointsIndexes.java | 1 + .../opentracks/data/ContentProviderUtils.java | 7 +- .../opentracks/data/MarkerIterator.java | 67 +++++++++++++++++++ .../opentracks/data/TrackPointIterator.java | 6 -- .../io/file/exporter/GPXTrackExporter.java | 15 ++--- .../io/file/exporter/KMLTrackExporter.java | 21 +++--- .../io/file/exporter/KMZTrackExporter.java | 26 +++---- .../io/file/importer/KMZTrackImporter.java | 15 +++-- 8 files changed, 107 insertions(+), 51 deletions(-) create mode 100644 src/main/java/de/dennisguse/opentracks/data/MarkerIterator.java diff --git a/src/main/java/de/dennisguse/opentracks/data/CachedTrackPointsIndexes.java b/src/main/java/de/dennisguse/opentracks/data/CachedTrackPointsIndexes.java index 59379cce6..f4abe195d 100644 --- a/src/main/java/de/dennisguse/opentracks/data/CachedTrackPointsIndexes.java +++ b/src/main/java/de/dennisguse/opentracks/data/CachedTrackPointsIndexes.java @@ -7,6 +7,7 @@ import de.dennisguse.opentracks.data.tables.TrackPointsColumns; /** * A cache of track points indexes. */ +//TOOD Check if this actually useful. class CachedTrackPointsIndexes { final int idIndex; final int typeIndex; diff --git a/src/main/java/de/dennisguse/opentracks/data/ContentProviderUtils.java b/src/main/java/de/dennisguse/opentracks/data/ContentProviderUtils.java index a7e0367f1..5ecf279d1 100644 --- a/src/main/java/de/dennisguse/opentracks/data/ContentProviderUtils.java +++ b/src/main/java/de/dennisguse/opentracks/data/ContentProviderUtils.java @@ -430,6 +430,11 @@ public class ContentProviderUtils { return null; } + public AutoCloseableIterator getMarkerIterator(@NonNull Track.Id trackId) { + return new MarkerIterator(this, trackId, -1); + } + + //TODO Use MarkerIterator instead public Cursor getMarkerCursor(@NonNull Track.Id trackId) { return getMarkerCursor(trackId, -1); } @@ -731,7 +736,7 @@ public class ContentProviderUtils { * @param trackId the track id * @param startTrackPointId the starting trackPoint id. `null` to ignore */ - public AutoCloseableIterator getTrackPointIterator(final Track.Id trackId, final TrackPoint.Id startTrackPointId) { + public AutoCloseableIterator getTrackPointIterator(Track.Id trackId, TrackPoint.Id startTrackPointId) { return new TrackPointIterator(this, trackId, startTrackPointId); } diff --git a/src/main/java/de/dennisguse/opentracks/data/MarkerIterator.java b/src/main/java/de/dennisguse/opentracks/data/MarkerIterator.java new file mode 100644 index 000000000..a989b3d23 --- /dev/null +++ b/src/main/java/de/dennisguse/opentracks/data/MarkerIterator.java @@ -0,0 +1,67 @@ +package de.dennisguse.opentracks.data; + +import android.database.Cursor; + +import androidx.annotation.NonNull; +import androidx.annotation.VisibleForTesting; + +import java.util.NoSuchElementException; + +import de.dennisguse.opentracks.data.models.Marker; +import de.dennisguse.opentracks.data.models.Track; +import de.dennisguse.opentracks.data.models.TrackPoint; +import de.dennisguse.opentracks.util.AutoCloseableIterator; + +class MarkerIterator extends AutoCloseableIterator { + + private static final String TAG = MarkerIterator.class.getSimpleName(); + + private final ContentProviderUtils contentProviderUtils; + private Cursor cursor; + + MarkerIterator(ContentProviderUtils contentProviderUtils, Track.Id trackId, int maxCount) { + this.contentProviderUtils = contentProviderUtils; + + cursor = contentProviderUtils.getMarkerCursor(trackId, maxCount); + } + + @Override + public boolean hasNext() { + if (cursor == null) { + return false; + } + return !cursor.isLast() && !cursor.isAfterLast(); + } + + @Override + @NonNull + public Marker next() { + if (cursor == null || !cursor.moveToNext()) { + throw new NoSuchElementException(); + } + return contentProviderUtils.createMarker(cursor); + } + + @Override + public int getPosition() { + return cursor.getPosition(); + } + + @VisibleForTesting + public int getCount() { + return cursor.getCount(); + } + + @Override + public void close() { + if (cursor != null) { + cursor.close(); + cursor = null; + } + } + + @Override + public void remove() { + throw new UnsupportedOperationException(); + } +} \ No newline at end of file diff --git a/src/main/java/de/dennisguse/opentracks/data/TrackPointIterator.java b/src/main/java/de/dennisguse/opentracks/data/TrackPointIterator.java index 8dfdaeaae..f0090dc37 100644 --- a/src/main/java/de/dennisguse/opentracks/data/TrackPointIterator.java +++ b/src/main/java/de/dennisguse/opentracks/data/TrackPointIterator.java @@ -17,16 +17,10 @@ import de.dennisguse.opentracks.util.AutoCloseableIterator; class TrackPointIterator extends AutoCloseableIterator { private static final String TAG = TrackPointIterator.class.getSimpleName(); - - private final ContentProviderUtils contentProviderUtils; - private final Track.Id trackId; private final CachedTrackPointsIndexes indexes; private Cursor cursor; TrackPointIterator(ContentProviderUtils contentProviderUtils, Track.Id trackId, TrackPoint.Id startTrackPointId) { - this.contentProviderUtils = contentProviderUtils; - this.trackId = trackId; - cursor = contentProviderUtils.getTrackPointCursor(trackId, startTrackPointId); indexes = new CachedTrackPointsIndexes(cursor); } diff --git a/src/main/java/de/dennisguse/opentracks/io/file/exporter/GPXTrackExporter.java b/src/main/java/de/dennisguse/opentracks/io/file/exporter/GPXTrackExporter.java index f574da481..e8eab9897 100644 --- a/src/main/java/de/dennisguse/opentracks/io/file/exporter/GPXTrackExporter.java +++ b/src/main/java/de/dennisguse/opentracks/io/file/exporter/GPXTrackExporter.java @@ -238,17 +238,12 @@ public class GPXTrackExporter implements TrackExporter { } private void writeMarkers(Track track) throws InterruptedException { - try (Cursor cursor = contentProviderUtils.getMarkerCursor(track.getId())) { - if (cursor != null && cursor.moveToFirst()) { - for (int i = 0; i < cursor.getCount(); i++) { - if (Thread.interrupted()) { - throw new InterruptedException(); - } - Marker marker = contentProviderUtils.createMarker(cursor); - writeMarker(track.getZoneOffset(), marker); + try (AutoCloseableIterator markerIterator = contentProviderUtils.getMarkerIterator(track.getId())) { + while (markerIterator.hasNext()) { + if (Thread.interrupted()) throw new InterruptedException(); - cursor.moveToNext(); - } + + writeMarker(track.getZoneOffset(), markerIterator.next()); } } } diff --git a/src/main/java/de/dennisguse/opentracks/io/file/exporter/KMLTrackExporter.java b/src/main/java/de/dennisguse/opentracks/io/file/exporter/KMLTrackExporter.java index 0b3e21933..4db74e4e5 100644 --- a/src/main/java/de/dennisguse/opentracks/io/file/exporter/KMLTrackExporter.java +++ b/src/main/java/de/dennisguse/opentracks/io/file/exporter/KMLTrackExporter.java @@ -136,21 +136,16 @@ public class KMLTrackExporter implements TrackExporter { private void writeMarkers(Track track) throws InterruptedException { boolean hasMarkers = false; - try (Cursor cursor = contentProviderUtils.getMarkerCursor(track.getId())) { - if (cursor != null && cursor.moveToFirst()) { - for (int i = 0; i < cursor.getCount(); i++) { - if (Thread.interrupted()) { - throw new InterruptedException(); - } - if (!hasMarkers) { - writeBeginMarkers(track); - hasMarkers = true; - } - Marker marker = contentProviderUtils.createMarker(cursor); - writeMarker(marker, track.getZoneOffset()); + try (AutoCloseableIterator markerIterator = contentProviderUtils.getMarkerIterator(track.getId())) { + while (markerIterator.hasNext()) { + if (Thread.interrupted()) throw new InterruptedException(); - cursor.moveToNext(); + if (!hasMarkers) { + writeBeginMarkers(track); + hasMarkers = true; } + + writeMarker(markerIterator.next(), track.getZoneOffset()); } } if (hasMarkers) { diff --git a/src/main/java/de/dennisguse/opentracks/io/file/exporter/KMZTrackExporter.java b/src/main/java/de/dennisguse/opentracks/io/file/exporter/KMZTrackExporter.java index 10f7798f4..872c56073 100644 --- a/src/main/java/de/dennisguse/opentracks/io/file/exporter/KMZTrackExporter.java +++ b/src/main/java/de/dennisguse/opentracks/io/file/exporter/KMZTrackExporter.java @@ -36,6 +36,7 @@ import de.dennisguse.opentracks.data.ContentProviderUtils; import de.dennisguse.opentracks.data.models.Marker; import de.dennisguse.opentracks.data.models.Track; import de.dennisguse.opentracks.ui.markers.MarkerUtils; +import de.dennisguse.opentracks.util.AutoCloseableIterator; import de.dennisguse.opentracks.util.FileUtils; /** @@ -89,22 +90,17 @@ public class KMZTrackExporter implements TrackExporter { private void addImages(Context context, List tracks, ZipOutputStream zipOutputStream) throws InterruptedException, IOException { for (Track track : tracks) { - try (Cursor cursor = contentProviderUtils.getMarkerCursor(track.getId())) { - if (cursor != null && cursor.moveToFirst()) { - for (int i = 0; i < cursor.getCount(); i++) { - if (Thread.interrupted()) { - throw new InterruptedException(); - } - Marker marker = contentProviderUtils.createMarker(cursor); - if (marker.hasPhoto()) { - Uri uriPhoto = marker.getPhotoUrl(); - boolean existsPhoto = MarkerUtils.buildInternalPhotoFile(context, track.getId(), uriPhoto) != null; - if (existsPhoto) { - addImage(context, zipOutputStream, uriPhoto, marker); - } - } + try (AutoCloseableIterator markerIterator = contentProviderUtils.getMarkerIterator(track.getId())) { + while (markerIterator.hasNext()) { + if (Thread.interrupted()) throw new InterruptedException(); - cursor.moveToNext(); + Marker marker = markerIterator.next(); + if (marker.hasPhoto()) { + Uri uriPhoto = marker.getPhotoUrl(); + boolean existsPhoto = MarkerUtils.buildInternalPhotoFile(context, track.getId(), uriPhoto) != null; + if (existsPhoto) { + addImage(context, zipOutputStream, uriPhoto, marker); + } } } } diff --git a/src/main/java/de/dennisguse/opentracks/io/file/importer/KMZTrackImporter.java b/src/main/java/de/dennisguse/opentracks/io/file/importer/KMZTrackImporter.java index 78bde1bc9..5d3441f89 100644 --- a/src/main/java/de/dennisguse/opentracks/io/file/importer/KMZTrackImporter.java +++ b/src/main/java/de/dennisguse/opentracks/io/file/importer/KMZTrackImporter.java @@ -31,6 +31,7 @@ import java.io.IOException; import java.io.InputStream; import java.util.ArrayList; import java.util.List; +import java.util.function.Consumer; import java.util.zip.ZipEntry; import java.util.zip.ZipInputStream; @@ -38,6 +39,7 @@ import de.dennisguse.opentracks.R; import de.dennisguse.opentracks.data.ContentProviderUtils; import de.dennisguse.opentracks.data.models.Marker; import de.dennisguse.opentracks.data.models.Track; +import de.dennisguse.opentracks.util.AutoCloseableIterator; import de.dennisguse.opentracks.util.FileUtils; /** @@ -188,13 +190,14 @@ public class KMZTrackImporter { private void deleteOrphanImages(Track.Id trackId) { // 1.- Gets all photo names in the markers of the track identified by id. ContentProviderUtils contentProviderUtils = new ContentProviderUtils(context); - List markers = contentProviderUtils.getMarkers(trackId); List photosName = new ArrayList<>(); - for (Marker marker : markers) { - if (marker.hasPhoto()) { - String photoUrl = Uri.decode(marker.getPhotoUrl().toString()); //TODO Why Uri.decode()? - photosName.add(photoUrl.substring(photoUrl.lastIndexOf(File.separatorChar) + 1)); - } + try (AutoCloseableIterator markerIterator = new ContentProviderUtils(context).getMarkerIterator(trackId)) { + markerIterator.forEachRemaining(marker -> { + if (marker.hasPhoto()) { + String photoUrl = Uri.decode(marker.getPhotoUrl().toString()); //TODO Why Uri.decode()? + photosName.add(photoUrl.substring(photoUrl.lastIndexOf(File.separatorChar) + 1)); + } + }); } // 2.- Deletes all orphan photos from external storage.