forked from upstream-mirrors/OpenTracks
Segments: use TrackPoint.type.
So, far specific latitude for segment start/end (i.e., PAUSE/RESUME) was used. #316.
This commit is contained in:
@@ -40,13 +40,12 @@ import de.dennisguse.opentracks.content.data.TrackPointsColumns;
|
||||
import de.dennisguse.opentracks.content.data.TracksColumns;
|
||||
import de.dennisguse.opentracks.content.provider.ContentProviderUtils;
|
||||
import de.dennisguse.opentracks.content.provider.TrackPointIterator;
|
||||
import de.dennisguse.opentracks.util.LocationUtils;
|
||||
import de.dennisguse.opentracks.util.PreferencesUtils;
|
||||
|
||||
/**
|
||||
* Track data hub.
|
||||
* Receives data from {@link de.dennisguse.opentracks.content.provider.CustomContentProvider} and distributes it to {@link TrackDataListener} after some processing.
|
||||
*
|
||||
* <p>
|
||||
* {@link TrackPoint}s are filtered/downsampled with a dynamic sampling frequency.
|
||||
*
|
||||
* @author Rodrigo Damazio
|
||||
@@ -314,9 +313,6 @@ public class TrackDataHub implements SharedPreferences.OnSharedPreferenceChangeL
|
||||
if (cursor != null && cursor.moveToFirst()) {
|
||||
do {
|
||||
Marker marker = contentProviderUtils.createMarker(cursor);
|
||||
if (!LocationUtils.isValidLocation(marker.getLocation())) {
|
||||
continue;
|
||||
}
|
||||
for (TrackDataListener trackDataListener : trackDataListeners) {
|
||||
trackDataListener.onNewMarker(marker);
|
||||
}
|
||||
@@ -362,7 +358,6 @@ public class TrackDataHub implements SharedPreferences.OnSharedPreferenceChangeL
|
||||
|
||||
TrackPoint.Id lastTrackPointId = contentProviderUtils.getLastTrackPointId(selectedTrackId);
|
||||
int samplingFrequency = -1;
|
||||
boolean includeNextPoint = false;
|
||||
|
||||
|
||||
TrackPoint.Id next;
|
||||
@@ -390,19 +385,15 @@ public class TrackDataHub implements SharedPreferences.OnSharedPreferenceChangeL
|
||||
samplingFrequency = 1 + (int) (numTotalPoints / targetNumPoints);
|
||||
}
|
||||
|
||||
if (!LocationUtils.isValidLocation(trackPoint.getLocation())) { //This can be split markers (not anymore supported feature)
|
||||
includeNextPoint = true;
|
||||
|
||||
// Also include the last point if the selected track is not recording.
|
||||
if ((localNumLoadedTrackPoints % samplingFrequency == 0) || (trackPointId == lastTrackPointId && !isSelectedTrackRecording())) {
|
||||
for (TrackDataListener trackDataListener : sampledInListeners) {
|
||||
trackDataListener.onSampledInTrackPoint(trackPoint);
|
||||
}
|
||||
} else {
|
||||
// Also include the last point if the selected track is not recording.
|
||||
if (includeNextPoint || (localNumLoadedTrackPoints % samplingFrequency == 0) || (trackPointId == lastTrackPointId && !isSelectedTrackRecording())) {
|
||||
includeNextPoint = false;
|
||||
for (TrackDataListener trackDataListener : sampledInListeners) {
|
||||
trackDataListener.onSampledInTrackPoint(trackPoint);
|
||||
}
|
||||
} else {
|
||||
for (TrackDataListener trackDataListener : sampledOutListeners) {
|
||||
trackDataListener.onSampledOutTrackPoint(trackPoint);
|
||||
}
|
||||
for (TrackDataListener trackDataListener : sampledOutListeners) {
|
||||
trackDataListener.onSampledOutTrackPoint(trackPoint);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -28,6 +28,8 @@ import androidx.annotation.VisibleForTesting;
|
||||
import java.util.Objects;
|
||||
|
||||
/**
|
||||
* NOTE: A marker is indirectly (via it's location) assigned to one {@link TrackPoint} with trackPoint.hasLocation() == true.
|
||||
*
|
||||
* @author Leif Hendrik Wilden
|
||||
* @author Rodrigo Damazio
|
||||
*/
|
||||
|
||||
@@ -26,6 +26,16 @@ import java.util.Objects;
|
||||
|
||||
/**
|
||||
* This class extends the standard Android location with extra information.
|
||||
* <p>
|
||||
* NOTE: default location will be latitude=0.0 and longitude=0.0 (this is not meaningful).
|
||||
* <p>
|
||||
* NOTE: For Locations provided by the GPS.
|
||||
* We are replacing the GPS-provided time using the system time.
|
||||
* Then we have the same timestamps for the user-driven events (aka start, pause, resume) and restore {@link de.dennisguse.opentracks.stats.TrackStatistics}.
|
||||
* Drawbacks:
|
||||
* * GPS-provided timestamp might be more precise (but also have GPS week rollover)
|
||||
* * System clock might be changed (and thus non-monotonic)
|
||||
* TODO: if these might be problems, we need to store both timestamps.
|
||||
*
|
||||
* @author Sandor Dornbush
|
||||
*/
|
||||
@@ -36,6 +46,41 @@ public class TrackPoint {
|
||||
|
||||
private final Location location;
|
||||
|
||||
//TODO Private
|
||||
public enum Type {
|
||||
SEGMENT_START_MANUAL(-2), //Start of a segment due to user interaction (start, resume); no useful coordinates
|
||||
|
||||
SEGMENT_START_AUTOMATIC(-1), //Start of a segment due to too much distance from previous TrackPoint
|
||||
TRACKPOINT(0), //Normal trackpoint got from GPS
|
||||
|
||||
SEGMENT_END_MANUAL(1); //End of a segment; no useful coordinates
|
||||
|
||||
public final int type_db;
|
||||
|
||||
Type(int type_db) {
|
||||
this.type_db = type_db;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "" + type_db;
|
||||
}
|
||||
|
||||
public static Type getById(int id) {
|
||||
for (Type e : values()) {
|
||||
if (e.type_db == id) return e;
|
||||
}
|
||||
|
||||
throw new RuntimeException("unknown id: " + id);
|
||||
}
|
||||
|
||||
public boolean hasLocation() {
|
||||
return this == SEGMENT_START_AUTOMATIC || this == TRACKPOINT;
|
||||
}
|
||||
}
|
||||
|
||||
private final Type type;
|
||||
|
||||
private Float heartRate_bpm = null;
|
||||
private Float cyclingCadence_rpm = null;
|
||||
private Float power = null;
|
||||
@@ -43,14 +88,26 @@ public class TrackPoint {
|
||||
private Float elevationLoss = null;
|
||||
|
||||
public TrackPoint() {
|
||||
this(Type.TRACKPOINT, new Location(""));
|
||||
}
|
||||
|
||||
public TrackPoint(Type type) {
|
||||
this.type = type;
|
||||
this.location = new Location("");
|
||||
}
|
||||
|
||||
public TrackPoint(@NonNull Location location) {
|
||||
this.type = Type.TRACKPOINT;
|
||||
this.location = location;
|
||||
}
|
||||
|
||||
public TrackPoint(@NonNull Type type, @NonNull Location location) {
|
||||
this.type = type;
|
||||
this.location = location;
|
||||
}
|
||||
|
||||
public TrackPoint(@NonNull TrackPoint trackPoint) {
|
||||
this.type = trackPoint.getType();
|
||||
this.location = trackPoint.getLocation();
|
||||
|
||||
this.heartRate_bpm = trackPoint.getHeartRate_bpm();
|
||||
@@ -62,6 +119,7 @@ public class TrackPoint {
|
||||
}
|
||||
|
||||
public TrackPoint(double latitude, double longitude, Double altitude, long time) {
|
||||
this.type = Type.TRACKPOINT;
|
||||
location = new Location(LocationManager.GPS_PROVIDER);
|
||||
location.setLatitude(latitude);
|
||||
location.setLongitude(longitude);
|
||||
@@ -72,31 +130,52 @@ public class TrackPoint {
|
||||
}
|
||||
|
||||
@Deprecated //See #316
|
||||
public static TrackPoint createPause() {
|
||||
return createPauseWithTime(System.currentTimeMillis());
|
||||
public static TrackPoint createSegmentStartManual() {
|
||||
return createSegmentStartManualWithTime(System.currentTimeMillis());
|
||||
}
|
||||
|
||||
@Deprecated //See #316
|
||||
public static TrackPoint createPauseWithTime(long time) {
|
||||
Location pause = new Location(LocationManager.GPS_PROVIDER);
|
||||
pause.setLongitude(0);
|
||||
pause.setLatitude(TrackPointsColumns.PAUSE_LATITUDE);
|
||||
pause.setTime(time);
|
||||
return new TrackPoint(pause);
|
||||
}
|
||||
|
||||
@Deprecated //See #316
|
||||
public static TrackPoint createResume() {
|
||||
return createResumeWithTime(System.currentTimeMillis());
|
||||
}
|
||||
|
||||
@Deprecated //See #316
|
||||
public static TrackPoint createResumeWithTime(long time) {
|
||||
public static TrackPoint createSegmentStartManualWithTime(long time) {
|
||||
Location resume = new Location(LocationManager.GPS_PROVIDER);
|
||||
resume.setLongitude(0);
|
||||
resume.setLatitude(TrackPointsColumns.RESUME_LATITUDE);
|
||||
resume.setTime(time);
|
||||
return new TrackPoint(resume);
|
||||
return new TrackPoint(Type.SEGMENT_START_MANUAL, resume);
|
||||
}
|
||||
|
||||
@Deprecated //See #316
|
||||
public static TrackPoint createSegmentStartAutomatic() {
|
||||
return createSegmentStartAutomaticWithTime(System.currentTimeMillis());
|
||||
}
|
||||
|
||||
public static TrackPoint createSegmentStartAutomaticWithTime(long time) {
|
||||
Location resume = new Location(LocationManager.GPS_PROVIDER);
|
||||
resume.setTime(time);
|
||||
return new TrackPoint(Type.SEGMENT_START_AUTOMATIC, resume);
|
||||
}
|
||||
|
||||
public static TrackPoint createSegmentEnd() {
|
||||
return createSegmentEndWithTime(System.currentTimeMillis());
|
||||
}
|
||||
|
||||
public static TrackPoint createSegmentEndWithTime(@NonNull TrackPoint trackPoint) {
|
||||
return createSegmentEndWithTime(trackPoint.getTime());
|
||||
}
|
||||
|
||||
public static TrackPoint createSegmentEndWithTime(long time) {
|
||||
Location pause = new Location(LocationManager.GPS_PROVIDER);
|
||||
pause.setTime(time);
|
||||
return new TrackPoint(Type.SEGMENT_END_MANUAL, pause);
|
||||
}
|
||||
|
||||
@NonNull
|
||||
public Type getType() {
|
||||
return type;
|
||||
}
|
||||
|
||||
public boolean isSegmentStart() {
|
||||
return type == Type.SEGMENT_START_AUTOMATIC || type == Type.SEGMENT_START_MANUAL;
|
||||
}
|
||||
|
||||
public boolean isSegmentEnd() {
|
||||
return type == Type.SEGMENT_END_MANUAL;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -268,7 +347,7 @@ public class TrackPoint {
|
||||
@NonNull
|
||||
@Override
|
||||
public String toString() {
|
||||
return "time=" + getTime() + ": lat=" + getLatitude() + " lng=" + getLongitude() + " acc=" + getAccuracy();
|
||||
return "time=" + getTime() + " (type=" + getType() + "): lat=" + getLatitude() + " lng=" + getLongitude() + " acc=" + getAccuracy();
|
||||
}
|
||||
|
||||
public static class Id {
|
||||
|
||||
@@ -37,13 +37,11 @@ public interface TrackPointsColumns extends BaseColumns {
|
||||
|
||||
// Columns
|
||||
String TRACKID = "trackid";
|
||||
// See {@link TrackPoint.Type}
|
||||
String TYPE = "type";
|
||||
|
||||
String LONGITUDE = "longitude";
|
||||
String LATITUDE = "latitude";
|
||||
@Deprecated
|
||||
double PAUSE_LATITUDE = 100.0;
|
||||
@Deprecated
|
||||
double RESUME_LATITUDE = 200.0;
|
||||
|
||||
String TIME = "time";
|
||||
String ALTITUDE = "elevation";
|
||||
@@ -71,6 +69,7 @@ public interface TrackPointsColumns extends BaseColumns {
|
||||
+ SENSOR_POWER + " FLOAT, "
|
||||
+ ELEVATION_GAIN + " FLOAT, "
|
||||
+ ELEVATION_LOSS + " FLOAT, "
|
||||
+ TYPE + " TEXT CHECK(type IN (-2, -1, 0, 1)), "
|
||||
+ "FOREIGN KEY (" + TRACKID + ") REFERENCES " + TracksColumns.TABLE_NAME + "(" + TracksColumns._ID + ") ON UPDATE CASCADE ON DELETE CASCADE"
|
||||
+ ")";
|
||||
|
||||
|
||||
@@ -9,6 +9,7 @@ import de.dennisguse.opentracks.content.data.TrackPointsColumns;
|
||||
*/
|
||||
class CachedTrackPointsIndexes {
|
||||
final int idIndex;
|
||||
final int typeIndex;
|
||||
final int longitudeIndex;
|
||||
final int latitudeIndex;
|
||||
final int timeIndex;
|
||||
@@ -24,6 +25,7 @@ class CachedTrackPointsIndexes {
|
||||
|
||||
CachedTrackPointsIndexes(Cursor cursor) {
|
||||
idIndex = cursor.getColumnIndex(TrackPointsColumns._ID);
|
||||
typeIndex = cursor.getColumnIndex(TrackPointsColumns.TYPE);
|
||||
longitudeIndex = cursor.getColumnIndexOrThrow(TrackPointsColumns.LONGITUDE);
|
||||
latitudeIndex = cursor.getColumnIndexOrThrow(TrackPointsColumns.LATITUDE);
|
||||
timeIndex = cursor.getColumnIndexOrThrow(TrackPointsColumns.TIME);
|
||||
|
||||
@@ -63,8 +63,6 @@ public class ContentProviderUtils {
|
||||
// The base URI for the app's content provider.
|
||||
public static final String CONTENT_BASE_URI = "content://" + AUTHORITY_PACKAGE;
|
||||
|
||||
// Maximum number of markers that will be loaded at one time.
|
||||
public static final int MAX_LOADED_MARKERS = 10000;
|
||||
private static final String ID_SEPARATOR = ",";
|
||||
|
||||
private final ContentResolver contentResolver;
|
||||
@@ -544,7 +542,7 @@ public class ContentProviderUtils {
|
||||
* @param indexes the cached trackPoints indexes
|
||||
*/
|
||||
static TrackPoint fillTrackPoint(Cursor cursor, CachedTrackPointsIndexes indexes) {
|
||||
TrackPoint trackPoint = new TrackPoint();
|
||||
TrackPoint trackPoint = new TrackPoint(TrackPoint.Type.getById(cursor.getInt(indexes.typeIndex)));
|
||||
if (!cursor.isNull(indexes.longitudeIndex)) {
|
||||
trackPoint.setId(new TrackPoint.Id(cursor.getInt(indexes.idIndex)));
|
||||
}
|
||||
@@ -696,7 +694,7 @@ public class ContentProviderUtils {
|
||||
*/
|
||||
@Deprecated
|
||||
public TrackPoint getLastValidTrackPoint(Track.Id trackId) {
|
||||
String selection = TrackPointsColumns._ID + "=(SELECT MAX(" + TrackPointsColumns._ID + ") FROM " + TrackPointsColumns.TABLE_NAME + " WHERE " + TrackPointsColumns.TRACKID + "=? AND " + TrackPointsColumns.LATITUDE + "<=" + MAX_LATITUDE + ")";
|
||||
String selection = TrackPointsColumns._ID + "=(SELECT MAX(" + TrackPointsColumns._ID + ") FROM " + TrackPointsColumns.TABLE_NAME + " WHERE " + TrackPointsColumns.TRACKID + "=? AND " + TrackPointsColumns.TYPE + " IN (" + TrackPoint.Type.SEGMENT_START_AUTOMATIC + "," + TrackPoint.Type.TRACKPOINT + "))";
|
||||
String[] selectionArgs = new String[]{Long.toString(trackId.getId())};
|
||||
return findTrackPointBy(selection, selectionArgs);
|
||||
}
|
||||
@@ -715,12 +713,15 @@ public class ContentProviderUtils {
|
||||
/**
|
||||
* Creates the {@link ContentValues} for a {@link TrackPoint}.
|
||||
*
|
||||
* @param trackPoint the trackPointstats_pace_km#87
|
||||
* @param trackPoint the trackPoint
|
||||
* @param trackId the track id
|
||||
*/
|
||||
private ContentValues createContentValues(TrackPoint trackPoint, Track.Id trackId) {
|
||||
ContentValues values = new ContentValues();
|
||||
values.put(TrackPointsColumns.TRACKID, trackId.getId());
|
||||
if (trackPoint.getType() != null) {
|
||||
values.put(TrackPointsColumns.TYPE, trackPoint.getType().type_db);
|
||||
}
|
||||
values.put(TrackPointsColumns.LONGITUDE, (int) (trackPoint.getLongitude() * 1E6));
|
||||
values.put(TrackPointsColumns.LATITUDE, (int) (trackPoint.getLatitude() * 1E6));
|
||||
|
||||
|
||||
@@ -329,10 +329,8 @@ public class CustomContentProvider extends ContentProvider {
|
||||
}
|
||||
|
||||
private Uri insertTrackPoint(Uri url, ContentValues values) {
|
||||
boolean hasLatitude = values.containsKey(TrackPointsColumns.LATITUDE);
|
||||
boolean hasLongitude = values.containsKey(TrackPointsColumns.LONGITUDE);
|
||||
boolean hasTime = values.containsKey(TrackPointsColumns.TIME);
|
||||
if (!hasLatitude || !hasLongitude || !hasTime) {
|
||||
if (!hasTime) {
|
||||
throw new IllegalArgumentException("Latitude, longitude, and time values are required.");
|
||||
}
|
||||
long rowId = db.insert(TrackPointsColumns.TABLE_NAME, TrackPointsColumns._ID, values);
|
||||
|
||||
@@ -25,7 +25,7 @@ public class CustomSQLiteOpenHelper extends SQLiteOpenHelper {
|
||||
|
||||
private static final String TAG = CustomSQLiteOpenHelper.class.getSimpleName();
|
||||
|
||||
private static final int DATABASE_VERSION = 29;
|
||||
private static final int DATABASE_VERSION = 30;
|
||||
|
||||
@VisibleForTesting
|
||||
public static final String DATABASE_NAME = "database.db";
|
||||
|
||||
Reference in New Issue
Block a user