diff --git a/src/androidTest/java/de/dennisguse/opentracks/io/file/importer/ExportImportTest.java b/src/androidTest/java/de/dennisguse/opentracks/io/file/importer/ExportImportTest.java
index 9669a2cea..f86bad646 100644
--- a/src/androidTest/java/de/dennisguse/opentracks/io/file/importer/ExportImportTest.java
+++ b/src/androidTest/java/de/dennisguse/opentracks/io/file/importer/ExportImportTest.java
@@ -65,6 +65,7 @@ import de.dennisguse.opentracks.sensors.sensorData.AggregatorCyclingCadence;
import de.dennisguse.opentracks.sensors.sensorData.AggregatorCyclingDistanceSpeed;
import de.dennisguse.opentracks.sensors.sensorData.AggregatorCyclingPower;
import de.dennisguse.opentracks.sensors.sensorData.AggregatorHeartRate;
+import de.dennisguse.opentracks.sensors.sensorData.Raw;
import de.dennisguse.opentracks.sensors.sensorData.SensorDataSet;
import de.dennisguse.opentracks.services.TrackRecordingService;
import de.dennisguse.opentracks.services.handlers.TrackPointCreator;
@@ -604,6 +605,6 @@ public class ExportImportTest {
mockAltitudeChange(trackPointCreator, altitudeGain);
trackPointCreator.setClock(time);
- trackPointCreator.onChange(location);
+ trackPointCreator.getSensorManager().onChanged(new Raw<>(location));
}
}
\ No newline at end of file
diff --git a/src/androidTest/java/de/dennisguse/opentracks/services/TrackRecordingServiceRecordingTest.java b/src/androidTest/java/de/dennisguse/opentracks/services/TrackRecordingServiceRecordingTest.java
index 82c75ce8e..09d041464 100644
--- a/src/androidTest/java/de/dennisguse/opentracks/services/TrackRecordingServiceRecordingTest.java
+++ b/src/androidTest/java/de/dennisguse/opentracks/services/TrackRecordingServiceRecordingTest.java
@@ -415,6 +415,10 @@ public class TrackRecordingServiceRecordingTest {
.setAltitudeGain(0f)
.setAltitudeLoss(0f),
new TrackPoint(TrackPoint.Type.SEGMENT_END_MANUAL, Instant.parse(stopTime))
+ .setLatitude(45.00002)
+ .setLongitude(35)
+ .setHorizontalAccuracy(Distance.of(1))
+ .setSpeed(Speed.of(15))
.setAltitudeGain(0f)
.setAltitudeLoss(0f)
), TestDataUtil.getTrackPoints(contentProviderUtils, trackId));
@@ -667,6 +671,9 @@ public class TrackRecordingServiceRecordingTest {
.setSpeed(Speed.of(5))
.setSensorDistance(Distance.of(0)),
new TrackPoint(TrackPoint.Type.SEGMENT_END_MANUAL, Instant.parse(stopTime))
+ .setLatitude(45.001)
+ .setLongitude(35)
+ .setHorizontalAccuracy(Distance.of(1))
.setSensorDistance(Distance.of(11))
.setSpeed(Speed.of(5))
.setSensorDistance(Distance.of(0))
diff --git a/src/androidTest/java/de/dennisguse/opentracks/services/handlers/GPSManagerTest.java b/src/androidTest/java/de/dennisguse/opentracks/services/handlers/GPSManagerTest.java
deleted file mode 100644
index 2021c424d..000000000
--- a/src/androidTest/java/de/dennisguse/opentracks/services/handlers/GPSManagerTest.java
+++ /dev/null
@@ -1,126 +0,0 @@
-package de.dennisguse.opentracks.services.handlers;
-
-import static org.mockito.ArgumentMatchers.any;
-import static org.mockito.Mockito.times;
-import static org.mockito.Mockito.verify;
-
-import android.content.Context;
-import android.location.Location;
-import android.os.Handler;
-import android.os.Looper;
-
-import androidx.test.core.app.ApplicationProvider;
-
-import org.junit.Before;
-import org.junit.BeforeClass;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-import org.mockito.InjectMocks;
-import org.mockito.Mock;
-import org.mockito.Mockito;
-import org.mockito.junit.MockitoJUnitRunner;
-
-import java.time.Instant;
-
-import de.dennisguse.opentracks.data.models.Distance;
-import de.dennisguse.opentracks.settings.PreferencesUtils;
-
-@RunWith(MockitoJUnitRunner.class)
-public class GPSManagerTest {
-
- private final Context context = ApplicationProvider.getApplicationContext();
-
- @Mock
- private TrackPointCreator trackPointCreator;
-
- @InjectMocks
- private GPSManager locationHandler;
-
- @BeforeClass
- public static void preSetUp() {
- // Prepare looper for Android's message queue
- if (Looper.myLooper() == null) Looper.prepare();
- }
-
- @Before
- public void setUp() {
- // Let's use default values.
- PreferencesUtils.clear();
-
- Mockito.when(trackPointCreator.createNow())
- .thenReturn(Instant.now());
-
- locationHandler.start(context, new Handler());
- }
-
- /**
- * When a valid location changed in LocationHandler -> newTrackPoint service method is called.
- */
- @Test
- public void testOnLocationChanged_okay() {
- // when
- locationHandler.onLocationChanged(createLocation(45f, 35f, 3, 5, System.currentTimeMillis()));
-
- // then
- verify(trackPointCreator, times(1)).onChange(any(Location.class));
- }
-
- /**
- * When location changed in LocationHandler with bad location -> newTrackPoint service method is not called.
- */
- @Test
- public void testOnLocationChanged_badLocation() {
- // given
- float latitude = 91f;
-
- // when
- // bad latitude
- locationHandler.onLocationChanged(createLocation(latitude, 35f, 3, 5, System.currentTimeMillis()));
-
- // then
- verify(trackPointCreator, times(0)).onChange(any(Location.class));
- }
-
- /**
- * When location changed in LocationHandler with poor accuracy -> newTrackPoint service method is not called.
- */
- @Test
- public void testOnLocationChanged_poorAccuracy() {
- // given
- Distance prefAccuracy = PreferencesUtils.getThresholdHorizontalAccuracy();
-
- // when
- locationHandler.onLocationChanged(createLocation(45f, 35f, (float) (prefAccuracy.toM() + 1), 5, System.currentTimeMillis()));
-
- // then
- // no newTrackPoint called
- verify(trackPointCreator, times(0)).onChange(any(Location.class));
- }
-
- @Test
- public void testOnLocationChanged_movingInaccurate() {
- // when
- locationHandler.onLocationChanged(createLocation(45.0, 35.0, 5, 15, System.currentTimeMillis()));
- locationHandler.onLocationChanged(createLocation(45.1, 35.0, Long.MAX_VALUE, 15, System.currentTimeMillis()));
- locationHandler.onLocationChanged(createLocation(45.2, 35.0, Long.MAX_VALUE, 15, System.currentTimeMillis()));
- locationHandler.onLocationChanged(createLocation(45.3, 35.0, Long.MAX_VALUE, 15, System.currentTimeMillis()));
- locationHandler.onLocationChanged(createLocation(99.0, 35.0, Long.MAX_VALUE, 15, System.currentTimeMillis()));
-
- // then
- verify(trackPointCreator, times(1)).onChange(any(Location.class));
- }
-
- /**
- * Creates a location with parameters and returns the Location object.
- */
- private static Location createLocation(double latitude, double longitude, float accuracy, long speed, long time) {
- Location location = new Location("gps");
- location.setLongitude(longitude);
- location.setLatitude(latitude);
- location.setAccuracy(accuracy);
- location.setSpeed(speed);
- location.setTime(time);
- location.setBearing(3.0f);
- return location;
- }
-}
\ No newline at end of file
diff --git a/src/main/java/de/dennisguse/opentracks/sensors/SensorManager.java b/src/main/java/de/dennisguse/opentracks/sensors/SensorManager.java
index a4815ba0b..8a2908b24 100644
--- a/src/main/java/de/dennisguse/opentracks/sensors/SensorManager.java
+++ b/src/main/java/de/dennisguse/opentracks/sensors/SensorManager.java
@@ -30,7 +30,6 @@ public class SensorManager implements SharedPreferences.OnSharedPreferenceChange
@Override
public void onConnect(Aggregator, ?> aggregator) {
sensorDataSet.add(aggregator);
- observer.onChange(new SensorDataSet(sensorDataSet));
}
@Override
@@ -42,13 +41,11 @@ public class SensorManager implements SharedPreferences.OnSharedPreferenceChange
@Override
public void onDisconnect(Aggregator, ?> aggregator) {
sensorDataSet.add(aggregator);
- observer.onChange(new SensorDataSet(sensorDataSet));
}
@Override
public void onRemove(Aggregator, ?> aggregator) {
sensorDataSet.remove(aggregator);
- observer.onChange(new SensorDataSet(sensorDataSet));
}
};
@@ -67,7 +64,7 @@ public class SensorManager implements SharedPreferences.OnSharedPreferenceChange
throw new RuntimeException("SensorManager cannot be started twice; stop first.");
}
- gpsManager = new GPSManager(observer); //TODO Pass listener
+ gpsManager = new GPSManager(observer, listener);
altitudeSumManager = new GainManager(listener);
bluetoothSensorManager = new BluetoothRemoteSensorManager(context, handler, listener);
@@ -104,7 +101,6 @@ public class SensorManager implements SharedPreferences.OnSharedPreferenceChange
sensorDataSet.reset();
}
- @Deprecated
@VisibleForTesting
public void onChanged(Raw> data) {
listener.onChange(data);
diff --git a/src/main/java/de/dennisguse/opentracks/sensors/sensorData/Aggregator.java b/src/main/java/de/dennisguse/opentracks/sensors/sensorData/Aggregator.java
index 82ae79c2f..91be573e1 100644
--- a/src/main/java/de/dennisguse/opentracks/sensors/sensorData/Aggregator.java
+++ b/src/main/java/de/dennisguse/opentracks/sensors/sensorData/Aggregator.java
@@ -6,7 +6,7 @@ import java.time.Instant;
import de.dennisguse.opentracks.sensors.BluetoothRemoteSensorManager;
-public abstract class Aggregator {
+public abstract class Aggregator {
protected Raw previous;
diff --git a/src/main/java/de/dennisguse/opentracks/sensors/sensorData/AggregatorGPS.java b/src/main/java/de/dennisguse/opentracks/sensors/sensorData/AggregatorGPS.java
new file mode 100644
index 000000000..71c2aca87
--- /dev/null
+++ b/src/main/java/de/dennisguse/opentracks/sensors/sensorData/AggregatorGPS.java
@@ -0,0 +1,28 @@
+package de.dennisguse.opentracks.sensors.sensorData;
+
+import android.location.Location;
+
+import androidx.annotation.NonNull;
+
+public class AggregatorGPS extends Aggregator {
+
+ public AggregatorGPS(String sensorAddress) {
+ super(sensorAddress);
+ }
+
+ @Override
+ protected void computeValue(Raw current) {
+ value = current.value();
+ }
+
+ @Override
+ public void reset() {
+ value = null;
+ }
+
+ @NonNull
+ @Override
+ protected Location getNoneValue() {
+ return new Location("none");
+ }
+}
diff --git a/src/main/java/de/dennisguse/opentracks/sensors/sensorData/Raw.java b/src/main/java/de/dennisguse/opentracks/sensors/sensorData/Raw.java
index 5f88bb229..125d2268f 100644
--- a/src/main/java/de/dennisguse/opentracks/sensors/sensorData/Raw.java
+++ b/src/main/java/de/dennisguse/opentracks/sensors/sensorData/Raw.java
@@ -5,7 +5,7 @@ import androidx.annotation.NonNull;
import java.time.Instant;
-public record Raw(
+public record Raw(
@NonNull T value,
@NonNull Instant time
diff --git a/src/main/java/de/dennisguse/opentracks/sensors/sensorData/SensorDataSet.java b/src/main/java/de/dennisguse/opentracks/sensors/sensorData/SensorDataSet.java
index 692aecf77..8b423fdba 100644
--- a/src/main/java/de/dennisguse/opentracks/sensors/sensorData/SensorDataSet.java
+++ b/src/main/java/de/dennisguse/opentracks/sensors/sensorData/SensorDataSet.java
@@ -1,5 +1,6 @@
package de.dennisguse.opentracks.sensors.sensorData;
+import android.location.Location;
import android.util.Log;
import android.util.Pair;
@@ -40,16 +41,20 @@ public final class SensorDataSet {
@VisibleForTesting
public AggregatorBarometer barometer;
+ public AggregatorGPS gps;
+
public SensorDataSet() {
}
public SensorDataSet(SensorDataSet toCopy) {
+ //TODO This is not a copy constructor anymore, but it should be - aggregators are no value objects
this.heartRate = toCopy.heartRate;
this.cyclingCadence = toCopy.cyclingCadence;
this.cyclingDistanceSpeed = toCopy.cyclingDistanceSpeed;
this.cyclingPower = toCopy.cyclingPower;
this.runningDistanceSpeedCadence = toCopy.runningDistanceSpeedCadence;
this.barometer = toCopy.barometer;
+ this.gps = toCopy.gps;
}
public Pair getHeartRate() {
@@ -93,7 +98,7 @@ public final class SensorDataSet {
}
public void update(@NonNull Raw> data) {
- Record value = data.value();
+ Object value = data.value();
if (value instanceof HeartRate) {
this.heartRate.add((Raw) data);
@@ -122,6 +127,10 @@ public final class SensorDataSet {
this.barometer.add((Raw) data);
return;
}
+ if (value instanceof Location) {
+ this.gps.add((Raw) data);
+ return;
+ }
throw new UnsupportedOperationException(data.getClass().getCanonicalName() + " " + data.value().getClass().getCanonicalName());
}
@@ -138,9 +147,14 @@ public final class SensorDataSet {
this.cyclingPower = null;
this.runningDistanceSpeedCadence = null;
this.barometer = null;
+ this.gps = null;
}
public void fillTrackPoint(TrackPoint trackPoint) {
+ if (gps != null && gps.hasValue()) {
+ trackPoint.setLocation(gps.getValue());
+ }
+
if (getHeartRate() != null) {
trackPoint.setHeartRate(getHeartRate().first);
}
@@ -180,6 +194,7 @@ public final class SensorDataSet {
if (cyclingPower != null) cyclingPower.reset();
if (runningDistanceSpeedCadence != null) runningDistanceSpeedCadence.reset();
if (barometer != null) barometer.reset();
+ if (gps != null) gps.reset();
}
private void set(@NonNull Aggregator, ?> type, @Nullable Aggregator, ?> sensorData) {
@@ -209,6 +224,10 @@ public final class SensorDataSet {
barometer = (AggregatorBarometer) sensorData;
return;
}
+ if (type instanceof AggregatorGPS) {
+ gps = (AggregatorGPS) sensorData;
+ return;
+ }
throw new UnsupportedOperationException(type.getClass().getCanonicalName());
}
diff --git a/src/main/java/de/dennisguse/opentracks/services/handlers/GPSManager.java b/src/main/java/de/dennisguse/opentracks/services/handlers/GPSManager.java
index c000292f9..c2b6dce93 100644
--- a/src/main/java/de/dennisguse/opentracks/services/handlers/GPSManager.java
+++ b/src/main/java/de/dennisguse/opentracks/services/handlers/GPSManager.java
@@ -20,6 +20,9 @@ import de.dennisguse.opentracks.R;
import de.dennisguse.opentracks.data.models.Distance;
import de.dennisguse.opentracks.data.models.TrackPoint;
import de.dennisguse.opentracks.sensors.SensorConnector;
+import de.dennisguse.opentracks.sensors.SensorManager;
+import de.dennisguse.opentracks.sensors.sensorData.AggregatorGPS;
+import de.dennisguse.opentracks.sensors.sensorData.Raw;
import de.dennisguse.opentracks.settings.PreferencesUtils;
import de.dennisguse.opentracks.util.LocationUtils;
import de.dennisguse.opentracks.util.PermissionRequester;
@@ -32,6 +35,8 @@ public class GPSManager implements SensorConnector, LocationListenerCompat, GpsS
private static final String LOCATION_PROVIDER = LocationManager.GPS_PROVIDER;
private TrackPointCreator trackPointCreator;
+
+ private SensorManager.SensorDataChangedObserver listener;
private Context context;
private Handler handler;
@@ -40,8 +45,9 @@ public class GPSManager implements SensorConnector, LocationListenerCompat, GpsS
private Duration gpsInterval;
private Distance thresholdHorizontalAccuracy;
- public GPSManager(TrackPointCreator trackPointCreator) {
+ public GPSManager(TrackPointCreator trackPointCreator, SensorManager.SensorDataChangedObserver listener) {
this.trackPointCreator = trackPointCreator;
+ this.listener = listener;
}
public void start(@NonNull Context context, @NonNull Handler handler) {
@@ -52,6 +58,9 @@ public class GPSManager implements SensorConnector, LocationListenerCompat, GpsS
gpsStatusManager = new GpsStatusManager(context, this, handler);
locationManager = (LocationManager) context.getSystemService(Context.LOCATION_SERVICE);
+
+ listener.onConnect(new AggregatorGPS("internal"));
+
registerLocationListener();
gpsStatusManager.start();
}
@@ -72,6 +81,9 @@ public class GPSManager implements SensorConnector, LocationListenerCompat, GpsS
gpsStatusManager.stop();
gpsStatusManager = null;
+ listener.onDisconnect(new AggregatorGPS("internal"));
+ listener = null;
+
trackPointCreator = null;
}
@@ -126,7 +138,7 @@ public class GPSManager implements SensorConnector, LocationListenerCompat, GpsS
return;
}
- trackPointCreator.onChange(location);
+ listener.onChange(new Raw<>(location));
}
@Override
diff --git a/src/main/java/de/dennisguse/opentracks/services/handlers/TrackPointCreator.java b/src/main/java/de/dennisguse/opentracks/services/handlers/TrackPointCreator.java
index f779e6ddc..6e521404a 100644
--- a/src/main/java/de/dennisguse/opentracks/services/handlers/TrackPointCreator.java
+++ b/src/main/java/de/dennisguse/opentracks/services/handlers/TrackPointCreator.java
@@ -2,7 +2,6 @@ package de.dennisguse.opentracks.services.handlers;
import android.content.Context;
import android.content.SharedPreferences;
-import android.location.Location;
import android.os.Handler;
import android.util.Log;
import android.util.Pair;
@@ -69,10 +68,6 @@ public class TrackPointCreator implements SharedPreferences.OnSharedPreferenceCh
this.context = null;
}
- public synchronized void onChange(@NonNull Location location) {
- onNewTrackPoint(new TrackPoint(location, createNow()));
- }
-
/**
* Got a new TrackPoint from Bluetooth only; contains no GPS location.
*/