Service refactoring: move location management to the LocationHandle.

Tests refactoring: location and service tests re-worked.
This commit is contained in:
Román Martínez
2020-07-17 12:29:30 +02:00
committed by GitHub
parent afea79476f
commit 17de8cea6f
13 changed files with 412 additions and 252 deletions
@@ -0,0 +1,51 @@
/*
* Copyright 2009 Google Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License"); you may not
* use this file except in compliance with the License. You may obtain a copy of
* the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations under
* the License.
*/
package de.dennisguse.opentracks.services.handlers;
/**
* This is a simple location listener policy that will always dictate the same polling interval.
*
* @author Sandor Dornbush
*/
public class AbsoluteLocationListenerPolicy implements LocationListenerPolicy {
private final long interval;
/**
* Constructor.
*
* @param interval the interval to request for gps signal
*/
public AbsoluteLocationListenerPolicy(long interval) {
this.interval = interval;
}
@Override
public long getDesiredPollingInterval() {
return interval;
}
@Override
public int getMinDistance_m() {
return 0;
}
@Override
public void updateIdleTime(long idleTime) {
// Ignore
}
}
@@ -0,0 +1,68 @@
/*
* Copyright 2009 Google Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License"); you may not
* use this file except in compliance with the License. You may obtain a copy of
* the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations under
* the License.
*/
package de.dennisguse.opentracks.services.handlers;
/**
* A {@link LocationListenerPolicy} that will change based on how long the user has been stationary.
* This policy will dictate a policy based on a min, max and idle time.
* The policy will dictate an interval bounded by min and max, and is half of the idle time.
*
* @author Sandor Dornbush
*/
public class AdaptiveLocationListenerPolicy implements LocationListenerPolicy {
private final long minInterval_ms;
private final long maxInterval_ms;
private final int minDistance_m;
// The time the user has been idle at the current location, in milliseconds.
private long idleTime;
/**
* Creates a policy that will be bounded by the given minInterval_ms and maxInterval_ms.
*
* @param minInterval_ms the smallest interval this policy will dictate
* @param maxInterval_ms the largest interval this policy will dictate
* @param minDistance_m the minimum distance
*/
public AdaptiveLocationListenerPolicy(long minInterval_ms, long maxInterval_ms, int minDistance_m) {
this.minInterval_ms = minInterval_ms;
this.maxInterval_ms = maxInterval_ms;
this.minDistance_m = minDistance_m;
}
/*
* Returns an interval half of the idle time, but bounded by minInterval_ms and maxInterval_ms.
*/
@Override
public long getDesiredPollingInterval() {
long desiredInterval_ms = idleTime / 2;
// Round to second to avoid setting the interval too often
desiredInterval_ms = (desiredInterval_ms / 1000) * 1000;
return Math.max(Math.min(maxInterval_ms, desiredInterval_ms), minInterval_ms);
}
@Override
public int getMinDistance_m() {
return minDistance_m;
}
@Override
public void updateIdleTime(long newIdleTime) {
idleTime = newIdleTime;
}
}
@@ -0,0 +1,47 @@
package de.dennisguse.opentracks.services.handlers;
import android.content.Context;
import android.content.SharedPreferences;
import de.dennisguse.opentracks.content.data.TrackPoint;
public class HandlerServer {
private String TAG = HandlerServer.class.getSimpleName();
private LocationHandler locationHandler;
private HandlerServerInterface service;
public HandlerServer(HandlerServerInterface service) {
this.locationHandler = new LocationHandler(this);
this.service = service;
}
public void start(Context context) {
locationHandler.onStart(context);
locationHandler.onSharedPreferenceChanged(context, null, null);
}
public void stop(Context context) {
locationHandler.onStop(context);
}
public void onSharedPreferenceChanged(Context context, SharedPreferences preferences, String key) {
locationHandler.onSharedPreferenceChanged(context, preferences, key);
}
public void sendTrackPoint(TrackPoint trackPoint, int recordingGpsAccuracy) {
service.newTrackPoint(trackPoint, recordingGpsAccuracy);
}
public interface HandlerServerInterface {
void newTrackPoint(TrackPoint trackPoint, int gpsAccuracy);
}
public interface Handler {
void onStart(Context context);
void onStop(Context context);
void onSharedPreferenceChanged(Context context, SharedPreferences preferences, String key);
}
}
@@ -0,0 +1,143 @@
package de.dennisguse.opentracks.services.handlers;
import android.content.Context;
import android.content.SharedPreferences;
import android.location.Location;
import android.location.LocationListener;
import android.location.LocationManager;
import android.os.Bundle;
import android.util.Log;
import androidx.annotation.NonNull;
import de.dennisguse.opentracks.R;
import de.dennisguse.opentracks.content.data.TrackPoint;
import de.dennisguse.opentracks.util.LocationUtils;
import de.dennisguse.opentracks.util.PreferencesUtils;
import de.dennisguse.opentracks.util.TrackPointUtils;
import de.dennisguse.opentracks.util.UnitConversions;
class LocationHandler implements HandlerServer.Handler, LocationListener {
private String TAG = LocationHandler.class.getSimpleName();
private LocationManager locationManager;
private HandlerServer handlerServer;
private LocationListenerPolicy locationListenerPolicy;
private long currentRecordingInterval;
private int recordingGpsAccuracy;
private TrackPoint lastValidTrackPoint;
public LocationHandler(HandlerServer handlerServer) {
this.handlerServer = handlerServer;
}
@Override
public void onStart(Context context) {
locationManager = (LocationManager) context.getSystemService(Context.LOCATION_SERVICE);
registerLocationListener();
}
@Override
public void onStop(Context context) {
locationManager = null;
unregisterLocationListener();
}
@Override
public void onSharedPreferenceChanged(Context context, SharedPreferences preferences, String key) {
if (PreferencesUtils.isKey(context, R.string.min_recording_interval_key, key)) {
int minRecordingInterval = PreferencesUtils.getMinRecordingInterval(context);
if (minRecordingInterval == PreferencesUtils.getMinRecordingIntervalAdaptBatteryLife(context)) {
// Choose battery life over moving time accuracy.
locationListenerPolicy = new AdaptiveLocationListenerPolicy(30 * UnitConversions.ONE_SECOND_MS, 5 * UnitConversions.ONE_MINUTE_MS, 5);
} else if (minRecordingInterval == PreferencesUtils.getMinRecordingIntervalAdaptAccuracy(context)) {
// Get all the updates.
locationListenerPolicy = new AdaptiveLocationListenerPolicy(UnitConversions.ONE_SECOND_MS, 30 * UnitConversions.ONE_SECOND_MS, 0);
} else {
locationListenerPolicy = new AbsoluteLocationListenerPolicy(minRecordingInterval * UnitConversions.ONE_SECOND_MS);
}
if (locationManager != null) {
registerLocationListener();
}
}
if (PreferencesUtils.isKey(context, R.string.recording_gps_accuracy_key, key)) {
recordingGpsAccuracy = PreferencesUtils.getRecordingGPSAccuracy(context);
}
}
@Override
public void onLocationChanged(@NonNull Location location) {
// TODO do we still need to process the location processing in an asynchronous manner? Let's go to check it out.
computeLocation(location);
}
@Override
public void onStatusChanged(String provider, int status, Bundle extras) {
}
@Override
public void onProviderEnabled(@NonNull String provider) {
}
@Override
public void onProviderDisabled(@NonNull String provider) {
}
/**
* Checks if location is valid and builds a track point that will be send through HandlerServer.
*
* @param location {@link Location} object.
*/
private void computeLocation(Location location) {
if (!LocationUtils.isValidLocation(location)) {
Log.w(TAG, "Ignore newTrackPoint. location is invalid.");
return;
}
TrackPoint trackPoint = new TrackPoint(location);
if (!TrackPointUtils.fulfillsAccuracy(trackPoint, recordingGpsAccuracy)) {
Log.d(TAG, "Ignore newTrackPoint. Poor accuracy.");
return;
}
long idleTime = 0L;
if (TrackPointUtils.after(trackPoint, lastValidTrackPoint)) {
idleTime = trackPoint.getTime() - lastValidTrackPoint.getTime();
}
locationListenerPolicy.updateIdleTime(idleTime);
if (currentRecordingInterval != locationListenerPolicy.getDesiredPollingInterval()) {
registerLocationListener();
}
lastValidTrackPoint = trackPoint;
handlerServer.sendTrackPoint(trackPoint, recordingGpsAccuracy);
}
private void registerLocationListener() {
if (locationManager == null) {
Log.e(TAG, "locationManager is null.");
return;
}
try {
long interval = locationListenerPolicy.getDesiredPollingInterval();
currentRecordingInterval = interval;
locationManager.requestLocationUpdates(LocationManager.GPS_PROVIDER, interval, locationListenerPolicy.getMinDistance_m(), this);
} catch (SecurityException e) {
Log.e(TAG, "Could not register location listener; permissions not granted.", e);
}
}
private void unregisterLocationListener() {
if (locationManager == null) {
Log.e(TAG, "locationManager is null.");
return;
}
locationManager.removeUpdates(this);
locationManager = null;
}
}
@@ -0,0 +1,44 @@
/*
* Copyright 2009 Google Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License"); you may not
* use this file except in compliance with the License. You may obtain a copy of
* the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations under
* the License.
*/
package de.dennisguse.opentracks.services.handlers;
/**
* This is an interface for classes that will manage the location listener policy.
*
* @author Sandor Dornbush
*/
interface LocationListenerPolicy {
/**
* Returns the polling interval this policy would like at this moment.
*
* @return the polling interval
*/
long getDesiredPollingInterval();
/**
* Returns the minimum distance between updates.
*/
int getMinDistance_m();
/**
* Notifies the amount of time the user has been idle at his current location.
*
* @param idleTime the time that the user has been idle at his current location
*/
void updateIdleTime(long idleTime);
}