From b651a8ca6a36733d70ecfc24fedc6b4d093e857c Mon Sep 17 00:00:00 2001 From: Dennis Guse Date: Wed, 4 Feb 2026 22:37:19 +0100 Subject: [PATCH] GPS is now optional: data source (internal, none) be selected in settings. Migrated to GPSHandler. Fixes #1279. --- .../io/file/importer/ExportImportTest.java | 13 +- .../TrackRecordingServiceRecordingTest.java | 7 +- .../{GpsManager.java => GpsHandler.java} | 150 +++++++----------- .../opentracks/sensors/GpsStatusManager.java | 16 +- .../opentracks/sensors/SensorHandler.java | 13 +- .../opentracks/sensors/SensorManager.java | 84 +++++----- .../sensors/driver/BluetoothDriver.java | 1 + .../sensors/driver/DriverObserver.java | 6 + .../sensors/driver/GpsInternal.java | 13 +- .../sensors/sensorData/Aggregator.java | 4 - .../sensors/sensorData/AggregatorGPS.java | 12 +- .../opentracks/settings/PreferencesUtils.java | 4 - .../bluetooth/BluetoothLeGpsPreference.java | 41 +++++ src/main/res/values/settings.xml | 1 + src/main/res/xml/settings_sensors.xml | 9 +- 15 files changed, 203 insertions(+), 171 deletions(-) rename src/main/java/de/dennisguse/opentracks/sensors/{GpsManager.java => GpsHandler.java} (52%) create mode 100644 src/main/java/de/dennisguse/opentracks/settings/bluetooth/BluetoothLeGpsPreference.java 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 8315d5e8a..bf12ed3ab 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 @@ -77,6 +77,7 @@ import de.dennisguse.opentracks.sensors.sensorData.AggregatorHeartRate; import de.dennisguse.opentracks.sensors.sensorData.AggregatorTemperature; import de.dennisguse.opentracks.services.TrackRecordingService; import de.dennisguse.opentracks.services.handlers.TrackPointCreator; +import de.dennisguse.opentracks.settings.PreferencesUtils; /** * Export a track to {@link TrackFileFormat} and verify that the import is identical. @@ -88,7 +89,7 @@ import de.dennisguse.opentracks.services.handlers.TrackPointCreator; public class ExportImportTest { @Rule - public final ServiceTestRule mServiceRule = ServiceTestRule.withTimeout(5, TimeUnit.SECONDS); + public final ServiceTestRule mServiceRule = ServiceTestRule.withTimeout(50, TimeUnit.SECONDS); //TODO 5s @Rule public GrantPermissionRule mGrantPermissionRule = TestUtil.createGrantPermissionRule(); @@ -97,13 +98,15 @@ public class ExportImportTest { @Rule public TimezoneRule timezoneRule = new TimezoneRule(TimeZone.getTimeZone("Europe/Berlin")); + private static final Context context = ApplicationProvider.getApplicationContext(); + @BeforeClass public static void preSetUp() { // Prepare looper for Android's message queue if (Looper.myLooper() == null) Looper.prepare(); - } - private final Context context = ApplicationProvider.getApplicationContext(); + PreferencesUtils.resetPreferences(context, true); + } private final ContentProviderUtils contentProviderUtils = new ContentProviderUtils(context); @@ -161,7 +164,7 @@ public class ExportImportTest { "Marker 1 desc", "Marker 1 typeLocalized", new Position( - Instant.parse("2020-02-02T02:02:03Z"), + Instant.parse("2020-02-02T02:02:03Z"), 3.1234567, 14.0014567, Distance.of(10), Altitude.WGS84.of(1020.25), @@ -756,6 +759,6 @@ public class ExportImportTest { mockAltitudeChange(trackPointCreator, altitudeGain); trackPointCreator.setClock(time); - trackPointCreator.getSensorManager().getGpsManager().onDataReceived(location); + trackPointCreator.getSensorManager().getGpsHandler().onDataReceived(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 9b94d2595..27c54e79c 100644 --- a/src/androidTest/java/de/dennisguse/opentracks/services/TrackRecordingServiceRecordingTest.java +++ b/src/androidTest/java/de/dennisguse/opentracks/services/TrackRecordingServiceRecordingTest.java @@ -9,6 +9,7 @@ import android.location.Location; import android.os.Looper; import androidx.annotation.NonNull; +import androidx.core.location.LocationManagerCompat; import androidx.test.core.app.ApplicationProvider; import androidx.test.ext.junit.runners.AndroidJUnit4; import androidx.test.filters.MediumTest; @@ -43,6 +44,7 @@ import de.dennisguse.opentracks.data.models.Track; import de.dennisguse.opentracks.data.models.TrackPoint; import de.dennisguse.opentracks.io.file.importer.TrackPointAssert; import de.dennisguse.opentracks.sensors.SensorManager; +import de.dennisguse.opentracks.sensors.driver.GpsInternal; import de.dennisguse.opentracks.sensors.driver.RunningSpeedAndCadenceBluetooth; import de.dennisguse.opentracks.sensors.sensorData.AggregatorBarometer; import de.dennisguse.opentracks.sensors.sensorData.AggregatorHeartRate; @@ -151,7 +153,8 @@ public class TrackRecordingServiceRecordingTest { Track.Id trackId = service.startNewTrack(); //We do not want the real GPS to interfere. - trackPointCreator.getSensorManager().getGpsManager().driver.disconnect(); + GpsInternal gpsInternal = (GpsInternal)trackPointCreator.getSensorManager().getGpsHandler().driver; + LocationManagerCompat.removeUpdates(gpsInternal.locationManager, gpsInternal.locationListenerCompat); String gps1 = "2020-02-02T02:02:03Z"; sendGPSLocation(trackPointCreator, gps1, 45.0, 35.0, 1, 15); @@ -995,6 +998,6 @@ public class TrackRecordingServiceRecordingTest { location.setSpeed(speed); trackPointCreator.setClock(time); - trackPointCreator.getSensorManager().getGpsManager().onDataReceived(location); + trackPointCreator.getSensorManager().getGpsHandler().onDataReceived(location); } } diff --git a/src/main/java/de/dennisguse/opentracks/sensors/GpsManager.java b/src/main/java/de/dennisguse/opentracks/sensors/GpsHandler.java similarity index 52% rename from src/main/java/de/dennisguse/opentracks/sensors/GpsManager.java rename to src/main/java/de/dennisguse/opentracks/sensors/GpsHandler.java index c89938007..ff2527720 100644 --- a/src/main/java/de/dennisguse/opentracks/sensors/GpsManager.java +++ b/src/main/java/de/dennisguse/opentracks/sensors/GpsHandler.java @@ -1,9 +1,7 @@ package de.dennisguse.opentracks.sensors; -import android.content.Context; import android.content.SharedPreferences; import android.location.Location; -import android.os.Handler; import android.util.Log; import androidx.annotation.NonNull; @@ -12,95 +10,25 @@ import androidx.annotation.VisibleForTesting; import de.dennisguse.opentracks.R; import de.dennisguse.opentracks.data.models.Distance; import de.dennisguse.opentracks.data.models.Position; -import de.dennisguse.opentracks.sensors.driver.DriverObserver; +import de.dennisguse.opentracks.sensors.driver.Driver; import de.dennisguse.opentracks.sensors.driver.GpsInternal; +import de.dennisguse.opentracks.sensors.driver.SensorType; import de.dennisguse.opentracks.sensors.sensorData.AggregatorGPS; import de.dennisguse.opentracks.services.handlers.TrackPointCreator; import de.dennisguse.opentracks.settings.PreferencesUtils; @VisibleForTesting(otherwise = VisibleForTesting.PACKAGE_PRIVATE) -public class GpsManager implements DriverObserver, GpsStatusManager.GpsStatusListener, SharedPreferences.OnSharedPreferenceChangeListener { +public class GpsHandler extends SensorHandler { - private final String TAG = GpsManager.class.getSimpleName(); + private final String TAG = GpsHandler.class.getSimpleName(); //TODO Refactor to just pass information via SensorManager. private TrackPointCreator trackPointCreator; - private SensorManager observer; - private Context context; - private Handler handler; - - @VisibleForTesting - public GpsInternal driver; private GpsStatusManager gpsStatusManager; private Distance thresholdHorizontalAccuracy; - private AggregatorGPS aggregatorGPS; - - public GpsManager(TrackPointCreator trackPointCreator, SensorManager observer) { - this.trackPointCreator = trackPointCreator; - this.observer = observer; - } - - public void start(@NonNull Context context, @NonNull Handler handler) { - this.context = context; - this.handler = handler; - - onSharedPreferenceChanged(null, null); - - gpsStatusManager = new GpsStatusManager(context, this, handler); - - this.aggregatorGPS = new AggregatorGPS("internal"); - - this.driver = new GpsInternal(this); - this.driver.connect(context, handler, "internal"); - gpsStatusManager.start(); - } - - @SuppressWarnings({"MissingPermission"}) - public void stop() { - if (driver.isConnected()) { - driver.disconnect(); - } - driver = null; - this.context = null; - handler = null; - - gpsStatusManager.stop(); - gpsStatusManager = null; - - this.aggregatorGPS = null; - observer = null; - - trackPointCreator = null; - } - - //TODO Exposed for refactoring. Is this necessary? - @Deprecated - AggregatorGPS getAggregatorGPS() { - return aggregatorGPS; - } - - //TODO Exposed for refactoring. This is very ugly. - @Deprecated - void setAggregatorGPS() { - this.aggregatorGPS = null; - } - - @Override - public void onSharedPreferenceChanged(SharedPreferences sharedPreferences, String key) { - boolean registerListener = false; - - if (PreferencesUtils.isKey(R.string.min_sampling_interval_key, key)) { - registerListener = true; - - if (gpsStatusManager != null) { - gpsStatusManager.onMinSamplingIntervalChanged(PreferencesUtils.getMinSamplingInterval()); - } - } - if (PreferencesUtils.isKey(R.string.recording_gps_accuracy_key, key)) { - thresholdHorizontalAccuracy = PreferencesUtils.getThresholdHorizontalAccuracy(); - } + private final SharedPreferences.OnSharedPreferenceChangeListener sharedPreferenceChangeListener = (sharedPreferences, key) -> { if (PreferencesUtils.isKey(R.string.recording_distance_interval_key, key)) { if (gpsStatusManager != null) { Distance gpsMinDistance = PreferencesUtils.getRecordingDistanceInterval(); @@ -108,49 +36,85 @@ public class GpsManager implements DriverObserver, GpsStatusManager.Gp } } - if (registerListener && driver != null && driver.isConnected()) { - driver.disconnect(); - driver.connect(context, handler, "internal"); + if (PreferencesUtils.isKey(R.string.recording_gps_accuracy_key, key)) { + thresholdHorizontalAccuracy = PreferencesUtils.getThresholdHorizontalAccuracy(); } - } - @Override - public void onGpsStatusChanged(GpsStatusValue currentStatus) { - trackPointCreator.sendGpsStatus(currentStatus); + if (PreferencesUtils.isKey(R.string.min_sampling_interval_key, key)) { + if (gpsStatusManager != null) { + gpsStatusManager.onMinSamplingIntervalChanged(PreferencesUtils.getMinSamplingInterval()); + } + + //TODO + connect(); + } + }; + + public GpsHandler(SensorManager sensorManager, TrackPointCreator trackPointCreator) { + super(sensorManager); + this.trackPointCreator = trackPointCreator; + gpsStatusManager = new GpsStatusManager(sensorManager.getContext(), trackPointCreator::sendGpsStatus, sensorManager.getHandler()); + + sharedPreferenceChangeListener.onSharedPreferenceChanged(null, null); } @Override public void onConnected(String sensorAddress, String sensorName) { + super.onConnected(sensorAddress, sensorName); gpsStatusManager.onGpsEnabled(); - } - - @Override - public void onConnectionLost() { - gpsStatusManager.onGpsDisabled(); + gpsStatusManager.start(); } @Override public void onDisconnected() { + super.onDisconnected(); + gpsStatusManager.stop(); + } + + @Override + public void onSensorDeactivated() { + super.onSensorDeactivated(); gpsStatusManager.onGpsDisabled(); } @Override public void onDataReceived(Location location) { // Send each update to the status; please note that this TrackPoint is not stored. - Position position = Position.of(location, observer.getNow()); + Position position = Position.of(location, trackPointCreator.getNow()); gpsStatusManager.onNewTrackPoint(position); + //TODO We could move the following check into the AggregatorGPS if (!position.hasValidLocation()) { Log.w(TAG, "Ignore newTrackPoint. Location is invalid."); return; } - if (!position.fulfillsAccuracy(thresholdHorizontalAccuracy)) { Log.d(TAG, "Ignore newTrackPoint. Poor accuracy."); return; } - aggregatorGPS.add(observer.getNow(), position); - observer.onChange(); + super.onDataReceived(location); + } + + @NonNull + @Override + protected Driver createDriverInternal() { + return new GpsInternal(this); + } + + @NonNull + @Override + protected AggregatorGPS createAggregator(String sensorAddress, String sensorName) { + return new AggregatorGPS(sensorAddress, sensorName); + } + + @Override + protected int getSensorPreferenceKey() { + return R.string.settings_sensor_bluetooth_gps_key; + } + + @Override + protected String getSensorPreferenceDefaultValue() { + return SensorType.INTERNAL.getPreferenceValue(); } } diff --git a/src/main/java/de/dennisguse/opentracks/sensors/GpsStatusManager.java b/src/main/java/de/dennisguse/opentracks/sensors/GpsStatusManager.java index 2b2296c35..5a5154d95 100644 --- a/src/main/java/de/dennisguse/opentracks/sensors/GpsStatusManager.java +++ b/src/main/java/de/dennisguse/opentracks/sensors/GpsStatusManager.java @@ -31,7 +31,7 @@ class GpsStatusManager { private Duration signalLostThreshold; private GpsStatusValue gpsStatus = GpsStatusValue.GPS_NONE; - private GpsStatusListener client; + private GpsStatusListener observer; private final Context context; @Nullable @@ -44,8 +44,8 @@ class GpsStatusManager { }; - public GpsStatusManager(Context context, GpsStatusListener client, Handler handler) { - this.client = client; + public GpsStatusManager(Context context, GpsStatusListener observer, Handler handler) { + this.observer = observer; this.context = context; this.handler = handler; @@ -54,7 +54,7 @@ class GpsStatusManager { } public void start() { - client.onGpsStatusChanged(GpsStatusValue.GPS_ENABLED); + observer.onGpsStatusChanged(GpsStatusValue.GPS_ENABLED); } /** @@ -62,9 +62,7 @@ class GpsStatusManager { */ public void stop() { stopTimer(); - client.onGpsStatusChanged(GpsStatusValue.GPS_NONE); - client = null; - handler = null; + observer.onGpsStatusChanged(GpsStatusValue.GPS_NONE); } /** @@ -159,8 +157,8 @@ class GpsStatusManager { private void setGpsStatus(GpsStatusValue current) { gpsStatus = current; - if (client != null) { - client.onGpsStatusChanged(current); + if (observer != null) { + observer.onGpsStatusChanged(current); } } diff --git a/src/main/java/de/dennisguse/opentracks/sensors/SensorHandler.java b/src/main/java/de/dennisguse/opentracks/sensors/SensorHandler.java index 7d2003b3f..a94036425 100644 --- a/src/main/java/de/dennisguse/opentracks/sensors/SensorHandler.java +++ b/src/main/java/de/dennisguse/opentracks/sensors/SensorHandler.java @@ -10,6 +10,7 @@ import java.time.Instant; import de.dennisguse.opentracks.sensors.driver.Driver; import de.dennisguse.opentracks.sensors.driver.DriverObserver; +import de.dennisguse.opentracks.sensors.driver.SensorType; import de.dennisguse.opentracks.sensors.sensorData.Aggregator; import de.dennisguse.opentracks.sensors.sensorData.SensorData; import de.dennisguse.opentracks.settings.PreferencesUtils; @@ -19,7 +20,8 @@ public abstract class SensorHandler implement private final SensorManager sensorManager; - private Driver driver; + @VisibleForTesting + public Driver driver; protected Aggregator aggregator; @@ -30,7 +32,7 @@ public abstract class SensorHandler implement void connect() { disconnect(); - String address = PreferencesUtils.getString(getSensorPreferenceKey(), PreferencesUtils.getBluetoothSensorAddressNone()); + String address = PreferencesUtils.getString(getSensorPreferenceKey(), getSensorPreferenceDefaultValue()); switch (PreferencesUtils.getSensorType(address)) { case NONE -> { @@ -71,6 +73,10 @@ public abstract class SensorHandler implement protected abstract int getSensorPreferenceKey(); + protected String getSensorPreferenceDefaultValue() { + return SensorType.NONE.getPreferenceValue(); + } + protected Context getContext() { return sensorManager.getContext(); } @@ -106,6 +112,9 @@ public abstract class SensorHandler implement @Override public void onDataReceived(AggregatorInput value) { + if (aggregator == null) { + throw new RuntimeException("Received data while being disconnected."); + } SensorHandler.this.aggregator.add(sensorManager.getNow(), value); sensorManager.onChange(); } diff --git a/src/main/java/de/dennisguse/opentracks/sensors/SensorManager.java b/src/main/java/de/dennisguse/opentracks/sensors/SensorManager.java index eb850db69..6021848a7 100644 --- a/src/main/java/de/dennisguse/opentracks/sensors/SensorManager.java +++ b/src/main/java/de/dennisguse/opentracks/sensors/SensorManager.java @@ -20,7 +20,6 @@ import de.dennisguse.opentracks.data.models.Speed; 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.AggregatorGPS; import de.dennisguse.opentracks.sensors.sensorData.AggregatorHeartRate; import de.dennisguse.opentracks.sensors.sensorData.AggregatorRunning; import de.dennisguse.opentracks.sensors.sensorData.AggregatorTemperature; @@ -51,14 +50,14 @@ public class SensorManager implements SharedPreferences.OnSharedPreferenceChange private AltitudeChangeHandler altitudeChangeHandler; - private GpsManager gpsManager; + private GpsHandler gpsHandler; public SensorManager(TrackPointCreator observer) { this.observer = observer; } public void start(Context context, Handler handler) { - if (gpsManager != null) { + if (isStarted()) { throw new RuntimeException("SensorManager cannot be started twice; stop first."); } @@ -66,7 +65,7 @@ public class SensorManager implements SharedPreferences.OnSharedPreferenceChange this.context = context; this.handler = handler; - gpsManager = new GpsManager(observer, this); + gpsHandler = new GpsHandler(this, observer); altitudeChangeHandler = new AltitudeChangeHandler(this); heartRateHandler = new HeartRateHandler(this); temperatureHandler = new TemperatureHandler(this); @@ -76,8 +75,6 @@ public class SensorManager implements SharedPreferences.OnSharedPreferenceChange runningSpeedCadenceDistanceHandler = new RunningSpeedCadenceDistanceHandler(this); onSharedPreferenceChanged(null, null); - - gpsManager.start(context, handler); } public void stop() { @@ -104,15 +101,15 @@ public class SensorManager implements SharedPreferences.OnSharedPreferenceChange altitudeChangeHandler.disconnect(); altitudeChangeHandler = null; - gpsManager.stop(); - gpsManager = null; + gpsHandler.disconnect(); + gpsHandler = null; context = null; handler = null; } public void reset() { - if (gpsManager == null || altitudeChangeHandler == null) { + if (gpsHandler == null || altitudeChangeHandler == null) { Log.d(TAG, "No recording running and no reset necessary."); return; } @@ -126,11 +123,11 @@ public class SensorManager implements SharedPreferences.OnSharedPreferenceChange cyclingPowerHandler.resetAggregated(); runningSpeedCadenceDistanceHandler.resetAggregated(); altitudeChangeHandler.resetAggregated(); - if (gpsManager.getAggregatorGPS() != null) gpsManager.getAggregatorGPS().resetAggregated(); + gpsHandler.resetAggregated(); } - public GpsManager getGpsManager() { - return gpsManager; + public GpsHandler getGpsHandler() { + return gpsHandler; } @Deprecated @@ -141,36 +138,38 @@ public class SensorManager implements SharedPreferences.OnSharedPreferenceChange @Override public void onSharedPreferenceChanged(SharedPreferences sharedPreferences, @Nullable String key) { - if (gpsManager != null) { - gpsManager.onSharedPreferenceChanged(sharedPreferences, key); + if (!isStarted()) return; - if (PreferencesUtils.isKey(altitudeChangeHandler.getSensorPreferenceKey(), key)) { - altitudeChangeHandler.connect(); - } + if (PreferencesUtils.isKey(gpsHandler.getSensorPreferenceKey(), key)) { + gpsHandler.connect(); + } - if (PreferencesUtils.isKey(heartRateHandler.getSensorPreferenceKey(), key)) { - heartRateHandler.connect(); - } + if (PreferencesUtils.isKey(altitudeChangeHandler.getSensorPreferenceKey(), key)) { + altitudeChangeHandler.connect(); + } - if (PreferencesUtils.isKey(temperatureHandler.getSensorPreferenceKey(), key)) { - temperatureHandler.connect(); - } + if (PreferencesUtils.isKey(heartRateHandler.getSensorPreferenceKey(), key)) { + heartRateHandler.connect(); + } - if (PreferencesUtils.isKey(cyclingCadenceHandler.getSensorPreferenceKey(), key)) { - cyclingCadenceHandler.connect(); - } + if (PreferencesUtils.isKey(temperatureHandler.getSensorPreferenceKey(), key)) { + temperatureHandler.connect(); + } - if (PreferencesUtils.isKey(cyclingDistanceSpeedHandler.getSensorPreferenceKey(), key)) { - cyclingDistanceSpeedHandler.connect(); - } + if (PreferencesUtils.isKey(cyclingCadenceHandler.getSensorPreferenceKey(), key)) { + cyclingCadenceHandler.connect(); + } - if (PreferencesUtils.isKey(cyclingCadenceHandler.getSensorPreferenceKey(), key)) { - cyclingPowerHandler.connect(); - } + if (PreferencesUtils.isKey(cyclingDistanceSpeedHandler.getSensorPreferenceKey(), key)) { + cyclingDistanceSpeedHandler.connect(); + } - if (PreferencesUtils.isKey(runningSpeedCadenceDistanceHandler.getSensorPreferenceKey(), key)) { - runningSpeedCadenceDistanceHandler.connect(); - } + if (PreferencesUtils.isKey(cyclingCadenceHandler.getSensorPreferenceKey(), key)) { + cyclingPowerHandler.connect(); + } + + if (PreferencesUtils.isKey(runningSpeedCadenceDistanceHandler.getSensorPreferenceKey(), key)) { + runningSpeedCadenceDistanceHandler.connect(); } } @@ -251,10 +250,13 @@ public class SensorManager implements SharedPreferences.OnSharedPreferenceChange } public SensorDataSet getSensorDataSet(Instant now) { - AggregatorGPS gps = gpsManager.getAggregatorGPS(); + SensorData gpsSensorData = gpsHandler.getSensorData(now); + + var a = temperatureHandler.getSensorData(now); + var b = altitudeChangeHandler.getSensorData(now); return new SensorDataSet( - gps != null && gps.hasReceivedData() ? gps.getAggregatedValueWithSensorName(now) : new SensorData<>(Position.of(now), ""), + gpsSensorData != null ? gpsSensorData : new SensorData<>(Position.of(now), ""), //We always need a Position(now) getSpeed(now), getDistance(now), getHeartRate(now), @@ -291,11 +293,15 @@ public class SensorManager implements SharedPreferences.OnSharedPreferenceChange if (runningSpeedCadenceDistanceHandler != null) { runningSpeedCadenceDistanceHandler.setAggregator(null); } - if (this.gpsManager != null) { - this.gpsManager.setAggregatorGPS(); + if (this.gpsHandler != null) { + this.gpsHandler.setAggregator(null); } } + private boolean isStarted() { + return wakeLock != null; + } + @VisibleForTesting public void setAggregator(AggregatorHeartRate data) { heartRateHandler.setAggregator(data); diff --git a/src/main/java/de/dennisguse/opentracks/sensors/driver/BluetoothDriver.java b/src/main/java/de/dennisguse/opentracks/sensors/driver/BluetoothDriver.java index e3b088383..ad30977ef 100644 --- a/src/main/java/de/dennisguse/opentracks/sensors/driver/BluetoothDriver.java +++ b/src/main/java/de/dennisguse/opentracks/sensors/driver/BluetoothDriver.java @@ -41,6 +41,7 @@ import de.dennisguse.opentracks.sensors.ServiceMeasurementUUID; /** * Manages connection to a Bluetooth LE sensor and subscribes for onChange-notifications. */ +//TODO Re-act to Bluetooth disabled/enabled @SuppressLint("MissingPermission") public class BluetoothDriver implements Driver { diff --git a/src/main/java/de/dennisguse/opentracks/sensors/driver/DriverObserver.java b/src/main/java/de/dennisguse/opentracks/sensors/driver/DriverObserver.java index 4fda6d99d..538c5f48b 100644 --- a/src/main/java/de/dennisguse/opentracks/sensors/driver/DriverObserver.java +++ b/src/main/java/de/dennisguse/opentracks/sensors/driver/DriverObserver.java @@ -22,4 +22,10 @@ public interface DriverObserver { */ //TODO check if really need this, because the driver was told to disconnect and then just signalizes that it finished this task. void onDisconnected(); + + /** + * The Driver detected that the sensor got deactivated. + * For example, the user deactivated the internal GPS. + */ + default void onSensorDeactivated() {} } diff --git a/src/main/java/de/dennisguse/opentracks/sensors/driver/GpsInternal.java b/src/main/java/de/dennisguse/opentracks/sensors/driver/GpsInternal.java index 77b668bf2..cb9316d56 100644 --- a/src/main/java/de/dennisguse/opentracks/sensors/driver/GpsInternal.java +++ b/src/main/java/de/dennisguse/opentracks/sensors/driver/GpsInternal.java @@ -7,6 +7,7 @@ import android.os.Handler; import android.util.Log; import androidx.annotation.NonNull; +import androidx.annotation.VisibleForTesting; import androidx.core.location.LocationListenerCompat; import androidx.core.location.LocationManagerCompat; import androidx.core.location.LocationRequestCompat; @@ -18,9 +19,10 @@ public class GpsInternal implements Driver { private static final String TAG = GpsInternal.class.getSimpleName(); - private static final String LOCATION_PROVIDER = LocationManager.GPS_PROVIDER; + public static final String LOCATION_PROVIDER = LocationManager.GPS_PROVIDER; - private final LocationListenerCompat locationListenerCompat = new LocationListenerCompat() { + @VisibleForTesting + public final LocationListenerCompat locationListenerCompat = new LocationListenerCompat() { @Override public void onLocationChanged(@NonNull Location location) { driverObserver.onDataReceived(location); @@ -33,13 +35,14 @@ public class GpsInternal implements Driver { @Override public void onProviderDisabled(@NonNull String provider) { - driverObserver.onConnectionLost(); + driverObserver.onSensorDeactivated(); } }; private final DriverObserver driverObserver; - private LocationManager locationManager; + @VisibleForTesting + public LocationManager locationManager; public GpsInternal(DriverObserver driverObserver) { this.driverObserver = driverObserver; @@ -62,8 +65,8 @@ public class GpsInternal implements Driver { if (PermissionRequester.GPS.hasPermission(context)) { try { Log.i(TAG, "Register for location updates " + context); - LocationManagerCompat.requestLocationUpdates(locationManager, LOCATION_PROVIDER, locationRequest, handler::post, locationListenerCompat); driverObserver.onConnected(null, null); + LocationManagerCompat.requestLocationUpdates(locationManager, LOCATION_PROVIDER, locationRequest, handler::post, locationListenerCompat); return; } catch (SecurityException e) { Log.e(TAG, "Could not register location listener; permissions not granted.", e); 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 4b5be814c..d61cd8a5a 100644 --- a/src/main/java/de/dennisguse/opentracks/sensors/sensorData/Aggregator.java +++ b/src/main/java/de/dennisguse/opentracks/sensors/sensorData/Aggregator.java @@ -16,10 +16,6 @@ public abstract class Aggregator { private final String sensorAddress; private final String sensorName; - Aggregator(String sensorAddress) { - this(sensorAddress, null); - } - Aggregator(String sensorAddress, String sensorName) { this.sensorAddress = sensorAddress; this.sensorName = sensorName; diff --git a/src/main/java/de/dennisguse/opentracks/sensors/sensorData/AggregatorGPS.java b/src/main/java/de/dennisguse/opentracks/sensors/sensorData/AggregatorGPS.java index 62838e11d..af6731b86 100644 --- a/src/main/java/de/dennisguse/opentracks/sensors/sensorData/AggregatorGPS.java +++ b/src/main/java/de/dennisguse/opentracks/sensors/sensorData/AggregatorGPS.java @@ -1,21 +1,23 @@ package de.dennisguse.opentracks.sensors.sensorData; +import android.location.Location; + import androidx.annotation.NonNull; import java.time.Instant; import de.dennisguse.opentracks.data.models.Position; -public class AggregatorGPS extends Aggregator { +public class AggregatorGPS extends Aggregator { - public AggregatorGPS(String sensorAddress) { - super(sensorAddress); + public AggregatorGPS(String sensorAddress, String sensorName) { + super(sensorAddress, sensorName); } @Override - protected void computeValue(Raw current) { - aggregatedValue = current.value(); + protected void computeValue(Raw current) { + aggregatedValue = Position.of(current.value(), current.time()); } @Override diff --git a/src/main/java/de/dennisguse/opentracks/settings/PreferencesUtils.java b/src/main/java/de/dennisguse/opentracks/settings/PreferencesUtils.java index ede4d04c8..8412b2266 100644 --- a/src/main/java/de/dennisguse/opentracks/settings/PreferencesUtils.java +++ b/src/main/java/de/dennisguse/opentracks/settings/PreferencesUtils.java @@ -253,10 +253,6 @@ public class PreferencesUtils { return isReportSpeed(track.activityTypeLocalized()); } - public static String getBluetoothSensorAddressNone() { - return SensorType.NONE.getPreferenceValue(); - } - public static SensorType getSensorType(String address) { return Arrays.stream(SensorType.values()) .filter(it -> it.getPreferenceValue().equals(address)) diff --git a/src/main/java/de/dennisguse/opentracks/settings/bluetooth/BluetoothLeGpsPreference.java b/src/main/java/de/dennisguse/opentracks/settings/bluetooth/BluetoothLeGpsPreference.java new file mode 100644 index 000000000..56dc14f8d --- /dev/null +++ b/src/main/java/de/dennisguse/opentracks/settings/bluetooth/BluetoothLeGpsPreference.java @@ -0,0 +1,41 @@ +package de.dennisguse.opentracks.settings.bluetooth; + +import android.content.Context; +import android.location.LocationManager; +import android.util.AttributeSet; + +import androidx.core.location.LocationManagerCompat; +import androidx.preference.PreferenceDialogFragmentCompat; + +import java.util.List; + +import de.dennisguse.opentracks.sensors.driver.GpsInternal; + +public class BluetoothLeGpsPreference extends BluetoothLeSensorPreference { + + public BluetoothLeGpsPreference(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) { + super(context, attrs, defStyleAttr, defStyleRes); + } + + public BluetoothLeGpsPreference(Context context, AttributeSet attrs, int defStyleAttr) { + super(context, attrs, defStyleAttr); + } + + public BluetoothLeGpsPreference(Context context, AttributeSet attrs) { + super(context, attrs); + } + + public BluetoothLeGpsPreference(Context context) { + super(context); + } + + @Override + public PreferenceDialogFragmentCompat createInstance() { + boolean hasInternalSensor = LocationManagerCompat.hasProvider((LocationManager) getContext().getSystemService(Context.LOCATION_SERVICE), GpsInternal.LOCATION_PROVIDER); + + return BluetoothLeSensorPreferenceDialog + .newInstance(getKey(), List.of(), hasInternalSensor); + } + + +} \ No newline at end of file diff --git a/src/main/res/values/settings.xml b/src/main/res/values/settings.xml index 91f08907a..769c78495 100644 --- a/src/main/res/values/settings.xml +++ b/src/main/res/values/settings.xml @@ -26,6 +26,7 @@ bluetoothCyclingPowerSensor bluetoothRunningSpeedAndCadenceSensor bluetoothPressureSensor + bluetoothGPSSensor NONE INTERNAL diff --git a/src/main/res/xml/settings_sensors.xml b/src/main/res/xml/settings_sensors.xml index 582e1bcfe..ac18e1879 100644 --- a/src/main/res/xml/settings_sensors.xml +++ b/src/main/res/xml/settings_sensors.xml @@ -5,12 +5,15 @@ android:title="@string/settings_sensors_title"> + - + android:title="@string/settings_sensor_bluetooth_service_filter_title" /> \ No newline at end of file