mirror of
https://codeberg.org/OpenTracksApp/OpenTracks.git
synced 2026-10-06 19:43:06 +02:00
Finalize the new interface, fix bugs and add unit tests.
This commit is contained in:
+95
-47
@@ -40,6 +40,8 @@ import java.util.NoSuchElementException;
|
|||||||
public class MyTracksProviderUtilsImpl implements MyTracksProviderUtils {
|
public class MyTracksProviderUtilsImpl implements MyTracksProviderUtils {
|
||||||
|
|
||||||
private final ContentResolver contentResolver;
|
private final ContentResolver contentResolver;
|
||||||
|
|
||||||
|
private int defaultCursorBatchSize = 2000;
|
||||||
|
|
||||||
public MyTracksProviderUtilsImpl(ContentResolver contentResolver) {
|
public MyTracksProviderUtilsImpl(ContentResolver contentResolver) {
|
||||||
this.contentResolver = contentResolver;
|
this.contentResolver = contentResolver;
|
||||||
@@ -185,55 +187,78 @@ public class MyTracksProviderUtilsImpl implements MyTracksProviderUtils {
|
|||||||
fillLocation(cursor, location);
|
fillLocation(cursor, location);
|
||||||
return location;
|
return location;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A cache of track column indices.
|
||||||
|
*/
|
||||||
|
private static class CachedTrackColumnIndices {
|
||||||
|
public final int idxId;
|
||||||
|
public final int idxLatitude;
|
||||||
|
public final int idxLongitude;
|
||||||
|
public final int idxAltitude;
|
||||||
|
public final int idxTime;
|
||||||
|
public final int idxBearing;
|
||||||
|
public final int idxAccuracy;
|
||||||
|
public final int idxSpeed;
|
||||||
|
public final int idxSensor;
|
||||||
|
|
||||||
@Override
|
public CachedTrackColumnIndices(Cursor cursor) {
|
||||||
public void fillLocation(Cursor cursor, Location location) {
|
idxId = cursor.getColumnIndex(TrackPointsColumns._ID);
|
||||||
|
idxLatitude = cursor.getColumnIndexOrThrow(TrackPointsColumns.LATITUDE);
|
||||||
|
idxLongitude = cursor.getColumnIndexOrThrow(TrackPointsColumns.LONGITUDE);
|
||||||
|
idxAltitude = cursor.getColumnIndexOrThrow(TrackPointsColumns.ALTITUDE);
|
||||||
|
idxTime = cursor.getColumnIndexOrThrow(TrackPointsColumns.TIME);
|
||||||
|
idxBearing = cursor.getColumnIndexOrThrow(TrackPointsColumns.BEARING);
|
||||||
|
idxAccuracy = cursor.getColumnIndexOrThrow(TrackPointsColumns.ACCURACY);
|
||||||
|
idxSpeed = cursor.getColumnIndexOrThrow(TrackPointsColumns.SPEED);
|
||||||
|
idxSensor = cursor.getColumnIndexOrThrow(TrackPointsColumns.SENSOR);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void fillLocation(Cursor cursor, CachedTrackColumnIndices columnIndices,
|
||||||
|
Location location) {
|
||||||
location.reset();
|
location.reset();
|
||||||
|
|
||||||
int idxLatitude = cursor.getColumnIndexOrThrow(TrackPointsColumns.LATITUDE);
|
if (!cursor.isNull(columnIndices.idxLatitude)) {
|
||||||
int idxLongitude =
|
location.setLatitude(1. * cursor.getInt(columnIndices.idxLatitude) / 1E6);
|
||||||
cursor.getColumnIndexOrThrow(TrackPointsColumns.LONGITUDE);
|
|
||||||
int idxAltitude = cursor.getColumnIndexOrThrow(TrackPointsColumns.ALTITUDE);
|
|
||||||
int idxTime = cursor.getColumnIndexOrThrow(TrackPointsColumns.TIME);
|
|
||||||
int idxBearing = cursor.getColumnIndexOrThrow(TrackPointsColumns.BEARING);
|
|
||||||
int idxAccuracy = cursor.getColumnIndexOrThrow(TrackPointsColumns.ACCURACY);
|
|
||||||
int idxSpeed = cursor.getColumnIndexOrThrow(TrackPointsColumns.SPEED);
|
|
||||||
int idxSensor = cursor.getColumnIndexOrThrow(TrackPointsColumns.SENSOR);
|
|
||||||
|
|
||||||
if (!cursor.isNull(idxLatitude)) {
|
|
||||||
location.setLatitude(1. * cursor.getInt(idxLatitude) / 1E6);
|
|
||||||
}
|
}
|
||||||
if (!cursor.isNull(idxLongitude)) {
|
if (!cursor.isNull(columnIndices.idxLongitude)) {
|
||||||
location.setLongitude(1. * cursor.getInt(idxLongitude) / 1E6);
|
location.setLongitude(1. * cursor.getInt(columnIndices.idxLongitude) / 1E6);
|
||||||
}
|
}
|
||||||
if (!cursor.isNull(idxAltitude)) {
|
if (!cursor.isNull(columnIndices.idxAltitude)) {
|
||||||
location.setAltitude(cursor.getFloat(idxAltitude));
|
location.setAltitude(cursor.getFloat(columnIndices.idxAltitude));
|
||||||
}
|
}
|
||||||
if (!cursor.isNull(idxTime)) {
|
if (!cursor.isNull(columnIndices.idxTime)) {
|
||||||
location.setTime(cursor.getLong(idxTime));
|
location.setTime(cursor.getLong(columnIndices.idxTime));
|
||||||
}
|
}
|
||||||
if (!cursor.isNull(idxBearing)) {
|
if (!cursor.isNull(columnIndices.idxBearing)) {
|
||||||
location.setBearing(cursor.getFloat(idxBearing));
|
location.setBearing(cursor.getFloat(columnIndices.idxBearing));
|
||||||
}
|
}
|
||||||
if (!cursor.isNull(idxSpeed)) {
|
if (!cursor.isNull(columnIndices.idxSpeed)) {
|
||||||
location.setSpeed(cursor.getFloat(idxSpeed));
|
location.setSpeed(cursor.getFloat(columnIndices.idxSpeed));
|
||||||
}
|
}
|
||||||
if (!cursor.isNull(idxAccuracy)) {
|
if (!cursor.isNull(columnIndices.idxAccuracy)) {
|
||||||
location.setAccuracy(cursor.getFloat(idxAccuracy));
|
location.setAccuracy(cursor.getFloat(columnIndices.idxAccuracy));
|
||||||
}
|
}
|
||||||
if (location instanceof MyTracksLocation &&
|
if (location instanceof MyTracksLocation &&
|
||||||
!cursor.isNull(idxSensor)) {
|
!cursor.isNull(columnIndices.idxSensor)) {
|
||||||
MyTracksLocation mtLocation = (MyTracksLocation) location;
|
MyTracksLocation mtLocation = (MyTracksLocation) location;
|
||||||
// TODO get the right buffer.
|
// TODO get the right buffer.
|
||||||
Sensor.SensorDataSet sensorData;
|
Sensor.SensorDataSet sensorData;
|
||||||
try {
|
try {
|
||||||
sensorData = Sensor.SensorDataSet.parseFrom(cursor.getBlob(idxSensor));
|
sensorData = Sensor.SensorDataSet.parseFrom(cursor.getBlob(columnIndices.idxSensor));
|
||||||
mtLocation.setSensorData(sensorData);
|
mtLocation.setSensorData(sensorData);
|
||||||
} catch (InvalidProtocolBufferException e) {
|
} catch (InvalidProtocolBufferException e) {
|
||||||
Log.w(TAG, "Failed to parse sensor data.", e);
|
Log.w(TAG, "Failed to parse sensor data.", e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void fillLocation(Cursor cursor, Location location) {
|
||||||
|
CachedTrackColumnIndices columnIndicies = new CachedTrackColumnIndices(cursor);
|
||||||
|
fillLocation(cursor, columnIndicies, location);
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Track createTrack(Cursor cursor) {
|
public Track createTrack(Cursor cursor) {
|
||||||
@@ -776,13 +801,12 @@ public class MyTracksProviderUtilsImpl implements MyTracksProviderUtils {
|
|||||||
public Cursor getLocationsCursor(long trackId, long minTrackPointId,
|
public Cursor getLocationsCursor(long trackId, long minTrackPointId,
|
||||||
int maxLocations, boolean descending) {
|
int maxLocations, boolean descending) {
|
||||||
String selection;
|
String selection;
|
||||||
if (minTrackPointId > 0) {
|
if (minTrackPointId >= 0) {
|
||||||
selection = String.format("%s=%d AND %s>=%d",
|
selection = String.format("%s=%d AND %s%s%d",
|
||||||
TrackPointsColumns.TRACKID, trackId,
|
TrackPointsColumns.TRACKID, trackId, TrackPointsColumns._ID,
|
||||||
TrackPointsColumns._ID, minTrackPointId);
|
descending ? "<=" : ">=", minTrackPointId);
|
||||||
} else {
|
} else {
|
||||||
selection = String.format("%s=%d",
|
selection = String.format("%s=%d", TrackPointsColumns.TRACKID, trackId);
|
||||||
TrackPointsColumns.TRACKID, trackId);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
String sortOrder = "_id " + (descending ? "DESC" : "ASC");
|
String sortOrder = "_id " + (descending ? "DESC" : "ASC");
|
||||||
@@ -790,8 +814,7 @@ public class MyTracksProviderUtilsImpl implements MyTracksProviderUtils {
|
|||||||
sortOrder += " LIMIT " + maxLocations;
|
sortOrder += " LIMIT " + maxLocations;
|
||||||
}
|
}
|
||||||
|
|
||||||
return contentResolver.query(
|
return contentResolver.query(TrackPointsColumns.CONTENT_URI, null, selection, null, sortOrder);
|
||||||
TrackPointsColumns.CONTENT_URI, null, selection, null, sortOrder);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -939,20 +962,25 @@ public class MyTracksProviderUtilsImpl implements MyTracksProviderUtils {
|
|||||||
@Override
|
@Override
|
||||||
public LocationIterator getLocationIterator(final long trackId, final long startTrackPointId,
|
public LocationIterator getLocationIterator(final long trackId, final long startTrackPointId,
|
||||||
final boolean descending, final LocationFactory locationFactory) {
|
final boolean descending, final LocationFactory locationFactory) {
|
||||||
|
if (locationFactory == null) {
|
||||||
|
throw new IllegalArgumentException("Expecting non-null locationFactory");
|
||||||
|
}
|
||||||
return new LocationIterator() {
|
return new LocationIterator() {
|
||||||
private long lastTrackPointId = startTrackPointId;
|
private long lastTrackPointId = startTrackPointId;
|
||||||
private Cursor cursor = getCursor(startTrackPointId);
|
private Cursor cursor = getCursor(startTrackPointId);
|
||||||
private final int idColumnIdx = cursor != null ?
|
private final CachedTrackColumnIndices columnIndices = cursor != null ?
|
||||||
cursor.getColumnIndexOrThrow(TrackPointsColumns._ID) : -1;
|
new CachedTrackColumnIndices(cursor) : null;
|
||||||
|
|
||||||
private Cursor getCursor(long trackPointId) {
|
private Cursor getCursor(long trackPointId) {
|
||||||
return getLocationsCursor(trackId, trackPointId, 2000, descending);
|
return getLocationsCursor(trackId, trackPointId, defaultCursorBatchSize, descending);
|
||||||
}
|
}
|
||||||
|
|
||||||
private boolean advanceCursorToNextBatch() {
|
private boolean advanceCursorToNextBatch() {
|
||||||
|
long pointId = lastTrackPointId + (descending ? -1 : 1);
|
||||||
|
Log.d(TAG, "Advancing cursor point ID: " + pointId);
|
||||||
cursor.close();
|
cursor.close();
|
||||||
cursor = getCursor(lastTrackPointId + (descending ? -1 : 1));
|
cursor = getCursor(pointId);
|
||||||
return cursor != null && cursor.moveToNext();
|
return cursor != null;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -962,18 +990,33 @@ public class MyTracksProviderUtilsImpl implements MyTracksProviderUtils {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean hasNext() {
|
public boolean hasNext() {
|
||||||
return cursor != null && !cursor.isAfterLast();
|
if (cursor == null) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (cursor.isAfterLast()) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (cursor.isLast()) {
|
||||||
|
// If the current batch size was less that max, we can safely return, otherwise
|
||||||
|
// we need to advance to the next batch.
|
||||||
|
return cursor.getCount() == defaultCursorBatchSize &&
|
||||||
|
advanceCursorToNextBatch() && !cursor.isAfterLast();
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Location next() {
|
public Location next() {
|
||||||
if (!cursor.moveToNext() || !advanceCursorToNextBatch()) {
|
if (cursor == null ||
|
||||||
|
!(cursor.moveToNext() || advanceCursorToNextBatch() || cursor.moveToNext())) {
|
||||||
throw new NoSuchElementException();
|
throw new NoSuchElementException();
|
||||||
}
|
}
|
||||||
lastTrackPointId = cursor.getLong(idColumnIdx);
|
|
||||||
|
lastTrackPointId = cursor.getLong(columnIndices.idxId);
|
||||||
Location location = locationFactory.createLocation();
|
Location location = locationFactory.createLocation();
|
||||||
// TODO: Cache column indices.
|
fillLocation(cursor, columnIndices, location);
|
||||||
fillLocation(cursor, location);
|
|
||||||
return location;
|
return location;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -991,4 +1034,9 @@ public class MyTracksProviderUtilsImpl implements MyTracksProviderUtils {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// @VisibleForTesting
|
||||||
|
void setDefaultCursorBatchSize(int defaultCursorBatchSize) {
|
||||||
|
this.defaultCursorBatchSize = defaultCursorBatchSize;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+4
-2
@@ -77,9 +77,11 @@ public class TrackRecordingServiceTest
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A context wrapper with the user provided {@link ContentResolver}.
|
* A context wrapper with the user provided {@link ContentResolver}.
|
||||||
|
*
|
||||||
|
* TODO: Move to test utils package.
|
||||||
*/
|
*/
|
||||||
private static class MockContext extends ContextWrapper {
|
public static class MockContext extends ContextWrapper {
|
||||||
private final ContentResolver contentResolver;
|
private final ContentResolver contentResolver;
|
||||||
|
|
||||||
public MockContext(ContentResolver contentResolver, Context base) {
|
public MockContext(ContentResolver contentResolver, Context base) {
|
||||||
|
|||||||
Reference in New Issue
Block a user