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); mockAltitudeChange(trackPointCreator, altitudeGain);
trackPointCreator.setClock(time); 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.Context;
import android.content.Intent; import android.content.Intent;
import android.location.Location; import android.location.Location;
import android.location.LocationManager;
import android.os.Looper; import android.os.Looper;
import androidx.annotation.NonNull; import androidx.annotation.NonNull;
import androidx.core.location.LocationManagerCompat;
import androidx.test.core.app.ApplicationProvider; import androidx.test.core.app.ApplicationProvider;
import androidx.test.ext.junit.runners.AndroidJUnit4; import androidx.test.ext.junit.runners.AndroidJUnit4;
import androidx.test.filters.MediumTest; import androidx.test.filters.MediumTest;
@@ -153,7 +151,7 @@ public class TrackRecordingServiceRecordingTest {
Track.Id trackId = service.startNewTrack(); Track.Id trackId = service.startNewTrack();
//We do not want the real GPS to interfere. //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"; String gps1 = "2020-02-02T02:02:03Z";
sendGPSLocation(trackPointCreator, gps1, 45.0, 35.0, 1, 15); sendGPSLocation(trackPointCreator, gps1, 45.0, 35.0, 1, 15);
@@ -987,6 +985,6 @@ public class TrackRecordingServiceRecordingTest {
location.setSpeed(speed); location.setSpeed(speed);
trackPointCreator.setClock(time); 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.Context;
import android.content.SharedPreferences; import android.content.SharedPreferences;
import android.location.Location; import android.location.Location;
import android.location.LocationManager;
import android.os.Bundle;
import android.os.Handler; import android.os.Handler;
import android.util.Log; import android.util.Log;
import androidx.annotation.NonNull; import androidx.annotation.NonNull;
import androidx.annotation.VisibleForTesting; 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.R;
import de.dennisguse.opentracks.data.models.Distance; import de.dennisguse.opentracks.data.models.Distance;
import de.dennisguse.opentracks.data.models.Position; 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.sensors.sensorData.AggregatorGPS;
import de.dennisguse.opentracks.services.handlers.TrackPointCreator; import de.dennisguse.opentracks.services.handlers.TrackPointCreator;
import de.dennisguse.opentracks.settings.PreferencesUtils; import de.dennisguse.opentracks.settings.PreferencesUtils;
import de.dennisguse.opentracks.util.PermissionRequester;
@VisibleForTesting(otherwise = VisibleForTesting.PACKAGE_PRIVATE) @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 final String TAG = GpsManager.class.getSimpleName();
private static final String LOCATION_PROVIDER = LocationManager.GPS_PROVIDER;
//TODO Refactor to just pass information via SensorManager. //TODO Refactor to just pass information via SensorManager.
private TrackPointCreator trackPointCreator; private TrackPointCreator trackPointCreator;
@@ -38,9 +30,9 @@ public class GpsManager implements LocationListenerCompat, GpsStatusManager.GpsS
private Context context; private Context context;
private Handler handler; private Handler handler;
private LocationManager locationManager; @VisibleForTesting
public GpsInternal driver;
private GpsStatusManager gpsStatusManager; private GpsStatusManager gpsStatusManager;
private Duration gpsInterval;
private Distance thresholdHorizontalAccuracy; private Distance thresholdHorizontalAccuracy;
private AggregatorGPS aggregatorGPS; private AggregatorGPS aggregatorGPS;
@@ -57,24 +49,20 @@ public class GpsManager implements LocationListenerCompat, GpsStatusManager.GpsS
onSharedPreferenceChanged(null, null); onSharedPreferenceChanged(null, null);
gpsStatusManager = new GpsStatusManager(context, this, handler); gpsStatusManager = new GpsStatusManager(context, this, handler);
locationManager = (LocationManager) context.getSystemService(Context.LOCATION_SERVICE);
this.aggregatorGPS = new AggregatorGPS("internal"); this.aggregatorGPS = new AggregatorGPS("internal");
registerLocationListener(); this.driver = new GpsInternal(this);
this.driver.connect(context, handler, "internal");
gpsStatusManager.start(); gpsStatusManager.start();
} }
private boolean isStarted() {
return locationManager != null;
}
@SuppressWarnings({"MissingPermission"}) @SuppressWarnings({"MissingPermission"})
public void stop() { public void stop() {
if (isStarted()) { if (driver.isConnected()) {
LocationManagerCompat.removeUpdates(locationManager, this); driver.disconnect();
} }
locationManager = null; driver = null;
this.context = null; this.context = null;
handler = null; handler = null;
@@ -106,36 +94,48 @@ public class GpsManager implements LocationListenerCompat, GpsStatusManager.GpsS
if (PreferencesUtils.isKey(R.string.min_sampling_interval_key, key)) { if (PreferencesUtils.isKey(R.string.min_sampling_interval_key, key)) {
registerListener = true; registerListener = true;
gpsInterval = PreferencesUtils.getMinSamplingInterval();
if (gpsStatusManager != null) { if (gpsStatusManager != null) {
gpsStatusManager.onMinSamplingIntervalChanged(gpsInterval); gpsStatusManager.onMinSamplingIntervalChanged(PreferencesUtils.getMinSamplingInterval());
} }
} }
if (PreferencesUtils.isKey(R.string.recording_gps_accuracy_key, key)) { if (PreferencesUtils.isKey(R.string.recording_gps_accuracy_key, key)) {
thresholdHorizontalAccuracy = PreferencesUtils.getThresholdHorizontalAccuracy(); thresholdHorizontalAccuracy = PreferencesUtils.getThresholdHorizontalAccuracy();
} }
if (PreferencesUtils.isKey(R.string.recording_distance_interval_key, key)) { if (PreferencesUtils.isKey(R.string.recording_distance_interval_key, key)) {
registerListener = true;
if (gpsStatusManager != null) { if (gpsStatusManager != null) {
Distance gpsMinDistance = PreferencesUtils.getRecordingDistanceInterval(); Distance gpsMinDistance = PreferencesUtils.getRecordingDistanceInterval();
gpsStatusManager.onRecordingDistanceChanged(gpsMinDistance); gpsStatusManager.onRecordingDistanceChanged(gpsMinDistance);
} }
} }
if (registerListener && isStarted()) { if (registerListener && driver != null && driver.isConnected()) {
registerLocationListener(); 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 @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. // 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, observer.getNow());
gpsStatusManager.onNewTrackPoint(position); gpsStatusManager.onNewTrackPoint(position);
@@ -153,44 +153,4 @@ public class GpsManager implements LocationListenerCompat, GpsStatusManager.GpsS
aggregatorGPS.add(observer.getNow(), position); aggregatorGPS.add(observer.getNow(), position);
observer.onChange(); 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; return entries;
} }
//TODO Rename to getGpsMinSamplingInterval
public static Duration getMinSamplingInterval() { public static Duration getMinSamplingInterval() {
final Duration MIN_SAMPLING_INTERVAL = getMinSamplingIntervalDefault(); final Duration MIN_SAMPLING_INTERVAL = getMinSamplingIntervalDefault();
return Duration.ofSeconds(getInt(R.string.min_sampling_interval_key, (int) MIN_SAMPLING_INTERVAL.getSeconds())); return Duration.ofSeconds(getInt(R.string.min_sampling_interval_key, (int) MIN_SAMPLING_INTERVAL.getSeconds()));