Extract Driver for internal GPS to GpsInternal.

This commit is contained in:
Dennis Guse
2026-01-28 20:21:55 +01:00
parent 719a052f34
commit 167d90332e
5 changed files with 127 additions and 80 deletions
@@ -756,6 +756,6 @@ public class ExportImportTest {
mockAltitudeChange(trackPointCreator, altitudeGain);
trackPointCreator.setClock(time);
trackPointCreator.getSensorManager().getGpsManager().onLocationChanged(location);
trackPointCreator.getSensorManager().getGpsManager().onData(location);
}
}
@@ -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);
}
}
@@ -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<Location>, 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);
}
}
@@ -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<Location> driverObserver;
private LocationManager locationManager;
public GpsInternal(DriverObserver<Location> 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;
}
}
@@ -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()));