TrackPoint has Location as delegate instead of inheritance.

This commit is contained in:
Dennis Guse
2020-03-29 12:30:16 +02:00
parent 901cffee97
commit 194379295d
25 changed files with 288 additions and 184 deletions
@@ -16,6 +16,8 @@
package de.dennisguse.opentracks;
import android.location.Location;
import de.dennisguse.opentracks.content.data.Track;
import de.dennisguse.opentracks.content.data.TrackPoint;
import de.dennisguse.opentracks.content.sensor.SensorDataSet;
@@ -68,16 +70,15 @@ public class TrackStubUtils {
* @return a SensorDataSetLocation stub.
*/
private static TrackPoint createSensorDataSetLocation(double latitude, double longitude, double altitude) {
TrackPoint trackPoint = new TrackPoint(LOCATION_PROVIDER);
trackPoint.setLatitude(latitude);
trackPoint.setLongitude(longitude);
trackPoint.setAltitude(altitude);
trackPoint.setAccuracy(INITIAL_ACCURACY);
trackPoint.setSpeed(INITIAL_SPEED);
trackPoint.setTime(INITIAL_TIME);
trackPoint.setBearing(INITIAL_BEARING);
trackPoint.setSensorDataSet(new SensorDataSet(Float.NaN, Float.NaN));
Location location = new Location(LOCATION_PROVIDER);
location.setLatitude(latitude);
location.setLongitude(longitude);
location.setAltitude(altitude);
location.setAccuracy(INITIAL_ACCURACY);
location.setSpeed(INITIAL_SPEED);
location.setTime(INITIAL_TIME);
location.setBearing(INITIAL_BEARING);
return trackPoint;
return new TrackPoint(location, new SensorDataSet(Float.NaN, Float.NaN));
}
}
@@ -1,5 +1,7 @@
package de.dennisguse.opentracks.content.data;
import android.location.Location;
public class TestDataUtil {
public static final double INITIAL_LATITUDE = 37.0;
@@ -30,12 +32,12 @@ public class TestDataUtil {
* @return created location
*/
public static TrackPoint createTrackPoint(int i) {
TrackPoint trackpoint = new TrackPoint("test");
trackpoint.setLatitude(INITIAL_LATITUDE + (double) i / 10000.0);
trackpoint.setLongitude(INITIAL_LONGITUDE - (double) i / 10000.0);
trackpoint.setAccuracy((float) i / 100.0f);
trackpoint.setAltitude(i * ALTITUDE_INTERVAL);
trackpoint.setTime(i + 1);
return trackpoint;
Location location = new Location("test");
location.setLatitude(INITIAL_LATITUDE + (double) i / 10000.0);
location.setLongitude(INITIAL_LONGITUDE - (double) i / 10000.0);
location.setAccuracy((float) i / 100.0f);
location.setAltitude(i * ALTITUDE_INTERVAL);
location.setTime(i + 1);
return new TrackPoint(location);
}
}
@@ -81,7 +81,7 @@ public class CustomContentProviderUtilsTest {
@Test
public void testLocationIterator_customFactory() {
final TrackPoint location = new TrackPoint("test_location");
final TrackPoint location = new TrackPoint(new Location("test_location"));
final AtomicInteger counter = new AtomicInteger();
testIterator(1, 15, 4, false, new TrackPointFactory() {
@Override
@@ -161,12 +161,12 @@ public class CustomContentProviderUtilsTest {
TrackPoint[] trackPoints = new TrackPoint[numPoints];
for (int i = 0; i < numPoints; ++i) {
TrackPoint loc = new TrackPoint("test");
Location loc = new Location("test");
loc.setLatitude(37.0 + (double) i / 10000.0);
loc.setLongitude(57.0 - (double) i / 10000.0);
loc.setAccuracy((float) i / 100.0f);
loc.setAltitude(i * 2.5);
trackPoints[i] = loc;
trackPoints[i] = new TrackPoint(loc);
}
contentProviderUtils.bulkInsertTrackPoint(trackPoints, numPoints, id);
@@ -646,7 +646,7 @@ public class CustomContentProviderUtilsTest {
insertTrackWithLocations(track);
TrackPoint lastTrackPoint = contentProviderUtils.getLastValidTrackPoint(trackId);
checkLocation(9, lastTrackPoint);
checkLocation(9, lastTrackPoint.getLocation());
}
/**
@@ -709,7 +709,7 @@ public class CustomContentProviderUtilsTest {
Assert.assertTrue(trackPointIterator.hasNext());
TrackPoint trackPoint = trackPointIterator.next();
Assert.assertEquals(startTrackPointId - i, trackPointIterator.getTrackPointId());
checkLocation((trackpointIds.length - 1) - i, trackPoint);
checkLocation((trackpointIds.length - 1) - i, trackPoint.getLocation());
}
Assert.assertFalse(trackPointIterator.hasNext());
}
@@ -734,10 +734,10 @@ public class CustomContentProviderUtilsTest {
TrackPointIterator locationIterator = contentProviderUtils.getTrackPointLocationIterator(trackId, startTrackPointId, false, TrackPointFactory.DEFAULT_LOCATION_FACTORY);
for (int i = 0; i < trackpointIds.length; i++) {
Assert.assertTrue(locationIterator.hasNext());
Location location = locationIterator.next();
TrackPoint trackPoint = locationIterator.next();
Assert.assertEquals(startTrackPointId + i, locationIterator.getTrackPointId());
checkLocation(i, location);
checkLocation(i, trackPoint.getLocation());
}
Assert.assertFalse(locationIterator.hasNext());
}
@@ -18,6 +18,7 @@ package de.dennisguse.opentracks.io.file.importer;
import android.content.ContentUris;
import android.content.Context;
import android.location.Location;
import android.location.LocationManager;
import android.net.Uri;
@@ -84,12 +85,12 @@ public abstract class AbstractTestFileTrackImporter {
public ContentProviderUtils contentProviderUtils;
TrackPoint createTrackPoint(int index, long time) {
TrackPoint trackPoint = new TrackPoint(LocationManager.GPS_PROVIDER);
Location trackPoint = new Location(LocationManager.GPS_PROVIDER);
trackPoint.setLatitude(TRACK_LATITUDE + index);
trackPoint.setLongitude(TRACK_LONGITUDE + index);
trackPoint.setAltitude(TRACK_ELEVATION + index);
trackPoint.setTime(time);
return trackPoint;
return new TrackPoint(trackPoint);
}
/**
@@ -67,7 +67,7 @@ public class ExportImportTest {
waypoint.setIcon("the waypoing icon" + i);
waypoint.setPhotoUrl("the photo url" + i);
waypoint.setTrackId(trackId);
waypoint.setLocation(track.getTrackPoints().get(i));
waypoint.setLocation(track.getTrackPoints().get(i).getLocation());
contentProviderUtils.insertWaypoint(waypoint);
waypoints.add(waypoint);
@@ -43,10 +43,10 @@ public class TrackRecordingServiceNotificationManagerTest {
TrackRecordingServiceNotificationManager subject = new TrackRecordingServiceNotificationManager(notificationManager, notificationCompatBuilder);
// when
subject.updateLocation(context, locationMock, 100);
subject.updateLocation(context, locationMock, 100);
subject.updateLocation(context, locationMock, 1000);
subject.updateLocation(context, locationMock, 100);
subject.updateTrackPoint(context, locationMock, 100);
subject.updateTrackPoint(context, locationMock, 100);
subject.updateTrackPoint(context, locationMock, 1000);
subject.updateTrackPoint(context, locationMock, 100);
// then
verify(notificationCompatBuilder, times(6)).setOnlyAlertOnce(true);
@@ -19,6 +19,7 @@ import android.content.ContentProvider;
import android.content.Context;
import android.content.Intent;
import android.content.SharedPreferences;
import android.location.Location;
import android.os.IBinder;
import androidx.test.core.app.ApplicationProvider;
@@ -42,7 +43,6 @@ import java.util.concurrent.TimeoutException;
import de.dennisguse.opentracks.R;
import de.dennisguse.opentracks.content.data.Track;
import de.dennisguse.opentracks.content.data.TrackPoint;
import de.dennisguse.opentracks.content.data.Waypoint;
import de.dennisguse.opentracks.content.provider.ContentProviderUtils;
import de.dennisguse.opentracks.content.provider.CustomContentProvider;
@@ -210,15 +210,15 @@ public class TrackRecordingServiceTest {
/**
* Inserts a location and waits for 200ms.
*/
private void insertTrackPoint(TrackRecordingServiceInterface trackRecordingService) throws InterruptedException {
TrackPoint trackPoint = new TrackPoint("gps");
trackPoint.setLongitude(35.0f);
trackPoint.setLatitude(45.0f);
trackPoint.setAccuracy(5);
trackPoint.setSpeed(10);
trackPoint.setTime(System.currentTimeMillis());
trackPoint.setBearing(3.0f);
trackRecordingService.insertTrackPoint(trackPoint);
private void insertLocation(TrackRecordingServiceInterface trackRecordingService) throws InterruptedException {
Location location = new Location("gps");
location.setLongitude(35.0f);
location.setLatitude(45.0f);
location.setAccuracy(5);
location.setSpeed(10);
location.setTime(System.currentTimeMillis());
location.setBearing(3.0f);
trackRecordingService.insertLocation(location);
Thread.sleep(200);
}
@@ -239,7 +239,7 @@ public class TrackRecordingServiceTest {
TrackRecordingServiceInterface service = ((TrackRecordingServiceInterface) mServiceRule.bindService(createStartIntent(context)));
service.startNewTrack();
Assert.assertTrue(service.isRecording());
insertTrackPoint(service);
insertLocation(service);
long trackId = service.getRecordingTrackId();
long waypointId = service.insertWaypoint(null, null, null, null);
@@ -3,6 +3,7 @@ package de.dennisguse.opentracks.services;
import android.content.ContentProvider;
import android.content.Context;
import android.content.SharedPreferences;
import android.location.Location;
import android.os.Looper;
import androidx.test.core.app.ApplicationProvider;
@@ -25,7 +26,6 @@ import java.util.concurrent.TimeoutException;
import de.dennisguse.opentracks.R;
import de.dennisguse.opentracks.content.data.Track;
import de.dennisguse.opentracks.content.data.TrackPoint;
import de.dennisguse.opentracks.content.provider.ContentProviderUtils;
import de.dennisguse.opentracks.content.provider.CustomContentProvider;
import de.dennisguse.opentracks.stats.TripStatistics;
@@ -208,14 +208,14 @@ public class TrackRecordingServiceTestLooper {
// Insert a few points, markers and statistics.
long startTime = System.currentTimeMillis();
for (int i = 0; i < 30; i++) {
TrackPoint trackPoint = new TrackPoint("gps");
trackPoint.setLongitude(35.0f + i / 10.0f);
trackPoint.setLatitude(45.0f - i / 5.0f);
trackPoint.setAccuracy(5);
trackPoint.setSpeed(10);
trackPoint.setTime(startTime + i * 10000);
trackPoint.setBearing(3.0f);
service.insertTrackPoint(trackPoint);
Location location = new Location("gps");
location.setLongitude(35.0f + i / 10.0f);
location.setLatitude(45.0f - i / 5.0f);
location.setAccuracy(5);
location.setSpeed(10);
location.setTime(startTime + i * 10000);
location.setBearing(3.0f);
service.insertLocation(location);
if (i % 7 == 0) {
service.insertWaypoint(null, null, null, null);
@@ -412,7 +412,7 @@ public class TrackDataHub implements DataSourceListener, SharedPreferences.OnSha
samplingFrequency = 1 + (int) (numTotalPoints / targetNumPoints);
}
if (!LocationUtils.isValidLocation(trackPoint)) { //This can be split markers (not anymore supported feature)
if (!LocationUtils.isValidLocation(trackPoint.getLocation())) { //This can be split markers (not anymore supported feature)
includeNextPoint = true;
} else {
// Also include the last point if the selected track is not recording.
@@ -18,6 +18,8 @@ package de.dennisguse.opentracks.content.data;
import android.location.Location;
import android.location.LocationManager;
import androidx.annotation.NonNull;
import de.dennisguse.opentracks.content.sensor.SensorDataSet;
/**
@@ -25,34 +27,71 @@ import de.dennisguse.opentracks.content.sensor.SensorDataSet;
*
* @author Sandor Dornbush
*/
//TODO Check if we can drop inheritance from Location and use attributes instead.
public class TrackPoint extends Location {
public class TrackPoint {
public static TrackPoint createPause() {
TrackPoint pause = new TrackPoint(LocationManager.GPS_PROVIDER);
pause.setLongitude(0);
pause.setLatitude(TrackPointsColumns.PAUSE_LATITUDE);
pause.setTime(System.currentTimeMillis());
return pause;
private Location location;
public TrackPoint() {
this.location = new Location("");
}
public static TrackPoint createResume() {
TrackPoint resume = new TrackPoint(LocationManager.GPS_PROVIDER);
resume.setLongitude(0);
resume.setLatitude(TrackPointsColumns.RESUME_LATITUDE);
resume.setTime(System.currentTimeMillis());
return resume;
public TrackPoint(@NonNull Location location) {
this.location = location;
}
public TrackPoint(@NonNull Location location, SensorDataSet sensorDataSet) {
this.location = location;
this.sensorDataSet = sensorDataSet;
}
public TrackPoint(@NonNull TrackPoint trackPoint, SensorDataSet sensorDataSet) {
this.location = trackPoint.getLocation();
this.sensorDataSet = sensorDataSet;
}
private SensorDataSet sensorDataSet = null;
public TrackPoint(Location location, SensorDataSet sensorDataSet) {
super(location);
this.sensorDataSet = sensorDataSet;
public TrackPoint(double latitude, double longitude, Double altitude, long time) {
location = new Location(LocationManager.GPS_PROVIDER);
location.setLatitude(latitude);
location.setLongitude(longitude);
if (altitude != null) {
location.setAltitude(altitude);
}
location.setTime(time);
}
public TrackPoint(String provider) {
super(provider);
public static TrackPoint createPause() {
return createPauseWithTime(System.currentTimeMillis());
}
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);
}
public static TrackPoint createResume() {
return createResumeWithTime(System.currentTimeMillis());
}
public static TrackPoint createResumeWithTime(long time) {
Location resume = new Location(LocationManager.GPS_PROVIDER);
resume.setLongitude(0);
resume.setLatitude(TrackPointsColumns.RESUME_LATITUDE);
resume.setTime(time);
return new TrackPoint(resume);
}
public @NonNull
Location getLocation() {
return location;
}
public void setLocation(@NonNull Location location) {
this.location = location;
}
public SensorDataSet getSensorDataSet() {
@@ -63,8 +102,92 @@ public class TrackPoint extends Location {
this.sensorDataSet = sensorDataSet;
}
public double getLatitude() {
return location.getLatitude();
}
public void setLatitude(double latitude) {
location.setLatitude(latitude);
}
public double getLongitude() {
return location.getLongitude();
}
public void setLongitude(double longitude) {
location.setLongitude(longitude);
}
public long getTime() {
return location.getTime();
}
public void setTime(long time) {
location.setTime(time);
}
public boolean hasAltitude() {
return location.hasAltitude();
}
public double getAltitude() {
return location.getAltitude();
}
public void setAltitude(double altitude) {
location.setAltitude(altitude);
}
public boolean hasSpeed() {
return location.hasSpeed();
}
public float getSpeed() {
return location.getSpeed();
}
public void setSpeed(float speed) {
location.setSpeed(speed);
}
public boolean hasBearing() {
return location.hasBearing();
}
public float getBearing() {
return location.getBearing();
}
public void setBearing(float bearing) {
location.setBearing(bearing);
}
public boolean hasAccuracy() {
return location.hasAccuracy();
}
public float getAccuracy() {
return location.getAccuracy();
}
public void setAccuracy(float horizontalAccuracy) {
location.setAccuracy(horizontalAccuracy);
}
public float distanceTo(@NonNull TrackPoint dest) {
return location.distanceTo(dest.getLocation());
}
public float bearingTo(@NonNull TrackPoint dest) {
return location.bearingTo(dest.getLocation());
}
public float bearingTo(@NonNull Location dest) {
return location.bearingTo(dest);
}
public void reset() {
super.reset();
location.reset();
sensorDataSet = null;
}
}
@@ -19,6 +19,8 @@ package de.dennisguse.opentracks.content.data;
import android.location.Location;
import android.net.Uri;
import androidx.annotation.NonNull;
/**
* A waypoint.
*
@@ -117,11 +119,12 @@ public final class Waypoint {
this.duration = duration;
}
public Location getLocation() {
public @NonNull
Location getLocation() {
return location;
}
public void setLocation(Location location) {
public void setLocation(@NonNull Location location) {
this.location = location;
}
@@ -755,7 +755,7 @@ public class ContentProviderUtils {
* @param cursor the cursor pointing to the location
*/
public TrackPoint createTrackPoint(Cursor cursor) {
TrackPoint location = new TrackPoint("");
TrackPoint location = new TrackPoint();
fillTrackPoint(cursor, new CachedTrackPointsIndexes(cursor), location);
return location;
}
@@ -1,5 +1,6 @@
package de.dennisguse.opentracks.content.provider;
import android.location.Location;
import android.location.LocationManager;
import de.dennisguse.opentracks.content.data.TrackPoint;
@@ -16,6 +17,6 @@ public class TrackPointFactory {
public static final TrackPointFactory DEFAULT_LOCATION_FACTORY = new TrackPointFactory();
public TrackPoint create() {
return new TrackPoint(LocationManager.GPS_PROVIDER);
return new TrackPoint(new Location(LocationManager.GPS_PROVIDER));
}
}
@@ -18,7 +18,7 @@ public class TrackPointIterator implements Iterator<TrackPoint>, AutoCloseable {
private final ContentProviderUtils contentProviderUtils;
private final long trackId;
private final boolean descending;
private final TrackPointFactory trackPointFactory;
private final TrackPointFactory trackPointFactory; //TODO Remove; seems to be an old performance optimization.
private final CachedTrackPointsIndexes indexes;
private long lastTrackPointId = -1L;
private Cursor cursor;
@@ -358,7 +358,7 @@ public class StatsFragment extends Fragment implements TrackDataListener {
}
if (lastTrackPoint != null) {
boolean hasFix = !LocationUtils.isLocationOld(lastTrackPoint);
boolean hasFix = !LocationUtils.isLocationOld(lastTrackPoint.getLocation());
boolean hasGoodFix = lastTrackPoint.hasAccuracy() && lastTrackPoint.getAccuracy() < recordingGpsAccuracy;
if (!hasFix || !hasGoodFix) {
@@ -139,7 +139,7 @@ public class FileTrackExporter implements TrackExporter {
setLocationTime(trackPoint, offset);
locationNumber++;
boolean isLocationValid = LocationUtils.isValidLocation(trackPoint);
boolean isLocationValid = LocationUtils.isValidLocation(trackPoint.getLocation());
boolean isSegmentValid = isLocationValid && isLastLocationValid;
if (!wroteTrack && isSegmentValid) {
// Found the first two consecutive locations that are valid
@@ -193,7 +193,7 @@ public class FileTrackExporter implements TrackExporter {
* Sets a trackPoint time.
*
* @param trackPoint the trackPoint
* @param offset the time offset
* @param offset the time offset
*/
private void setLocationTime(TrackPoint trackPoint, long offset) {
if (trackPoint != null) {
@@ -213,7 +213,7 @@ public class FileTrackExporter implements TrackExporter {
@Override
public TrackPoint create() {
if (currentTrackPoint == null) {
currentTrackPoint = new TrackPoint("");
currentTrackPoint = new TrackPoint();
}
return currentTrackPoint;
}
@@ -170,7 +170,7 @@ public class GpxTrackWriter implements TrackWriter {
@Override
public void writeTrackPoint(TrackPoint trackPoint) {
if (printWriter != null) {
printWriter.println("<trkpt " + formatLocation(trackPoint) + ">");
printWriter.println("<trkpt " + formatLocation(trackPoint.getLocation()) + ">");
if (trackPoint.hasAltitude()) {
printWriter.println("<ele>" + ELEVATION_FORMAT.format(trackPoint.getAltitude()) + "</ele>");
}
@@ -192,7 +192,7 @@ public class KmlTrackWriter implements TrackWriter {
this.startTrackPoint = startTrackPoint;
if (printWriter != null) {
String name = context.getString(R.string.marker_label_start, track.getName());
writePlacemark(name, "", "", START_STYLE, startTrackPoint);
writePlacemark(name, "", "", START_STYLE, startTrackPoint.getLocation());
printWriter.println("<Placemark>");
if (exportTrackDetail) {
@@ -218,7 +218,7 @@ public class KmlTrackWriter implements TrackWriter {
if (exportTrackDetail) {
String name = context.getString(R.string.marker_label_end, track.getName());
String description = descriptionGenerator.generateTrackDescription(track, false);
writePlacemark(name, "", description, END_STYLE, endTrackPoint);
writePlacemark(name, "", description, END_STYLE, endTrackPoint.getLocation());
}
}
}
@@ -259,10 +259,10 @@ public class KmlTrackWriter implements TrackWriter {
public void writeTrackPoint(TrackPoint trackPoint) {
if (printWriter != null) {
if (exportTrackDetail) {
printWriter.println("<when>" + getTime(trackPoint) + "</when>");
printWriter.println("<when>" + getTime(trackPoint.getLocation()) + "</when>");
}
printWriter.println("<gx:coord>" + getCoordinates(trackPoint, " ") + "</gx:coord>");
printWriter.println("<gx:coord>" + getCoordinates(trackPoint.getLocation(), " ") + "</gx:coord>");
if (exportSensorData) {
SensorDataSet sensorDataSet = trackPoint.getSensorDataSet();
@@ -386,7 +386,7 @@ public class KmlTrackWriter implements TrackWriter {
if (trackPointId == -1L) {
return location.getBearing();
}
Location viewLocation;
TrackPoint viewLocation;
try (Cursor cursor = contentProviderUtils.getTrackPointCursor(trackId, trackPointId, 10, true)) {
if (cursor == null || cursor.getCount() == 0) {
return location.getBearing();
@@ -17,8 +17,6 @@
package de.dennisguse.opentracks.io.file.importer;
import android.content.Context;
import android.location.Location;
import android.location.LocationManager;
import android.net.Uri;
import android.util.Log;
@@ -40,7 +38,6 @@ import javax.xml.parsers.SAXParserFactory;
import de.dennisguse.opentracks.R;
import de.dennisguse.opentracks.content.data.Track;
import de.dennisguse.opentracks.content.data.TrackPoint;
import de.dennisguse.opentracks.content.data.TrackPointsColumns;
import de.dennisguse.opentracks.content.data.Waypoint;
import de.dennisguse.opentracks.content.provider.ContentProviderUtils;
import de.dennisguse.opentracks.content.provider.TrackPointFactory;
@@ -193,7 +190,7 @@ abstract class AbstractFileTrackImporter extends DefaultHandler implements Track
waypoint = null;
} else {
// The waypoint trackPoint time matches the track point time
if (!LocationUtils.isValidLocation(trackPoint)) {
if (!LocationUtils.isValidLocation(trackPoint.getLocation())) {
// Invalid trackPoint, load the next trackPoint
trackPoint = null;
continue;
@@ -207,7 +204,7 @@ abstract class AbstractFileTrackImporter extends DefaultHandler implements Track
long duration = trackTripStatisticstrackUpdater.getTripStatistics().getTotalTime();
// Insert waypoint
Waypoint newWaypoint = new Waypoint(waypoint.getName(), waypointDescription, waypoint.getCategory(), icon, track.getId(), length, duration, trackPoint, waypoint.getPhotoUrl());
Waypoint newWaypoint = new Waypoint(waypoint.getName(), waypointDescription, waypoint.getCategory(), icon, track.getId(), length, duration, trackPoint.getLocation(), waypoint.getPhotoUrl());
contentProviderUtils.insertWaypoint(newWaypoint);
}
@@ -274,7 +271,7 @@ abstract class AbstractFileTrackImporter extends DefaultHandler implements Track
//If not the first segment, add a pause separator if there is at least one location in the last segment.
if (trackData.numberOfSegments > 1 && trackData.lastLocationInCurrentSegment != null) {
insertLocation(createTrackPoint(TrackPointsColumns.PAUSE_LATITUDE, 0.0, 0.0, trackData.lastLocationInCurrentSegment.getTime()));
insertLocation(TrackPoint.createPauseWithTime(trackData.lastLocationInCurrentSegment.getTime()));
}
trackData.lastLocationInCurrentSegment = null;
}
@@ -289,12 +286,12 @@ abstract class AbstractFileTrackImporter extends DefaultHandler implements Track
}
Waypoint waypoint = new Waypoint();
TrackPoint location = createTrackPoint();
TrackPoint trackPoint = createTrackPoint();
if (!LocationUtils.isValidLocation(location)) {
throw new SAXException(createErrorMessage("Invalid location detected: " + location));
if (!LocationUtils.isValidLocation(trackPoint.getLocation())) {
throw new SAXException(createErrorMessage("Invalid location detected: " + trackPoint));
}
waypoint.setLocation(location);
waypoint.setLocation(trackPoint.getLocation());
if (name != null) {
waypoint.setName(name);
@@ -316,11 +313,11 @@ abstract class AbstractFileTrackImporter extends DefaultHandler implements Track
* Gets a track point.
*/
protected TrackPoint getTrackPoint() throws SAXException {
TrackPoint location = createTrackPoint();
TrackPoint trackPoint = createTrackPoint();
// Calculate derived attributes from the previous point
if (trackData.lastLocationInCurrentSegment != null && trackData.lastLocationInCurrentSegment.getTime() != 0) {
long timeDifference = location.getTime() - trackData.lastLocationInCurrentSegment.getTime();
long timeDifference = trackPoint.getTime() - trackData.lastLocationInCurrentSegment.getTime();
// Check for negative time change
if (timeDifference <= 0) {
@@ -332,22 +329,22 @@ abstract class AbstractFileTrackImporter extends DefaultHandler implements Track
* GPS points tend to have some inherent imprecision, speed and bearing will likely be off, so the statistics for things like max speed will also be off.
*/
double duration = timeDifference * UnitConversions.MS_TO_S;
double speed = trackData.lastLocationInCurrentSegment.distanceTo(location) / duration;
location.setSpeed((float) speed);
double speed = trackData.lastLocationInCurrentSegment.distanceTo(trackPoint) / duration;
trackPoint.setSpeed((float) speed);
}
location.setBearing(trackData.lastLocationInCurrentSegment.bearingTo(location));
trackPoint.setBearing(trackData.lastLocationInCurrentSegment.bearingTo(trackPoint));
}
if (!LocationUtils.isValidLocation(location)) {
throw new SAXException(createErrorMessage("Invalid location detected: " + location));
if (!LocationUtils.isValidLocation(trackPoint.getLocation())) {
throw new SAXException(createErrorMessage("Invalid location detected: " + trackPoint));
}
if (trackData.numberOfSegments > 1 && trackData.lastLocationInCurrentSegment == null) {
// If not the first segment, add a resume separator before adding the first location.
insertLocation(createTrackPoint(TrackPointsColumns.RESUME_LATITUDE, 0.0, 0.0, location.getTime()));
insertLocation(TrackPoint.createResumeWithTime(trackPoint.getTime()));
}
trackData.lastLocationInCurrentSegment = location;
return location;
trackData.lastLocationInCurrentSegment = trackPoint;
return trackPoint;
}
/**
@@ -416,31 +413,7 @@ abstract class AbstractFileTrackImporter extends DefaultHandler implements Track
throw new SAXException(createErrorMessage(String.format(Locale.US, "Unable to parse time: %s", time)), e);
}
}
return createTrackPoint(latitudeValue, longitudeValue, altitudeValue, timeValue);
}
/**
* Creates a location.
*
* @param latitudeValue the latitude value
* @param longitudeValue the longitude value
* @param altitudeValue the altitude value
* @param timeValue the time value
*/
private TrackPoint createTrackPoint(double latitudeValue, double longitudeValue, Double altitudeValue, long timeValue) {
TrackPoint location = new TrackPoint(LocationManager.GPS_PROVIDER);
location.setLatitude(latitudeValue);
location.setLongitude(longitudeValue);
if (altitudeValue != null) {
location.setAltitude(altitudeValue);
} else {
location.removeAltitude();
}
location.setTime(timeValue);
location.removeAccuracy();
location.removeBearing();
location.removeSpeed();
return location;
return new TrackPoint(latitudeValue, longitudeValue, altitudeValue, timeValue);
}
/**
@@ -499,7 +472,7 @@ abstract class AbstractFileTrackImporter extends DefaultHandler implements Track
int numberOfSegments = 0;
// The last location in the current segment; Null if the current segment doesn't have a last location
Location lastLocationInCurrentSegment;
TrackPoint lastLocationInCurrentSegment;
// The number of locations processed for the current track
int numberOfLocations = 0;
@@ -311,7 +311,7 @@ public class TrackRecordingService extends Service {
long duration = stats.getTotalTime();
// Insert waypoint
Waypoint waypoint = new Waypoint(name, description, category, icon, recordingTrackId, length, duration, trackPoint, photoUrl);
Waypoint waypoint = new Waypoint(name, description, category, icon, recordingTrackId, length, duration, trackPoint.getLocation(), photoUrl);
Uri uri = contentProviderUtils.insertWaypoint(waypoint);
return ContentUris.parseId(uri);
}
@@ -568,7 +568,8 @@ public class TrackRecordingService extends Service {
return;
}
notificationManager.updateLocation(this, location, recordingGpsAccuracy);
TrackPoint trackPoint = new TrackPoint(location, getSensorDataSet());
notificationManager.updateTrackPoint(this, trackPoint, recordingGpsAccuracy);
if (!location.hasAccuracy() || location.getAccuracy() >= recordingGpsAccuracy) {
Log.d(TAG, "Ignore onLocationChangedAsync. Poor accuracy.");
@@ -583,15 +584,14 @@ public class TrackRecordingService extends Service {
TrackPoint lastValidTrackPoint = getLastValidTrackPointInCurrentSegment(track.getId());
long idleTime = 0L;
if (lastValidTrackPoint != null && location.getTime() > lastValidTrackPoint.getTime()) {
idleTime = location.getTime() - lastValidTrackPoint.getTime();
if (lastValidTrackPoint != null && location.getTime() > lastValidTrackPoint.getLocation().getTime()) {
idleTime = location.getTime() - lastValidTrackPoint.getLocation().getTime();
}
locationListenerPolicy.updateIdleTime(idleTime);
if (currentRecordingInterval != locationListenerPolicy.getDesiredPollingInterval()) {
registerLocationListener();
}
TrackPoint trackPoint = new TrackPoint(location, getSensorDataSet());
// Always insert the first segment location
if (!currentSegmentHasLocation) {
@@ -601,14 +601,14 @@ public class TrackRecordingService extends Service {
return;
}
if (!LocationUtils.isValidLocation(lastValidTrackPoint)) {
if (!LocationUtils.isValidLocation(lastValidTrackPoint.getLocation())) {
// Should not happen. The current segment should have a location. Just insert the current location.
insertTrackPoint(track, trackPoint, null);
lastTrackPoint = trackPoint;
return;
}
double distanceToLastTrackLocation = location.distanceTo(lastValidTrackPoint);
double distanceToLastTrackLocation = location.distanceTo(lastValidTrackPoint.getLocation());
if (distanceToLastTrackLocation > maxRecordingDistance) {
insertTrackPoint(track, lastTrackPoint, lastValidTrackPoint);
insertTrackPoint(track, TrackPoint.createPause(), null);
@@ -637,10 +637,10 @@ public class TrackRecordingService extends Service {
* Inserts a trackPoint.
*
* @param track the track
* @param trackPoint the trackPoint
* @param trackPoint the trackPoint
* @param lastValidTrackPoint the last valid track point, can be null
*/
private void insertTrackPoint(Track track, TrackPoint trackPoint, Location lastValidTrackPoint) {
private void insertTrackPoint(Track track, TrackPoint trackPoint, TrackPoint lastValidTrackPoint) {
if (trackPoint == null) {
Log.w(TAG, "Ignore insertLocation. trackPoint is null.");
return;
@@ -1,8 +1,9 @@
package de.dennisguse.opentracks.services;
import android.location.Location;
import androidx.annotation.VisibleForTesting;
import de.dennisguse.opentracks.content.data.TrackPoint;
import de.dennisguse.opentracks.content.sensor.SensorDataSet;
/**
@@ -82,8 +83,8 @@ class TrackRecordingServiceBinder extends android.os.Binder implements TrackReco
@VisibleForTesting
@Override
public void insertTrackPoint(TrackPoint trackPoint) {
trackRecordingService.onLocationChangedAsync(trackPoint);
public void insertLocation(Location location) {
trackRecordingService.onLocationChangedAsync(location);
}
@Override
@@ -15,9 +15,10 @@
*/
package de.dennisguse.opentracks.services;
import android.location.Location;
import androidx.annotation.VisibleForTesting;
import de.dennisguse.opentracks.content.data.TrackPoint;
import de.dennisguse.opentracks.content.sensor.SensorDataSet;
/**
@@ -95,15 +96,15 @@ public interface TrackRecordingServiceInterface {
/**
* ONLY FOR TESTING
*
* Inserts a track point in the current recording track.
* Inserts a location in the current recording track.
* <p>
* When recording a track, GPS locations are automatically inserted. This is used for
* inserting special track points or for testing.
* When recording a track, GPS locations are automatically inserted.
* This is used for inserting special track points or for testing.
*
* @param trackPoint the track point to be inserted
* @param location the location to be inserted
*/
@VisibleForTesting
void insertTrackPoint(TrackPoint trackPoint);
void insertLocation(Location location);
/**
* Gets the current sensor data. Returns null if there is no data.
@@ -5,13 +5,13 @@ import android.app.NotificationChannel;
import android.app.NotificationManager;
import android.app.PendingIntent;
import android.content.Context;
import android.location.Location;
import android.os.Build;
import androidx.annotation.VisibleForTesting;
import androidx.core.app.NotificationCompat;
import de.dennisguse.opentracks.R;
import de.dennisguse.opentracks.content.data.TrackPoint;
import de.dennisguse.opentracks.util.PreferencesUtils;
import de.dennisguse.opentracks.util.StringUtils;
@@ -63,12 +63,12 @@ class TrackRecordingServiceNotificationManager {
updateNotification();
}
void updateLocation(Context context, Location location, int recordingGpsAccuracy) {
void updateTrackPoint(Context context, TrackPoint trackPoint, int recordingGpsAccuracy) {
String formattedAccuracy = context.getString(R.string.value_none);
if (location.hasAccuracy()) {
formattedAccuracy = StringUtils.formatDistance(context, location.getAccuracy(), PreferencesUtils.isMetricUnits(context));
if (trackPoint.hasAccuracy()) {
formattedAccuracy = StringUtils.formatDistance(context, trackPoint.getAccuracy(), PreferencesUtils.isMetricUnits(context));
boolean currentLocationWasAccurate = location.getAccuracy() < recordingGpsAccuracy;
boolean currentLocationWasAccurate = trackPoint.getAccuracy() < recordingGpsAccuracy;
boolean shouldAlert = !currentLocationWasAccurate && previousLocationWasAccurate;
notificationBuilder.setOnlyAlertOnce(!shouldAlert);
previousLocationWasAccurate = currentLocationWasAccurate;
@@ -16,7 +16,6 @@
package de.dennisguse.opentracks.stats;
import android.location.Location;
import android.util.Log;
import androidx.annotation.VisibleForTesting;
@@ -82,10 +81,10 @@ public class TripStatisticsUpdater {
// The current segment's trip statistics
private TripStatistics currentSegment;
// Current segment's last location.
private Location lastLocation;
// Current segment's last moving location
private Location lastMovingLocation;
// Current segment's last trackPoint
private TrackPoint lastTrackPoint;
// Current segment's last moving trackPoint
private TrackPoint lastMovingTrackPoint;
/**
* Creates a new trip statistics updater.
@@ -131,18 +130,18 @@ public class TripStatisticsUpdater {
*/
public void addTrackPoint(TrackPoint trackPoint, int minRecordingDistance) {
// Always update time
updateTime(trackPoint.getTime());
if (!LocationUtils.isValidLocation(trackPoint)) {
updateTime(trackPoint.getLocation().getTime());
if (!LocationUtils.isValidLocation(trackPoint.getLocation())) {
// Either pause or resume marker
if (trackPoint.getLatitude() == TrackPointsColumns.PAUSE_LATITUDE) {
if (lastLocation != null && lastMovingLocation != null && lastLocation != lastMovingLocation) {
currentSegment.addTotalDistance(lastMovingLocation.distanceTo(lastLocation));
if (trackPoint.getLocation().getLatitude() == TrackPointsColumns.PAUSE_LATITUDE) {
if (lastTrackPoint != null && lastMovingTrackPoint != null && lastTrackPoint != lastMovingTrackPoint) {
currentSegment.addTotalDistance(lastMovingTrackPoint.distanceTo(lastTrackPoint));
}
tripStatistics.merge(currentSegment);
}
currentSegment = init(trackPoint.getTime());
lastLocation = null;
lastMovingLocation = null;
currentSegment = init(trackPoint.getLocation().getTime());
lastTrackPoint = null;
lastMovingTrackPoint = null;
elevationBuffer.reset();
runBuffer.reset();
gradeBuffer.reset();
@@ -153,21 +152,21 @@ public class TripStatisticsUpdater {
//TODO Use Barometer to compute elevation gain.
double elevationDifference = trackPoint.hasAltitude() ? updateElevation(trackPoint.getAltitude()) : 0.0;
if (lastLocation == null || lastMovingLocation == null) {
lastLocation = trackPoint;
lastMovingLocation = trackPoint;
if (lastTrackPoint == null || lastMovingTrackPoint == null) {
lastTrackPoint = trackPoint;
lastMovingTrackPoint = trackPoint;
return;
}
double movingDistance = lastMovingLocation.distanceTo(trackPoint);
double movingDistance = lastMovingTrackPoint.distanceTo(trackPoint);
if (movingDistance < minRecordingDistance && (!trackPoint.hasSpeed() || trackPoint.getSpeed() < MAX_NO_MOVEMENT_SPEED)) {
speedBuffer.reset();
lastLocation = trackPoint;
lastTrackPoint = trackPoint;
return;
}
long movingTime = trackPoint.getTime() - lastLocation.getTime();
long movingTime = trackPoint.getTime() - lastTrackPoint.getTime();
if (movingTime < 0) {
lastLocation = trackPoint;
lastTrackPoint = trackPoint;
return;
}
@@ -178,16 +177,16 @@ public class TripStatisticsUpdater {
currentSegment.addMovingTime(movingTime);
// Update grade
double run = lastLocation.distanceTo(trackPoint);
double run = lastTrackPoint.distanceTo(trackPoint);
updateGrade(run, elevationDifference);
// Update max speed
if (trackPoint.hasSpeed() && lastLocation.hasSpeed()) {
updateSpeed(trackPoint.getTime(), trackPoint.getSpeed(), lastLocation.getTime(), lastLocation.getSpeed());
if (trackPoint.hasSpeed() && lastTrackPoint.hasSpeed()) {
updateSpeed(trackPoint.getTime(), trackPoint.getSpeed(), lastTrackPoint.getTime(), lastTrackPoint.getSpeed());
}
lastLocation = trackPoint;
lastMovingLocation = trackPoint;
lastTrackPoint = trackPoint;
lastMovingTrackPoint = trackPoint;
}
public void addTrackPoint(TrackPointIterator iterator, int minRecordingDistance) {
@@ -115,8 +115,7 @@ public class LocationUtils {
current = stack.pop();
maxDist = 0;
for (idx = current[0] + 1; idx < current[1]; ++idx) {
dist = LocationUtils.distance(
trackPoints.get(idx), trackPoints.get(current[0]), trackPoints.get(current[1]));
dist = LocationUtils.distance(trackPoints.get(idx).getLocation(), trackPoints.get(current[0]).getLocation(), trackPoints.get(current[1]).getLocation());
if (dist > maxDist) {
maxDist = dist;
maxIdx = idx;
@@ -165,7 +164,7 @@ public class LocationUtils {
* @param location the location to test
* @return true if the location is a valid location.
*/
//TODO Still needed?
//TODO Move to TrackPoint
public static boolean isValidLocation(Location location) {
return location != null
&& Math.abs(location.getLatitude()) <= 90