forked from upstream-mirrors/OpenTracks
14b6746dff
Fixes #1328.
195 lines
6.5 KiB
Java
195 lines
6.5 KiB
Java
package de.dennisguse.opentracks.services.handlers;
|
|
|
|
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 java.time.Instant;
|
|
|
|
import de.dennisguse.opentracks.R;
|
|
import de.dennisguse.opentracks.data.models.Distance;
|
|
import de.dennisguse.opentracks.data.models.TrackPoint;
|
|
import de.dennisguse.opentracks.settings.PreferencesUtils;
|
|
import de.dennisguse.opentracks.util.LocationUtils;
|
|
import de.dennisguse.opentracks.util.PermissionRequester;
|
|
|
|
@VisibleForTesting(otherwise = VisibleForTesting.PACKAGE_PRIVATE)
|
|
public class GPSHandler implements LocationListenerCompat, GpsStatus.GpsStatusListener, SharedPreferences.OnSharedPreferenceChangeListener {
|
|
|
|
private final String TAG = GPSHandler.class.getSimpleName();
|
|
|
|
public static final String LOCATION_PROVIDER = LocationManager.GPS_PROVIDER;
|
|
|
|
private final TrackPointCreator trackPointCreator;
|
|
private Context context;
|
|
private Handler handler;
|
|
|
|
private LocationManager locationManager;
|
|
private GpsStatus gpsStatus;
|
|
private Duration gpsInterval;
|
|
private Distance thresholdHorizontalAccuracy;
|
|
|
|
public GPSHandler(TrackPointCreator trackPointCreator) {
|
|
this.trackPointCreator = trackPointCreator;
|
|
}
|
|
|
|
public void onStart(@NonNull Context context, @NonNull Handler handler) {
|
|
this.context = context;
|
|
this.handler = handler;
|
|
|
|
PreferencesUtils.registerOnSharedPreferenceChangeListener(this);
|
|
gpsStatus = new GpsStatus(context, this);
|
|
locationManager = (LocationManager) context.getSystemService(Context.LOCATION_SERVICE);
|
|
registerLocationListener();
|
|
gpsStatus.start();
|
|
}
|
|
|
|
private boolean isStarted() {
|
|
return locationManager != null;
|
|
}
|
|
|
|
@SuppressWarnings({"MissingPermission"})
|
|
//TODO upgrade to AGP7.0.0/API31 started complaining about removeUpdates.
|
|
public void onStop() {
|
|
if (locationManager != null && context != null) {
|
|
if (PermissionRequester.GPS.hasPermission(context)) {
|
|
LocationManagerCompat.removeUpdates(locationManager, this);
|
|
}
|
|
locationManager = null;
|
|
context = null;
|
|
handler = null;
|
|
}
|
|
|
|
if (gpsStatus != null) {
|
|
gpsStatus.stop();
|
|
gpsStatus = null;
|
|
}
|
|
PreferencesUtils.unregisterOnSharedPreferenceChangeListener(this);
|
|
}
|
|
|
|
@Override
|
|
public void onSharedPreferenceChanged(SharedPreferences sharedPreferences, String key) {
|
|
boolean registerListener = false;
|
|
|
|
if (PreferencesUtils.isKey(R.string.min_recording_interval_key, key)) {
|
|
registerListener = true;
|
|
|
|
gpsInterval = PreferencesUtils.getMinRecordingInterval();
|
|
|
|
if (gpsStatus != null) {
|
|
gpsStatus.onMinRecordingIntervalChanged(gpsInterval);
|
|
}
|
|
}
|
|
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 (gpsStatus != null) {
|
|
Distance gpsMinDistance = PreferencesUtils.getRecordingDistanceInterval();
|
|
gpsStatus.onRecordingDistanceChanged(gpsMinDistance);
|
|
}
|
|
}
|
|
|
|
if (registerListener) {
|
|
registerLocationListener();
|
|
}
|
|
}
|
|
|
|
/**
|
|
* 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) {
|
|
if (!isStarted()) {
|
|
Log.w(TAG, "Location is ignored; not started.");
|
|
return;
|
|
}
|
|
|
|
if (gpsStatus != null) {
|
|
// Send each update to the status; please note that this TrackPoint is not stored.
|
|
TrackPoint trackPoint = new TrackPoint(location, Instant.ofEpochMilli(location.getTime()));
|
|
gpsStatus.onLocationChanged(trackPoint);
|
|
}
|
|
|
|
if (!LocationUtils.isValidLocation(location)) {
|
|
Log.w(TAG, "Ignore newTrackPoint. location is invalid.");
|
|
return;
|
|
}
|
|
|
|
if (!LocationUtils.fulfillsAccuracy(location, thresholdHorizontalAccuracy)) {
|
|
Log.d(TAG, "Ignore newTrackPoint. Poor accuracy.");
|
|
return;
|
|
}
|
|
|
|
trackPointCreator.onChange(location);
|
|
}
|
|
|
|
@Override
|
|
public void onStatusChanged(String provider, int status, Bundle extras) {
|
|
}
|
|
|
|
@Override
|
|
public void onProviderEnabled(@NonNull String provider) {
|
|
if (gpsStatus != null) {
|
|
gpsStatus.onGpsEnabled();
|
|
}
|
|
}
|
|
|
|
@Override
|
|
public void onProviderDisabled(@NonNull String provider) {
|
|
if (gpsStatus != null) {
|
|
gpsStatus.onGpsDisabled();
|
|
}
|
|
}
|
|
|
|
private void registerLocationListener() {
|
|
if (locationManager == null || context == null) {
|
|
Log.e(TAG, "Not started.");
|
|
return;
|
|
}
|
|
|
|
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 {
|
|
final Handler HANDLER = handler;
|
|
LocationManagerCompat.requestLocationUpdates(locationManager, LOCATION_PROVIDER, locationRequest, HANDLER::post, this);
|
|
} catch (SecurityException e) {
|
|
Log.e(TAG, "Could not register location listener; permissions not granted.", e);
|
|
}
|
|
}
|
|
}
|
|
|
|
Distance getThresholdHorizontalAccuracy() {
|
|
return thresholdHorizontalAccuracy;
|
|
}
|
|
|
|
@Override
|
|
public void onGpsStatusChanged(GpsStatusValue prevStatus, GpsStatusValue currentStatus) {
|
|
trackPointCreator.sendGpsStatus(currentStatus);
|
|
}
|
|
}
|