From 167d90332ebf66edab38fef66b7ab4b3aa00c0b1 Mon Sep 17 00:00:00 2001 From: Dennis Guse Date: Wed, 28 Jan 2026 20:21:55 +0100 Subject: [PATCH] Extract Driver for internal GPS to GpsInternal. --- .../io/file/importer/ExportImportTest.java | 2 +- .../TrackRecordingServiceRecordingTest.java | 6 +- .../opentracks/sensors/GpsManager.java | 110 ++++++------------ .../sensors/driver/GpsInternal.java | 88 ++++++++++++++ .../opentracks/settings/PreferencesUtils.java | 1 + 5 files changed, 127 insertions(+), 80 deletions(-) create mode 100644 src/main/java/de/dennisguse/opentracks/sensors/driver/GpsInternal.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 518521de2..f0c455c67 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 @@ -756,6 +756,6 @@ public class ExportImportTest { mockAltitudeChange(trackPointCreator, altitudeGain); trackPointCreator.setClock(time); - trackPointCreator.getSensorManager().getGpsManager().onLocationChanged(location); + trackPointCreator.getSensorManager().getGpsManager().onData(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 3676d539b..d8a8e7686 100644 --- a/src/androidTest/java/de/dennisguse/opentracks/services/TrackRecordingServiceRecordingTest.java +++ b/src/androidTest/java/de/dennisguse/opentracks/services/TrackRecordingServiceRecordingTest.java @@ -6,11 +6,9 @@ import static org.junit.Assert.assertFalse; import android.content.Context; import android.content.Intent; import android.location.Location; -import android.location.LocationManager; 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; @@ -153,7 +151,7 @@ public class TrackRecordingServiceRecordingTest { Track.Id trackId = service.startNewTrack(); //We do not want the real GPS to interfere. - LocationManagerCompat.removeUpdates((LocationManager) context.getSystemService(Context.LOCATION_SERVICE), trackPointCreator.getSensorManager().getGpsManager()); + trackPointCreator.getSensorManager().getGpsManager().driver.disconnect(); String gps1 = "2020-02-02T02:02:03Z"; sendGPSLocation(trackPointCreator, gps1, 45.0, 35.0, 1, 15); @@ -987,6 +985,6 @@ public class TrackRecordingServiceRecordingTest { location.setSpeed(speed); trackPointCreator.setClock(time); - trackPointCreator.getSensorManager().getGpsManager().onLocationChanged(location); + trackPointCreator.getSensorManager().getGpsManager().onData(location); } } diff --git a/src/main/java/de/dennisguse/opentracks/sensors/GpsManager.java b/src/main/java/de/dennisguse/opentracks/sensors/GpsManager.java index 463945460..8106bae7c 100644 --- a/src/main/java/de/dennisguse/opentracks/sensors/GpsManager.java +++ b/src/main/java/de/dennisguse/opentracks/sensors/GpsManager.java @@ -3,34 +3,26 @@ package de.dennisguse.opentracks.sensors; import android.content.Context; import android.content.SharedPreferences; import android.location.Location; -import android.location.LocationManager; -import android.os.Bundle; 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; - -import java.time.Duration; 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.GpsInternal; import de.dennisguse.opentracks.sensors.sensorData.AggregatorGPS; import de.dennisguse.opentracks.services.handlers.TrackPointCreator; import de.dennisguse.opentracks.settings.PreferencesUtils; -import de.dennisguse.opentracks.util.PermissionRequester; @VisibleForTesting(otherwise = VisibleForTesting.PACKAGE_PRIVATE) -public class GpsManager implements LocationListenerCompat, GpsStatusManager.GpsStatusListener, SharedPreferences.OnSharedPreferenceChangeListener { +public class GpsManager implements DriverObserver, GpsStatusManager.GpsStatusListener, SharedPreferences.OnSharedPreferenceChangeListener { private final String TAG = GpsManager.class.getSimpleName(); - private static final String LOCATION_PROVIDER = LocationManager.GPS_PROVIDER; - //TODO Refactor to just pass information via SensorManager. private TrackPointCreator trackPointCreator; @@ -38,9 +30,9 @@ public class GpsManager implements LocationListenerCompat, GpsStatusManager.GpsS private Context context; private Handler handler; - private LocationManager locationManager; + @VisibleForTesting + public GpsInternal driver; private GpsStatusManager gpsStatusManager; - private Duration gpsInterval; private Distance thresholdHorizontalAccuracy; private AggregatorGPS aggregatorGPS; @@ -57,24 +49,20 @@ public class GpsManager implements LocationListenerCompat, GpsStatusManager.GpsS onSharedPreferenceChanged(null, null); gpsStatusManager = new GpsStatusManager(context, this, handler); - locationManager = (LocationManager) context.getSystemService(Context.LOCATION_SERVICE); this.aggregatorGPS = new AggregatorGPS("internal"); - registerLocationListener(); + this.driver = new GpsInternal(this); + this.driver.connect(context, handler, "internal"); gpsStatusManager.start(); } - private boolean isStarted() { - return locationManager != null; - } - @SuppressWarnings({"MissingPermission"}) public void stop() { - if (isStarted()) { - LocationManagerCompat.removeUpdates(locationManager, this); + if (driver.isConnected()) { + driver.disconnect(); } - locationManager = null; + driver = null; this.context = null; handler = null; @@ -106,36 +94,48 @@ public class GpsManager implements LocationListenerCompat, GpsStatusManager.GpsS if (PreferencesUtils.isKey(R.string.min_sampling_interval_key, key)) { registerListener = true; - gpsInterval = PreferencesUtils.getMinSamplingInterval(); - if (gpsStatusManager != null) { - gpsStatusManager.onMinSamplingIntervalChanged(gpsInterval); + gpsStatusManager.onMinSamplingIntervalChanged(PreferencesUtils.getMinSamplingInterval()); } } if (PreferencesUtils.isKey(R.string.recording_gps_accuracy_key, key)) { thresholdHorizontalAccuracy = PreferencesUtils.getThresholdHorizontalAccuracy(); } if (PreferencesUtils.isKey(R.string.recording_distance_interval_key, key)) { - registerListener = true; - if (gpsStatusManager != null) { Distance gpsMinDistance = PreferencesUtils.getRecordingDistanceInterval(); gpsStatusManager.onRecordingDistanceChanged(gpsMinDistance); } } - if (registerListener && isStarted()) { - registerLocationListener(); + if (registerListener && driver != null && driver.isConnected()) { + driver.disconnect(); + driver.connect(context, handler, "internal"); } } - /** - * Checks if location is valid and builds a track point that will be send through TrackPointCreator. - * - * @param location {@link Location} object. - */ @Override - public void onLocationChanged(@NonNull Location location) { + public void onGpsStatusChanged(GpsStatusValue currentStatus) { + trackPointCreator.sendGpsStatus(currentStatus); + } + + @Override + public void onConnect() { + gpsStatusManager.onGpsEnabled(); + } + + @Override + public void onDisconnect() { + gpsStatusManager.onGpsDisabled(); + } + + @Override + public void onRemove() { + gpsStatusManager.onGpsDisabled(); + } + + @Override + public void onData(Location location) { // Send each update to the status; please note that this TrackPoint is not stored. Position position = Position.of(location, observer.getNow()); gpsStatusManager.onNewTrackPoint(position); @@ -153,44 +153,4 @@ public class GpsManager implements LocationListenerCompat, GpsStatusManager.GpsS aggregatorGPS.add(observer.getNow(), position); observer.onChange(); } - - @Override - public void onStatusChanged(@NonNull String provider, int status, Bundle extras) { - } - - @Override - public void onProviderEnabled(@NonNull String provider) { - gpsStatusManager.onGpsEnabled(); - } - - @Override - public void onProviderDisabled(@NonNull String provider) { - gpsStatusManager.onGpsDisabled(); - } - - private void registerLocationListener() { - if (!LocationManagerCompat.hasProvider(locationManager, LOCATION_PROVIDER)) { - Log.e(TAG, "Device doesn't have GPS."); - return; - } - - LocationRequestCompat locationRequest = new LocationRequestCompat.Builder(gpsInterval.toMillis()) - .setQuality(LocationRequestCompat.QUALITY_HIGH_ACCURACY) - .setMaxUpdateDelayMillis(0) - .build(); - - if (PermissionRequester.GPS.hasPermission(context)) { - try { - Log.i(TAG, "Register for location updates " + context); - LocationManagerCompat.requestLocationUpdates(locationManager, LOCATION_PROVIDER, locationRequest, handler::post, this); - } catch (SecurityException e) { - Log.e(TAG, "Could not register location listener; permissions not granted.", e); - } - } - } - - @Override - public void onGpsStatusChanged(GpsStatusValue currentStatus) { - trackPointCreator.sendGpsStatus(currentStatus); - } } diff --git a/src/main/java/de/dennisguse/opentracks/sensors/driver/GpsInternal.java b/src/main/java/de/dennisguse/opentracks/sensors/driver/GpsInternal.java new file mode 100644 index 000000000..216c9a562 --- /dev/null +++ b/src/main/java/de/dennisguse/opentracks/sensors/driver/GpsInternal.java @@ -0,0 +1,88 @@ +package de.dennisguse.opentracks.sensors.driver; + +import android.content.Context; +import android.location.Location; +import android.location.LocationManager; +import android.os.Handler; +import android.util.Log; + +import androidx.annotation.NonNull; +import androidx.core.location.LocationListenerCompat; +import androidx.core.location.LocationManagerCompat; +import androidx.core.location.LocationRequestCompat; + +import de.dennisguse.opentracks.settings.PreferencesUtils; +import de.dennisguse.opentracks.util.PermissionRequester; + +public class GpsInternal implements Driver { + + private static final String TAG = GpsInternal.class.getSimpleName(); + + private static final String LOCATION_PROVIDER = LocationManager.GPS_PROVIDER; + + private final LocationListenerCompat locationListenerCompat = new LocationListenerCompat() { + @Override + public void onLocationChanged(@NonNull Location location) { + driverObserver.onData(location); + } + + @Override + public void onProviderEnabled(@NonNull String provider) { + driverObserver.onConnect(); + } + + @Override + public void onProviderDisabled(@NonNull String provider) { + driverObserver.onDisconnect(); + } + }; + + private final DriverObserver driverObserver; + + private LocationManager locationManager; + + public GpsInternal(DriverObserver driverObserver) { + this.driverObserver = driverObserver; + } + + @Override + public void connect(Context context, Handler handler, String address) { + locationManager = (LocationManager) context.getSystemService(Context.LOCATION_SERVICE); + + if (!LocationManagerCompat.hasProvider(locationManager, LOCATION_PROVIDER)) { + Log.e(TAG, "Device doesn't have GPS."); + return; + } + + LocationRequestCompat locationRequest = new LocationRequestCompat.Builder(PreferencesUtils.getMinSamplingInterval().toMillis()) + .setQuality(LocationRequestCompat.QUALITY_HIGH_ACCURACY) + .setMaxUpdateDelayMillis(0) + .build(); + + if (PermissionRequester.GPS.hasPermission(context)) { + try { + Log.i(TAG, "Register for location updates " + context); + LocationManagerCompat.requestLocationUpdates(locationManager, LOCATION_PROVIDER, locationRequest, handler::post, locationListenerCompat); + } catch (SecurityException e) { + Log.e(TAG, "Could not register location listener; permissions not granted.", e); + } + } + } + + @SuppressWarnings({"MissingPermission"}) + @Override + public void disconnect() { + if (!isConnected()) return; + + LocationManagerCompat.removeUpdates(locationManager, locationListenerCompat); + + locationManager = null; + driverObserver.onDisconnect(); + } + + + @Override + public boolean isConnected() { + return locationManager != null; + } +} diff --git a/src/main/java/de/dennisguse/opentracks/settings/PreferencesUtils.java b/src/main/java/de/dennisguse/opentracks/settings/PreferencesUtils.java index 2ed5d6ab9..3854035f7 100644 --- a/src/main/java/de/dennisguse/opentracks/settings/PreferencesUtils.java +++ b/src/main/java/de/dennisguse/opentracks/settings/PreferencesUtils.java @@ -579,6 +579,7 @@ public class PreferencesUtils { return entries; } + //TODO Rename to getGpsMinSamplingInterval public static Duration getMinSamplingInterval() { final Duration MIN_SAMPLING_INTERVAL = getMinSamplingIntervalDefault(); return Duration.ofSeconds(getInt(R.string.min_sampling_interval_key, (int) MIN_SAMPLING_INTERVAL.getSeconds()));