Cleanup: AutoCloseableIterator now handles the cursor.

This commit is contained in:
Dennis Guse
2025-12-04 17:09:31 +01:00
parent 12f0c3313f
commit d49d6c9857
19 changed files with 105 additions and 236 deletions
@@ -13,6 +13,8 @@ import java.util.List;
import java.util.UUID; import java.util.UUID;
import de.dennisguse.opentracks.data.ContentProviderUtils; import de.dennisguse.opentracks.data.ContentProviderUtils;
import de.dennisguse.opentracks.data.MarkerIterator;
import de.dennisguse.opentracks.data.TrackPointIterator;
import de.dennisguse.opentracks.data.models.ActivityType; import de.dennisguse.opentracks.data.models.ActivityType;
import de.dennisguse.opentracks.data.models.Altitude; import de.dennisguse.opentracks.data.models.Altitude;
import de.dennisguse.opentracks.data.models.Distance; import de.dennisguse.opentracks.data.models.Distance;
@@ -23,7 +25,6 @@ import de.dennisguse.opentracks.data.models.Statistics;
import de.dennisguse.opentracks.data.models.Track; import de.dennisguse.opentracks.data.models.Track;
import de.dennisguse.opentracks.data.models.TrackPoint; import de.dennisguse.opentracks.data.models.TrackPoint;
import de.dennisguse.opentracks.ui.markers.MarkerUtils; import de.dennisguse.opentracks.ui.markers.MarkerUtils;
import de.dennisguse.opentracks.util.AutoCloseableIterator;
import de.dennisguse.opentracks.util.FileUtils; import de.dennisguse.opentracks.util.FileUtils;
public class TestDataUtil { public class TestDataUtil {
@@ -168,7 +169,7 @@ public class TestDataUtil {
} }
public static List<TrackPoint> getTrackPoints(ContentProviderUtils contentProviderUtils, Track.Id trackId) { public static List<TrackPoint> getTrackPoints(ContentProviderUtils contentProviderUtils, Track.Id trackId) {
try (AutoCloseableIterator<TrackPoint> trackPointIterator = contentProviderUtils.getTrackPointIterator(trackId, null)) { try (TrackPointIterator trackPointIterator = contentProviderUtils.getTrackPointIterator(trackId, null)) {
ArrayList<TrackPoint> trackPoints = new ArrayList<>(); ArrayList<TrackPoint> trackPoints = new ArrayList<>();
trackPointIterator.forEachRemaining(trackPoints::add); trackPointIterator.forEachRemaining(trackPoints::add);
return trackPoints; return trackPoints;
@@ -176,7 +177,7 @@ public class TestDataUtil {
} }
public static List<Marker> getMarkers(ContentProviderUtils contentProviderUtils, Track.Id trackId) { public static List<Marker> getMarkers(ContentProviderUtils contentProviderUtils, Track.Id trackId) {
try (AutoCloseableIterator<Marker> trackPointIterator = contentProviderUtils.getMarkerIterator(trackId)) { try (MarkerIterator trackPointIterator = contentProviderUtils.getMarkerIterator(trackId)) {
ArrayList<Marker> markers = new ArrayList<>(); ArrayList<Marker> markers = new ArrayList<>();
trackPointIterator.forEachRemaining(markers::add); trackPointIterator.forEachRemaining(markers::add);
return markers; return markers;
@@ -67,7 +67,6 @@ import de.dennisguse.opentracks.data.tables.MarkerColumns;
import de.dennisguse.opentracks.data.tables.TrackPointsColumns; import de.dennisguse.opentracks.data.tables.TrackPointsColumns;
import de.dennisguse.opentracks.data.tables.TracksColumns; import de.dennisguse.opentracks.data.tables.TracksColumns;
import de.dennisguse.opentracks.stats.SensorStatistics; import de.dennisguse.opentracks.stats.SensorStatistics;
import de.dennisguse.opentracks.util.AutoCloseableIterator;
import de.dennisguse.opentracks.util.FileUtils; import de.dennisguse.opentracks.util.FileUtils;
/** /**
@@ -119,7 +118,7 @@ public class CustomContentProviderUtilsTest {
private void testIterator(Track.Id trackId, int numPoints) { private void testIterator(Track.Id trackId, int numPoints) {
TrackPoint.Id lastPointId = initializeTrack(trackId, numPoints); TrackPoint.Id lastPointId = initializeTrack(trackId, numPoints);
List<TrackPoint> locations = new ArrayList<>(numPoints); List<TrackPoint> locations = new ArrayList<>(numPoints);
try (AutoCloseableIterator<TrackPoint> it = contentProviderUtils.getTrackPointIterator(trackId, null)) { try (TrackPointIterator it = contentProviderUtils.getTrackPointIterator(trackId, null)) {
while (it.hasNext()) { while (it.hasNext()) {
TrackPoint trackPoint = it.next(); TrackPoint trackPoint = it.next();
assertNotNull(trackPoint); assertNotNull(trackPoint);
@@ -165,7 +164,7 @@ public class CustomContentProviderUtilsTest {
// Load all inserted trackPoints. // Load all inserted trackPoints.
TrackPoint.Id lastPointId = null; TrackPoint.Id lastPointId = null;
int counter = 0; int counter = 0;
try (AutoCloseableIterator<TrackPoint> it = contentProviderUtils.getTrackPointIterator(id, null)) { try (TrackPointIterator it = contentProviderUtils.getTrackPointIterator(id, null)) {
while (it.hasNext()) { while (it.hasNext()) {
TrackPoint trackPoint = it.next(); TrackPoint trackPoint = it.next();
lastPointId = trackPoint.getId(); lastPointId = trackPoint.getId();
@@ -852,7 +851,7 @@ public class CustomContentProviderUtilsTest {
TrackPoint.Id startTrackPointId = trackpointIds.get(0); TrackPoint.Id startTrackPointId = trackpointIds.get(0);
// when // when
AutoCloseableIterator<TrackPoint> trackPointIterator = contentProviderUtils.getTrackPointIterator(trackId, trackpointIds.get(0)); TrackPointIterator trackPointIterator = contentProviderUtils.getTrackPointIterator(trackId, trackpointIds.get(0));
// then // then
for (int i = 0; i < trackpointIds.size(); i++) { for (int i = 0; i < trackpointIds.size(); i++) {
@@ -1210,7 +1209,7 @@ public class CustomContentProviderUtilsTest {
} }
private TrackPoint getLastValidTrackPoint(Track.Id trackId) { private TrackPoint getLastValidTrackPoint(Track.Id trackId) {
try (AutoCloseableIterator<TrackPoint> trackPointsCursor = contentProviderUtils.getTrackPointIterator(trackId, null)) { try (TrackPointIterator trackPointsCursor = contentProviderUtils.getTrackPointIterator(trackId, null)) {
List<TrackPoint> trackpoints = new ArrayList<>(); List<TrackPoint> trackpoints = new ArrayList<>();
while (trackPointsCursor.hasNext()) while (trackPointsCursor.hasNext())
trackpoints.add(trackPointsCursor.next()); trackpoints.add(trackPointsCursor.next());
@@ -45,7 +45,6 @@ import java.util.Arrays;
import java.util.Objects; import java.util.Objects;
import de.dennisguse.opentracks.data.ContentProviderUtils; import de.dennisguse.opentracks.data.ContentProviderUtils;
import de.dennisguse.opentracks.data.TrackListIterator;
import de.dennisguse.opentracks.data.models.Track; import de.dennisguse.opentracks.data.models.Track;
import de.dennisguse.opentracks.databinding.TrackListBinding; import de.dennisguse.opentracks.databinding.TrackListBinding;
import de.dennisguse.opentracks.sensors.GpsStatusValue; import de.dennisguse.opentracks.sensors.GpsStatusValue;
@@ -63,7 +62,6 @@ import de.dennisguse.opentracks.ui.aggregatedStatistics.ConfirmDeleteDialogFragm
import de.dennisguse.opentracks.ui.markers.MarkerListActivity; import de.dennisguse.opentracks.ui.markers.MarkerListActivity;
import de.dennisguse.opentracks.ui.util.ActivityUtils; import de.dennisguse.opentracks.ui.util.ActivityUtils;
import de.dennisguse.opentracks.ui.util.RecyclerViewSwipeDeleteCallback; import de.dennisguse.opentracks.ui.util.RecyclerViewSwipeDeleteCallback;
import de.dennisguse.opentracks.util.AutoCloseableIterator;
import de.dennisguse.opentracks.util.IntentDashboardUtils; import de.dennisguse.opentracks.util.IntentDashboardUtils;
import de.dennisguse.opentracks.util.IntentUtils; import de.dennisguse.opentracks.util.IntentUtils;
import de.dennisguse.opentracks.util.PermissionRequester; import de.dennisguse.opentracks.util.PermissionRequester;
@@ -312,9 +310,7 @@ public class TrackListActivity extends AbstractTrackDeleteActivity implements Co
viewBinding.trackListToolbar.setTitle(Objects.requireNonNullElseGet(searchQuery, () -> getString(R.string.app_name))); viewBinding.trackListToolbar.setTitle(Objects.requireNonNullElseGet(searchQuery, () -> getString(R.string.app_name)));
AutoCloseableIterator<TrackListIterator.Item> tracks = new ContentProviderUtils(this).searchTracks(searchQuery); adapter.swapData(new ContentProviderUtils(this).searchTracks(searchQuery));
adapter.swapData(tracks);
} }
@Override @Override
@@ -56,7 +56,6 @@ import de.dennisguse.opentracks.data.tables.TrackPointsColumns;
import de.dennisguse.opentracks.data.tables.TracksColumns; import de.dennisguse.opentracks.data.tables.TracksColumns;
import de.dennisguse.opentracks.stats.SensorStatistics; import de.dennisguse.opentracks.stats.SensorStatistics;
import de.dennisguse.opentracks.ui.markers.MarkerUtils; import de.dennisguse.opentracks.ui.markers.MarkerUtils;
import de.dennisguse.opentracks.util.AutoCloseableIterator;
import de.dennisguse.opentracks.util.FileUtils; import de.dennisguse.opentracks.util.FileUtils;
/** /**
@@ -180,7 +179,7 @@ public class ContentProviderUtils {
return tracks; return tracks;
} }
public AutoCloseableIterator<TrackListIterator.Item> searchTracks(String searchQuery) { public TrackListIterator searchTracks(String searchQuery) {
// Needed, because MARKER_COUNT is a virtual column and has to be explicitly requested. // Needed, because MARKER_COUNT is a virtual column and has to be explicitly requested.
final String[] PROJECTION = new String[]{ final String[] PROJECTION = new String[]{
TracksColumns._ID, TracksColumns._ID,
@@ -401,11 +400,11 @@ public class ContentProviderUtils {
return null; return null;
} }
public AutoCloseableIterator<Marker> getMarkerIterator(@NonNull Track.Id trackId) { public MarkerIterator getMarkerIterator(@NonNull Track.Id trackId) {
return new MarkerIterator(this, trackId, -1); return new MarkerIterator(this, trackId, -1);
} }
public AutoCloseableIterator<Marker> getMarkerIterator(@NonNull Track.Id trackId, int maxCount) { public MarkerIterator getMarkerIterator(@NonNull Track.Id trackId, int maxCount) {
return new MarkerIterator(this, trackId, maxCount); return new MarkerIterator(this, trackId, maxCount);
} }
@@ -674,7 +673,7 @@ public class ContentProviderUtils {
* @param trackId the track id * @param trackId the track id
* @param startTrackPointId the starting trackPoint id. `null` to ignore * @param startTrackPointId the starting trackPoint id. `null` to ignore
*/ */
public AutoCloseableIterator<TrackPoint> getTrackPointIterator(Track.Id trackId, TrackPoint.Id startTrackPointId) { public TrackPointIterator getTrackPointIterator(Track.Id trackId, TrackPoint.Id startTrackPointId) {
return new TrackPointIterator(this, trackId, startTrackPointId); return new TrackPointIterator(this, trackId, startTrackPointId);
} }
@@ -3,9 +3,6 @@ package de.dennisguse.opentracks.data;
import android.database.Cursor; import android.database.Cursor;
import androidx.annotation.NonNull; 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.Marker;
import de.dennisguse.opentracks.data.models.Track; import de.dennisguse.opentracks.data.models.Track;
@@ -13,20 +10,16 @@ import de.dennisguse.opentracks.util.AutoCloseableIterator;
public class MarkerIterator extends AutoCloseableIterator<Marker> { public class MarkerIterator extends AutoCloseableIterator<Marker> {
private static final String TAG = MarkerIterator.class.getSimpleName();
private final ContentProviderUtils contentProviderUtils; private final ContentProviderUtils contentProviderUtils;
private Cursor cursor;
MarkerIterator(ContentProviderUtils contentProviderUtils, Cursor cursor) { MarkerIterator(ContentProviderUtils contentProviderUtils, Cursor cursor) {
super(cursor);
this.contentProviderUtils = contentProviderUtils; this.contentProviderUtils = contentProviderUtils;
this.cursor = cursor;
} }
MarkerIterator(ContentProviderUtils contentProviderUtils, Track.Id trackId, int maxCount) { MarkerIterator(ContentProviderUtils contentProviderUtils, Track.Id trackId, int maxCount) {
super(contentProviderUtils.getMarkerCursor(trackId, maxCount));
this.contentProviderUtils = contentProviderUtils; this.contentProviderUtils = contentProviderUtils;
cursor = contentProviderUtils.getMarkerCursor(trackId, maxCount);
} }
@NonNull @NonNull
@@ -34,49 +27,4 @@ public class MarkerIterator extends AutoCloseableIterator<Marker> {
public Marker get() { public Marker get() {
return contentProviderUtils.createMarker(cursor); return contentProviderUtils.createMarker(cursor);
} }
@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 get();
}
@Override
public int getPosition() {
return cursor.getPosition();
}
@Override
public boolean moveToPosition(int position) {
return cursor.moveToPosition(position);
}
@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();
}
} }
@@ -39,7 +39,6 @@ import de.dennisguse.opentracks.services.RecordingStatus;
import de.dennisguse.opentracks.services.TrackRecordingService; import de.dennisguse.opentracks.services.TrackRecordingService;
import de.dennisguse.opentracks.services.handlers.AltitudeCorrectionManager; import de.dennisguse.opentracks.services.handlers.AltitudeCorrectionManager;
import de.dennisguse.opentracks.stats.TrackStatisticsUpdater; import de.dennisguse.opentracks.stats.TrackStatisticsUpdater;
import de.dennisguse.opentracks.util.AutoCloseableIterator;
/** /**
* Track data hub. * Track data hub.
@@ -279,7 +278,7 @@ public class TrackDataHub {
} }
TrackPoint trackPoint = null; TrackPoint trackPoint = null;
try (AutoCloseableIterator<TrackPoint> trackPointIterator = contentProviderUtils.getTrackPointIterator(selectedTrackId, next)) { try (TrackPointIterator trackPointIterator = contentProviderUtils.getTrackPointIterator(selectedTrackId, next)) {
long numTotalPoints = trackPointIterator.getCount(); long numTotalPoints = trackPointIterator.getCount();
int samplingFrequency = 1 + (int) (numTotalPoints / targetNumPoints); int samplingFrequency = 1 + (int) (numTotalPoints / targetNumPoints);
@@ -3,12 +3,10 @@ package de.dennisguse.opentracks.data;
import android.database.Cursor; import android.database.Cursor;
import androidx.annotation.NonNull; import androidx.annotation.NonNull;
import androidx.annotation.VisibleForTesting;
import java.time.Duration; import java.time.Duration;
import java.time.Instant; import java.time.Instant;
import java.time.ZoneOffset; import java.time.ZoneOffset;
import java.util.NoSuchElementException;
import de.dennisguse.opentracks.data.models.ActivityType; import de.dennisguse.opentracks.data.models.ActivityType;
import de.dennisguse.opentracks.data.models.Distance; import de.dennisguse.opentracks.data.models.Distance;
@@ -18,12 +16,8 @@ import de.dennisguse.opentracks.util.AutoCloseableIterator;
public class TrackListIterator extends AutoCloseableIterator<TrackListIterator.Item> { public class TrackListIterator extends AutoCloseableIterator<TrackListIterator.Item> {
private static final String TAG = TrackListIterator.class.getSimpleName();
private Cursor cursor;
TrackListIterator(Cursor cursor) { TrackListIterator(Cursor cursor) {
this.cursor = cursor; super(cursor);
} }
@NonNull @NonNull
@@ -54,51 +48,6 @@ public class TrackListIterator extends AutoCloseableIterator<TrackListIterator.I
); );
} }
@Override
public boolean hasNext() {
if (cursor == null) {
return false;
}
return !cursor.isLast() && !cursor.isAfterLast();
}
@Override
@NonNull
public Item next() {
if (cursor == null || !cursor.moveToNext()) {
throw new NoSuchElementException();
}
return get();
}
@Override
public int getPosition() {
return cursor.getPosition();
}
@Override
public boolean moveToPosition(int position) {
return cursor.moveToPosition(position);
}
@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();
}
public record Item( public record Item(
@NonNull @NonNull
Track.Id id, Track.Id id,
@@ -128,7 +77,6 @@ public class TrackListIterator extends AutoCloseableIterator<TrackListIterator.I
Distance totalDistance, Distance totalDistance,
int markerCount int markerCount
) { ) {
} }
} }
@@ -1,78 +1,23 @@
package de.dennisguse.opentracks.data; package de.dennisguse.opentracks.data;
import android.database.Cursor;
import androidx.annotation.NonNull; import androidx.annotation.NonNull;
import androidx.annotation.VisibleForTesting;
import java.util.NoSuchElementException;
import de.dennisguse.opentracks.data.models.Track; import de.dennisguse.opentracks.data.models.Track;
import de.dennisguse.opentracks.data.models.TrackPoint; import de.dennisguse.opentracks.data.models.TrackPoint;
import de.dennisguse.opentracks.util.AutoCloseableIterator; import de.dennisguse.opentracks.util.AutoCloseableIterator;
/** public class TrackPointIterator extends AutoCloseableIterator<TrackPoint> {
* A lightweight wrapper around the original {@link Cursor}.
*/
class TrackPointIterator extends AutoCloseableIterator<TrackPoint> {
private static final String TAG = TrackPointIterator.class.getSimpleName();
private final CachedTrackPointsIndexes indexes; private final CachedTrackPointsIndexes indexes;
private Cursor cursor;
TrackPointIterator(ContentProviderUtils contentProviderUtils, Track.Id trackId, TrackPoint.Id startTrackPointId) { TrackPointIterator(ContentProviderUtils contentProviderUtils, Track.Id trackId, TrackPoint.Id startTrackPointId) {
cursor = contentProviderUtils.getTrackPointCursor(trackId, startTrackPointId); super(contentProviderUtils.getTrackPointCursor(trackId, startTrackPointId));
indexes = new CachedTrackPointsIndexes(cursor); indexes = new CachedTrackPointsIndexes(cursor);
} }
@Override
public boolean hasNext() {
if (cursor == null) {
return false;
}
return !cursor.isLast() && !cursor.isAfterLast();
}
@Override @Override
@NonNull @NonNull
public TrackPoint get() { public TrackPoint get() {
return ContentProviderUtils.fillTrackPoint(cursor, indexes); return ContentProviderUtils.fillTrackPoint(cursor, indexes);
} }
@Override
@NonNull
public TrackPoint next() {
if (cursor == null || !cursor.moveToNext()) {
throw new NoSuchElementException();
}
return get();
}
@Override
public int getPosition() {
return cursor.getPosition();
}
@Override
public boolean moveToPosition(int position) {
return cursor.moveToPosition(position);
}
@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();
}
} }
@@ -28,10 +28,10 @@ import java.util.Locale;
import java.util.function.Function; import java.util.function.Function;
import de.dennisguse.opentracks.data.ContentProviderUtils; import de.dennisguse.opentracks.data.ContentProviderUtils;
import de.dennisguse.opentracks.data.TrackPointIterator;
import de.dennisguse.opentracks.data.models.Marker; import de.dennisguse.opentracks.data.models.Marker;
import de.dennisguse.opentracks.data.models.Track; import de.dennisguse.opentracks.data.models.Track;
import de.dennisguse.opentracks.data.models.TrackPoint; import de.dennisguse.opentracks.data.models.TrackPoint;
import de.dennisguse.opentracks.util.AutoCloseableIterator;
import de.dennisguse.opentracks.util.StringUtils; import de.dennisguse.opentracks.util.StringUtils;
/** /**
@@ -131,7 +131,7 @@ public class CSVTrackExporter implements TrackExporter {
} }
private void writeTrackPoints(List<Column> columns, Track track) throws InterruptedException { private void writeTrackPoints(List<Column> columns, Track track) throws InterruptedException {
try (AutoCloseableIterator<TrackPoint> trackPointIterator = contentProviderUtils.getTrackPointIterator(track.id(), null)) { try (TrackPointIterator trackPointIterator = contentProviderUtils.getTrackPointIterator(track.id(), null)) {
while (trackPointIterator.hasNext()) { while (trackPointIterator.hasNext()) {
if (Thread.interrupted()) throw new InterruptedException(); if (Thread.interrupted()) throw new InterruptedException();
@@ -33,13 +33,14 @@ import java.util.function.Function;
import java.util.stream.Stream; import java.util.stream.Stream;
import de.dennisguse.opentracks.data.ContentProviderUtils; import de.dennisguse.opentracks.data.ContentProviderUtils;
import de.dennisguse.opentracks.data.MarkerIterator;
import de.dennisguse.opentracks.data.TrackPointIterator;
import de.dennisguse.opentracks.data.models.Distance; import de.dennisguse.opentracks.data.models.Distance;
import de.dennisguse.opentracks.data.models.Marker; import de.dennisguse.opentracks.data.models.Marker;
import de.dennisguse.opentracks.data.models.Position; import de.dennisguse.opentracks.data.models.Position;
import de.dennisguse.opentracks.data.models.Statistics; import de.dennisguse.opentracks.data.models.Statistics;
import de.dennisguse.opentracks.data.models.Track; import de.dennisguse.opentracks.data.models.Track;
import de.dennisguse.opentracks.data.models.TrackPoint; import de.dennisguse.opentracks.data.models.TrackPoint;
import de.dennisguse.opentracks.util.AutoCloseableIterator;
import de.dennisguse.opentracks.util.StringUtils; import de.dennisguse.opentracks.util.StringUtils;
/** /**
@@ -128,7 +129,7 @@ public class GPXTrackExporter implements TrackExporter {
LinkedList<TrackPoint> sensorPoints = new LinkedList<>(); LinkedList<TrackPoint> sensorPoints = new LinkedList<>();
try (AutoCloseableIterator<TrackPoint> trackPointIterator = contentProviderUtils.getTrackPointIterator(track.id(), null)) { try (TrackPointIterator trackPointIterator = contentProviderUtils.getTrackPointIterator(track.id(), null)) {
while (trackPointIterator.hasNext()) { while (trackPointIterator.hasNext()) {
if (Thread.interrupted()) throw new InterruptedException(); if (Thread.interrupted()) throw new InterruptedException();
@@ -238,7 +239,7 @@ public class GPXTrackExporter implements TrackExporter {
} }
private void writeMarkers(Track track) throws InterruptedException { private void writeMarkers(Track track) throws InterruptedException {
try (AutoCloseableIterator<Marker> markerIterator = contentProviderUtils.getMarkerIterator(track.id())) { try (MarkerIterator markerIterator = contentProviderUtils.getMarkerIterator(track.id())) {
while (markerIterator.hasNext()) { while (markerIterator.hasNext()) {
if (Thread.interrupted()) throw new InterruptedException(); if (Thread.interrupted()) throw new InterruptedException();
@@ -35,13 +35,14 @@ import java.util.Objects;
import de.dennisguse.opentracks.R; import de.dennisguse.opentracks.R;
import de.dennisguse.opentracks.data.ContentProviderUtils; import de.dennisguse.opentracks.data.ContentProviderUtils;
import de.dennisguse.opentracks.data.MarkerIterator;
import de.dennisguse.opentracks.data.TrackPointIterator;
import de.dennisguse.opentracks.data.models.ActivityType; import de.dennisguse.opentracks.data.models.ActivityType;
import de.dennisguse.opentracks.data.models.Marker; import de.dennisguse.opentracks.data.models.Marker;
import de.dennisguse.opentracks.data.models.Position; import de.dennisguse.opentracks.data.models.Position;
import de.dennisguse.opentracks.data.models.Track; import de.dennisguse.opentracks.data.models.Track;
import de.dennisguse.opentracks.data.models.TrackPoint; import de.dennisguse.opentracks.data.models.TrackPoint;
import de.dennisguse.opentracks.ui.markers.MarkerUtils; import de.dennisguse.opentracks.ui.markers.MarkerUtils;
import de.dennisguse.opentracks.util.AutoCloseableIterator;
import de.dennisguse.opentracks.util.StringUtils; import de.dennisguse.opentracks.util.StringUtils;
/** /**
@@ -135,7 +136,7 @@ public class KMLTrackExporter implements TrackExporter {
private void writeMarkers(Track track) throws InterruptedException { private void writeMarkers(Track track) throws InterruptedException {
boolean hasMarkers = false; boolean hasMarkers = false;
try (AutoCloseableIterator<Marker> markerIterator = contentProviderUtils.getMarkerIterator(track.id())) { try (MarkerIterator markerIterator = contentProviderUtils.getMarkerIterator(track.id())) {
while (markerIterator.hasNext()) { while (markerIterator.hasNext()) {
if (Thread.interrupted()) throw new InterruptedException(); if (Thread.interrupted()) throw new InterruptedException();
@@ -156,7 +157,7 @@ public class KMLTrackExporter implements TrackExporter {
boolean wroteTrack = false; boolean wroteTrack = false;
boolean wroteSegment = false; boolean wroteSegment = false;
try (AutoCloseableIterator<TrackPoint> trackPointIterator = contentProviderUtils.getTrackPointIterator(track.id(), null)) { try (TrackPointIterator trackPointIterator = contentProviderUtils.getTrackPointIterator(track.id(), null)) {
while (trackPointIterator.hasNext()) { while (trackPointIterator.hasNext()) {
if (Thread.interrupted()) throw new InterruptedException(); if (Thread.interrupted()) throw new InterruptedException();
@@ -32,10 +32,10 @@ import java.util.zip.ZipEntry;
import java.util.zip.ZipOutputStream; import java.util.zip.ZipOutputStream;
import de.dennisguse.opentracks.data.ContentProviderUtils; import de.dennisguse.opentracks.data.ContentProviderUtils;
import de.dennisguse.opentracks.data.MarkerIterator;
import de.dennisguse.opentracks.data.models.Marker; import de.dennisguse.opentracks.data.models.Marker;
import de.dennisguse.opentracks.data.models.Track; import de.dennisguse.opentracks.data.models.Track;
import de.dennisguse.opentracks.ui.markers.MarkerUtils; import de.dennisguse.opentracks.ui.markers.MarkerUtils;
import de.dennisguse.opentracks.util.AutoCloseableIterator;
import de.dennisguse.opentracks.util.FileUtils; import de.dennisguse.opentracks.util.FileUtils;
/** /**
@@ -89,7 +89,7 @@ public class KMZTrackExporter implements TrackExporter {
private void addImages(Context context, List<Track> tracks, ZipOutputStream zipOutputStream) throws InterruptedException, IOException { private void addImages(Context context, List<Track> tracks, ZipOutputStream zipOutputStream) throws InterruptedException, IOException {
for (Track track : tracks) { for (Track track : tracks) {
try (AutoCloseableIterator<Marker> markerIterator = contentProviderUtils.getMarkerIterator(track.id())) { try (MarkerIterator markerIterator = contentProviderUtils.getMarkerIterator(track.id())) {
while (markerIterator.hasNext()) { while (markerIterator.hasNext()) {
if (Thread.interrupted()) throw new InterruptedException(); if (Thread.interrupted()) throw new InterruptedException();
@@ -35,9 +35,8 @@ import java.util.zip.ZipInputStream;
import de.dennisguse.opentracks.R; import de.dennisguse.opentracks.R;
import de.dennisguse.opentracks.data.ContentProviderUtils; import de.dennisguse.opentracks.data.ContentProviderUtils;
import de.dennisguse.opentracks.data.models.Marker; import de.dennisguse.opentracks.data.MarkerIterator;
import de.dennisguse.opentracks.data.models.Track; import de.dennisguse.opentracks.data.models.Track;
import de.dennisguse.opentracks.util.AutoCloseableIterator;
import de.dennisguse.opentracks.util.FileUtils; import de.dennisguse.opentracks.util.FileUtils;
/** /**
@@ -189,7 +188,7 @@ public class KMZTrackImporter {
// 1.- Gets all photo names in the markers of the track identified by id. // 1.- Gets all photo names in the markers of the track identified by id.
ContentProviderUtils contentProviderUtils = new ContentProviderUtils(context); ContentProviderUtils contentProviderUtils = new ContentProviderUtils(context);
List<String> photosName = new ArrayList<>(); List<String> photosName = new ArrayList<>();
try (AutoCloseableIterator<Marker> markerIterator = new ContentProviderUtils(context).getMarkerIterator(trackId)) { try (MarkerIterator markerIterator = new ContentProviderUtils(context).getMarkerIterator(trackId)) {
markerIterator.forEachRemaining(marker -> { markerIterator.forEachRemaining(marker -> {
if (marker.photoUrl() != null) { if (marker.photoUrl() != null) {
String photoUrl = Uri.decode(marker.photoUrl().toString()); //TODO Why Uri.decode()? String photoUrl = Uri.decode(marker.photoUrl().toString()); //TODO Why Uri.decode()?
@@ -13,6 +13,7 @@ import java.time.Instant;
import java.time.ZoneOffset; import java.time.ZoneOffset;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Collections; import java.util.Collections;
import java.util.Comparator;
import java.util.LinkedList; import java.util.LinkedList;
import java.util.List; import java.util.List;
import java.util.Objects; import java.util.Objects;
@@ -134,15 +135,7 @@ public class TrackImporter {
trackData.uuid = UUID.randomUUID(); trackData.uuid = UUID.randomUUID();
} }
trackPoints.sort((o1, o2) -> { trackPoints.sort(Comparator.comparing(TrackPoint::getTime));
if (o1.getTime().isBefore(o2.getTime())) {
return -1;
}
if (o1.getTime().isAfter(o2.getTime())) {
return 1;
}
return 0;
});
adjustTrackPoints(); adjustTrackPoints();
@@ -30,6 +30,7 @@ import java.util.List;
import de.dennisguse.opentracks.R; import de.dennisguse.opentracks.R;
import de.dennisguse.opentracks.data.ContentProviderUtils; import de.dennisguse.opentracks.data.ContentProviderUtils;
import de.dennisguse.opentracks.data.TrackPointIterator;
import de.dennisguse.opentracks.data.models.Distance; import de.dennisguse.opentracks.data.models.Distance;
import de.dennisguse.opentracks.data.models.Statistics; import de.dennisguse.opentracks.data.models.Statistics;
import de.dennisguse.opentracks.data.models.TrackPoint; import de.dennisguse.opentracks.data.models.TrackPoint;
@@ -37,7 +38,6 @@ import de.dennisguse.opentracks.services.RecordingData;
import de.dennisguse.opentracks.settings.PreferencesUtils; import de.dennisguse.opentracks.settings.PreferencesUtils;
import de.dennisguse.opentracks.stats.SensorStatistics; import de.dennisguse.opentracks.stats.SensorStatistics;
import de.dennisguse.opentracks.ui.intervals.IntervalStatisticsUpdater; import de.dennisguse.opentracks.ui.intervals.IntervalStatisticsUpdater;
import de.dennisguse.opentracks.util.AutoCloseableIterator;
/** /**
* Execute a periodic task on a time or distance schedule. * Execute a periodic task on a time or distance schedule.
@@ -149,7 +149,7 @@ public class VoiceAnnouncementManager implements SharedPreferences.OnSharedPrefe
} }
//TODO Avoid reloading (some) TrackPoints just to announce the last interval. //TODO Avoid reloading (some) TrackPoints just to announce the last interval.
try (AutoCloseableIterator<TrackPoint> trackPointIterator = contentProviderUtils.getTrackPointIterator(data.track().id(), startTrackPointId)) { try (TrackPointIterator trackPointIterator = contentProviderUtils.getTrackPointIterator(data.track().id(), startTrackPointId)) {
startTrackPointId = intervalStatistics.addTrackPoints(trackPointIterator); startTrackPointId = intervalStatistics.addTrackPoints(trackPointIterator);
Statistics lastInterval = intervalStatistics.getLastInterval(); Statistics lastInterval = intervalStatistics.getLastInterval();
SensorStatistics sensorStatistics = null; SensorStatistics sensorStatistics = null;
@@ -27,7 +27,6 @@ import de.dennisguse.opentracks.services.RecordingStatus;
import de.dennisguse.opentracks.settings.UnitSystem; import de.dennisguse.opentracks.settings.UnitSystem;
import de.dennisguse.opentracks.ui.util.ActivityUtils; import de.dennisguse.opentracks.ui.util.ActivityUtils;
import de.dennisguse.opentracks.ui.util.ListItemUtils; import de.dennisguse.opentracks.ui.util.ListItemUtils;
import de.dennisguse.opentracks.util.AutoCloseableIterator;
import de.dennisguse.opentracks.util.IntentUtils; import de.dennisguse.opentracks.util.IntentUtils;
public class TrackListAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolder> implements ActionMode.Callback { public class TrackListAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolder> implements ActionMode.Callback {
@@ -39,7 +38,7 @@ public class TrackListAdapter extends RecyclerView.Adapter<RecyclerView.ViewHold
private final SparseBooleanArray selection = new SparseBooleanArray(); private final SparseBooleanArray selection = new SparseBooleanArray();
private RecordingStatus recordingStatus; private RecordingStatus recordingStatus;
private UnitSystem unitSystem; private UnitSystem unitSystem;
private AutoCloseableIterator<TrackListIterator.Item> cursor; private TrackListIterator cursor;
private boolean selectionMode = false; private boolean selectionMode = false;
private ActivityUtils.ContextualActionModeCallback actionModeCallback; private ActivityUtils.ContextualActionModeCallback actionModeCallback;
private ActionMode actionMode; private ActionMode actionMode;
@@ -85,7 +84,7 @@ public class TrackListAdapter extends RecyclerView.Adapter<RecyclerView.ViewHold
return cursor.getCount(); return cursor.getCount();
} }
public void swapData(AutoCloseableIterator<TrackListIterator.Item> cursor) { public void swapData(TrackListIterator cursor) {
this.cursor = cursor; this.cursor = cursor;
this.notifyDataSetChanged(); this.notifyDataSetChanged();
} }
@@ -176,7 +175,7 @@ public class TrackListAdapter extends RecyclerView.Adapter<RecyclerView.ViewHold
} }
public void bind(AutoCloseableIterator<TrackListIterator.Item> cursor) { public void bind(TrackListIterator cursor) {
TrackListIterator.Item tracksListItem = cursor.get(); TrackListIterator.Item tracksListItem = cursor.get();
trackId = tracksListItem.id(); trackId = tracksListItem.id();
@@ -17,13 +17,13 @@ import java.util.concurrent.Executor;
import java.util.concurrent.Executors; import java.util.concurrent.Executors;
import de.dennisguse.opentracks.data.ContentProviderUtils; import de.dennisguse.opentracks.data.ContentProviderUtils;
import de.dennisguse.opentracks.data.TrackPointIterator;
import de.dennisguse.opentracks.data.models.Distance; import de.dennisguse.opentracks.data.models.Distance;
import de.dennisguse.opentracks.data.models.Statistics; import de.dennisguse.opentracks.data.models.Statistics;
import de.dennisguse.opentracks.data.models.Track; import de.dennisguse.opentracks.data.models.Track;
import de.dennisguse.opentracks.data.models.TrackPoint; import de.dennisguse.opentracks.data.models.TrackPoint;
import de.dennisguse.opentracks.data.tables.TrackPointsColumns; import de.dennisguse.opentracks.data.tables.TrackPointsColumns;
import de.dennisguse.opentracks.settings.UnitSystem; import de.dennisguse.opentracks.settings.UnitSystem;
import de.dennisguse.opentracks.util.AutoCloseableIterator;
/** /**
* This model is used to load intervals for a track. * This model is used to load intervals for a track.
@@ -93,7 +93,7 @@ public class IntervalStatisticsModel extends AndroidViewModel {
private void loadIntervalStatistics(Track.Id trackId) { private void loadIntervalStatistics(Track.Id trackId) {
executor.execute(() -> { executor.execute(() -> {
ContentProviderUtils contentProviderUtils = new ContentProviderUtils(getApplication()); ContentProviderUtils contentProviderUtils = new ContentProviderUtils(getApplication());
try (AutoCloseableIterator<TrackPoint> trackPointIterator = contentProviderUtils.getTrackPointIterator(trackId, lastTrackPointId)) { try (TrackPointIterator trackPointIterator = contentProviderUtils.getTrackPointIterator(trackId, lastTrackPointId)) {
lastTrackPointId = intervalStatistics.addTrackPoints(trackPointIterator); lastTrackPointId = intervalStatistics.addTrackPoints(trackPointIterator);
intervalsLiveData.postValue(intervalStatistics.getIntervalList()); intervalsLiveData.postValue(intervalStatistics.getIntervalList());
} }
@@ -30,10 +30,10 @@ import androidx.viewpager2.widget.ViewPager2;
import de.dennisguse.opentracks.AbstractActivity; import de.dennisguse.opentracks.AbstractActivity;
import de.dennisguse.opentracks.R; import de.dennisguse.opentracks.R;
import de.dennisguse.opentracks.data.ContentProviderUtils; import de.dennisguse.opentracks.data.ContentProviderUtils;
import de.dennisguse.opentracks.data.MarkerIterator;
import de.dennisguse.opentracks.data.models.Marker; import de.dennisguse.opentracks.data.models.Marker;
import de.dennisguse.opentracks.databinding.MarkerDetailActivityBinding; import de.dennisguse.opentracks.databinding.MarkerDetailActivityBinding;
import de.dennisguse.opentracks.ui.markers.DeleteMarkerDialogFragment.DeleteMarkerCaller; import de.dennisguse.opentracks.ui.markers.DeleteMarkerDialogFragment.DeleteMarkerCaller;
import de.dennisguse.opentracks.util.AutoCloseableIterator;
/** /**
* An activity to display marker detail info. * An activity to display marker detail info.
@@ -50,7 +50,7 @@ public class MarkerDetailActivity extends AbstractActivity implements DeleteMark
private MarkerDetailActivityBinding viewBinding; private MarkerDetailActivityBinding viewBinding;
private AutoCloseableIterator<Marker> markerAutoCloseableIterator; private MarkerIterator markerIterator;
@Override @Override
protected void onCreate(Bundle bundle) { protected void onCreate(Bundle bundle) {
@@ -66,15 +66,15 @@ public class MarkerDetailActivity extends AbstractActivity implements DeleteMark
ContentProviderUtils contentProviderUtils = new ContentProviderUtils(this); ContentProviderUtils contentProviderUtils = new ContentProviderUtils(this);
Marker marker = contentProviderUtils.getMarker(markerId); Marker marker = contentProviderUtils.getMarker(markerId);
markerAutoCloseableIterator = contentProviderUtils.getMarkerIterator(marker.trackId()); markerIterator = contentProviderUtils.getMarkerIterator(marker.trackId());
if (markerAutoCloseableIterator == null) { if (markerIterator == null) {
finish(); finish();
} }
int markerIndex = -1; int markerIndex = -1;
while (markerAutoCloseableIterator.hasNext()) { while (markerIterator.hasNext()) {
if (markerId.equals(markerAutoCloseableIterator.next().id())) { if (markerId.equals(markerIterator.next().id())) {
markerIndex = markerAutoCloseableIterator.getPosition(); markerIndex = markerIterator.getPosition();
break; break;
} }
} }
@@ -104,8 +104,8 @@ public class MarkerDetailActivity extends AbstractActivity implements DeleteMark
super.onDestroy(); super.onDestroy();
viewBinding = null; viewBinding = null;
if (markerAutoCloseableIterator != null) markerAutoCloseableIterator.close(); if (markerIterator != null) markerIterator.close();
markerAutoCloseableIterator = null; markerIterator = null;
} }
@Override @Override
@@ -122,18 +122,18 @@ public class MarkerDetailActivity extends AbstractActivity implements DeleteMark
@Override @Override
@NonNull @NonNull
public Fragment createFragment(int position) { public Fragment createFragment(int position) {
markerAutoCloseableIterator.moveToPosition(position); markerIterator.moveToPosition(position);
return MarkerDetailFragment.newInstance(markerAutoCloseableIterator.get(position).id()); return MarkerDetailFragment.newInstance(markerIterator.get(position).id());
} }
@Override @Override
public int getItemCount() { public int getItemCount() {
return markerAutoCloseableIterator.getCount(); return markerIterator.getCount();
} }
@Nullable @Nullable
public CharSequence getPageTitle(int position) { public CharSequence getPageTitle(int position) {
return getString(R.string.marker_detail_title, position + 1, getItemCount(), markerAutoCloseableIterator.get().name()); return getString(R.string.marker_detail_title, position + 1, getItemCount(), markerIterator.get().name());
} }
} }
} }
@@ -1,29 +1,70 @@
package de.dennisguse.opentracks.util; package de.dennisguse.opentracks.util;
import android.database.Cursor;
import androidx.annotation.NonNull; import androidx.annotation.NonNull;
import androidx.annotation.VisibleForTesting;
import java.util.Iterator; import java.util.Iterator;
import java.util.NoSuchElementException;
//TODO All children use a cursor. Move this shared functionality here.
public abstract class AutoCloseableIterator<T> implements Iterator<T>, AutoCloseable { public abstract class AutoCloseableIterator<T> implements Iterator<T>, AutoCloseable {
/** protected Cursor cursor;
* Override: AutoClosable.close() throws Exception.
*/
public abstract void close();
public abstract int getPosition(); public AutoCloseableIterator(Cursor cursor) {
this.cursor = cursor;
}
public abstract boolean moveToPosition(int position); @Override
@NonNull
public final T next() {
if (cursor == null || !cursor.moveToNext()) {
throw new NoSuchElementException();
}
return get();
}
public final int getPosition() {
return cursor.getPosition();
}
public final boolean moveToPosition(int position) {
return cursor.moveToPosition(position);
}
@Override
public final boolean hasNext() {
if (cursor == null) {
return false;
}
return !cursor.isLast() && !cursor.isAfterLast();
}
@NonNull @NonNull
public abstract T get(); public abstract T get();
@NonNull @NonNull
public T get(int position) { public final T get(int position) {
moveToPosition(position); moveToPosition(position);
return get(); return get();
} }
public abstract int getCount(); @VisibleForTesting
public final int getCount() {
return cursor.getCount();
}
@Override
public final void remove() {
throw new UnsupportedOperationException();
}
@Override
public final void close() {
if (cursor != null) {
cursor.close();
cursor = null;
}
}
} }