forked from upstream-mirrors/OpenTracks
Cleanup: mainly code formatting and made LocationIterator implement autoclosable.
This commit is contained in:
@@ -17,8 +17,7 @@
|
||||
package de.dennisguse.opentracks.services;
|
||||
|
||||
/**
|
||||
* This is a simple location listener policy that will always dictate the same
|
||||
* polling interval.
|
||||
* This is a simple location listener policy that will always dictate the same polling interval.
|
||||
*
|
||||
* @author Sandor Dornbush
|
||||
*/
|
||||
@@ -41,7 +40,7 @@ public class AbsoluteLocationListenerPolicy implements LocationListenerPolicy {
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getMinDistance() {
|
||||
public int getMinDistance_m() {
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
+20
-25
@@ -17,53 +17,48 @@
|
||||
package de.dennisguse.opentracks.services;
|
||||
|
||||
/**
|
||||
* 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.
|
||||
* 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;
|
||||
private final long maxInterval;
|
||||
private final int minDistance;
|
||||
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 and
|
||||
* maxInterval.
|
||||
* Creates a policy that will be bounded by the given minInterval_ms and maxInterval_ms.
|
||||
*
|
||||
* @param minInterval the smallest interval this policy will dictate, in
|
||||
* milliseconds
|
||||
* @param maxInterval the largest interval this policy will dictate, in
|
||||
* milliseconds
|
||||
* @param minDistance the minimum distance in meters
|
||||
* @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, long maxInterval, int minDistance) {
|
||||
this.minInterval = minInterval;
|
||||
this.maxInterval = maxInterval;
|
||||
this.minDistance = minDistance;
|
||||
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 minInteval and
|
||||
* maxInterval.
|
||||
* Returns an interval half of the idle time, but bounded by minInterval_ms and maxInterval_ms.
|
||||
*/
|
||||
@Override
|
||||
public long getDesiredPollingInterval() {
|
||||
long desiredInterval = idleTime / 2;
|
||||
long desiredInterval_ms = idleTime / 2;
|
||||
// Round to second to avoid setting the interval too often
|
||||
desiredInterval = (desiredInterval / 1000) * 1000;
|
||||
return Math.max(Math.min(maxInterval, desiredInterval), minInterval);
|
||||
desiredInterval_ms = (desiredInterval_ms / 1000) * 1000;
|
||||
return Math.max(Math.min(maxInterval_ms, desiredInterval_ms), minInterval_ms);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getMinDistance() {
|
||||
return minDistance;
|
||||
public int getMinDistance_m() {
|
||||
return minDistance_m;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -30,12 +30,10 @@ import de.dennisguse.opentracks.R;
|
||||
import de.dennisguse.opentracks.widgets.TrackWidgetProvider;
|
||||
|
||||
/**
|
||||
* A service to control starting and stopping of a recording. This service,
|
||||
* through the AndroidManifest.xml, is configured to only allow components of
|
||||
* the same application to invoke it. Thus this service can be used my the
|
||||
* app widget, {@link TrackWidgetProvider}, but not by other applications. This
|
||||
* application delegates starting and stopping a recording to
|
||||
* {@link TrackRecordingService} using RPC calls.
|
||||
* A service to control starting and stopping of a recording.
|
||||
* This service, through the AndroidManifest.xml, is configured to only allow components of the same application to invoke it.
|
||||
* Thus this service can be used by the app widget, {@link TrackWidgetProvider}, but not by other applications.
|
||||
* This application delegates starting and stopping a recording to {@link TrackRecordingService} using RPC calls.
|
||||
*
|
||||
* @author Jimmy Shih
|
||||
*/
|
||||
@@ -60,9 +58,8 @@ public class ControlRecordingService extends IntentService implements ServiceCon
|
||||
}
|
||||
|
||||
@VisibleForTesting
|
||||
@Nullable
|
||||
@Override
|
||||
public IBinder onBind(Intent intent) {
|
||||
public IBinder onBind(@Nullable Intent intent) {
|
||||
return new LocalBinder();
|
||||
}
|
||||
|
||||
@@ -104,8 +101,7 @@ public class ControlRecordingService extends IntentService implements ServiceCon
|
||||
}
|
||||
|
||||
/**
|
||||
* Notifies all threads that connection to {@link TrackRecordingService} is
|
||||
* available.
|
||||
* Notifies all threads that connection to {@link TrackRecordingService} is available.
|
||||
*/
|
||||
private synchronized void notifyConnected() {
|
||||
connected = true;
|
||||
@@ -120,7 +116,6 @@ public class ControlRecordingService extends IntentService implements ServiceCon
|
||||
try {
|
||||
wait();
|
||||
} catch (InterruptedException e) {
|
||||
|
||||
// can safely ignore
|
||||
}
|
||||
}
|
||||
@@ -133,7 +128,7 @@ public class ControlRecordingService extends IntentService implements ServiceCon
|
||||
}
|
||||
|
||||
@VisibleForTesting
|
||||
public class LocalBinder extends Binder {
|
||||
class LocalBinder extends Binder {
|
||||
ControlRecordingService getService() {
|
||||
return ControlRecordingService.this;
|
||||
}
|
||||
|
||||
@@ -17,9 +17,10 @@ package de.dennisguse.opentracks.services;
|
||||
|
||||
import android.location.Location;
|
||||
|
||||
import androidx.annotation.VisibleForTesting;
|
||||
|
||||
import de.dennisguse.opentracks.content.WaypointCreationRequest;
|
||||
import de.dennisguse.opentracks.content.sensor.SensorDataSet;
|
||||
import de.dennisguse.opentracks.content.sensor.SensorState;
|
||||
|
||||
/**
|
||||
* App's service.
|
||||
@@ -97,6 +98,7 @@ public interface ITrackRecordingService {
|
||||
*
|
||||
* @param location the track point to be inserted
|
||||
*/
|
||||
@VisibleForTesting
|
||||
void insertTrackPoint(Location location);
|
||||
|
||||
/**
|
||||
@@ -105,11 +107,4 @@ public interface ITrackRecordingService {
|
||||
* @return SensorDataSet object.
|
||||
*/
|
||||
SensorDataSet getSensorData();
|
||||
|
||||
/**
|
||||
* Gets the current sensor manager state.
|
||||
* <p>
|
||||
* return a Sensor.SensorState enum value.
|
||||
*/
|
||||
SensorState getSensorState();
|
||||
}
|
||||
|
||||
@@ -17,9 +17,7 @@
|
||||
package de.dennisguse.opentracks.services;
|
||||
|
||||
/**
|
||||
* This is an interface for classes that will manage the location listener
|
||||
* policy. Different policy options are: {@link AbsoluteLocationListenerPolicy}
|
||||
* and {@link AdaptiveLocationListenerPolicy}.
|
||||
* This is an interface for classes that will manage the location listener policy.
|
||||
*
|
||||
* @author Sandor Dornbush
|
||||
*/
|
||||
@@ -35,13 +33,12 @@ public interface LocationListenerPolicy {
|
||||
/**
|
||||
* Returns the minimum distance between updates.
|
||||
*/
|
||||
int getMinDistance();
|
||||
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
|
||||
* @param idleTime the time that the user has been idle at his current location
|
||||
*/
|
||||
void updateIdleTime(long idleTime);
|
||||
}
|
||||
|
||||
@@ -57,7 +57,6 @@ import de.dennisguse.opentracks.content.Waypoint;
|
||||
import de.dennisguse.opentracks.content.Waypoint.WaypointType;
|
||||
import de.dennisguse.opentracks.content.WaypointCreationRequest;
|
||||
import de.dennisguse.opentracks.content.sensor.SensorDataSet;
|
||||
import de.dennisguse.opentracks.content.sensor.SensorState;
|
||||
import de.dennisguse.opentracks.services.sensors.RemoteSensorManager;
|
||||
import de.dennisguse.opentracks.services.sensors.RemoteSensorManagerFactory;
|
||||
import de.dennisguse.opentracks.services.tasks.AnnouncementPeriodicTaskFactory;
|
||||
@@ -947,7 +946,7 @@ public class TrackRecordingService extends Service {
|
||||
}
|
||||
try {
|
||||
long interval = locationListenerPolicy.getDesiredPollingInterval();
|
||||
locationManagerConnector.requestLocationUpdates(interval, locationListenerPolicy.getMinDistance(), locationListener);
|
||||
locationManagerConnector.requestLocationUpdates(interval, locationListenerPolicy.getMinDistance_m(), locationListener);
|
||||
currentRecordingInterval = interval;
|
||||
} catch (RuntimeException e) {
|
||||
Log.e(TAG, "Could not register location listener.", e);
|
||||
@@ -1116,15 +1115,6 @@ public class TrackRecordingService extends Service {
|
||||
return trackRecordingService.remoteSensorManager.getSensorDataSet();
|
||||
}
|
||||
|
||||
@Override
|
||||
public SensorState getSensorState() {
|
||||
if (trackRecordingService.remoteSensorManager == null) {
|
||||
Log.d(TAG, "remoteSensorManager is null.");
|
||||
return SensorState.NONE;
|
||||
}
|
||||
return trackRecordingService.remoteSensorManager.getSensorState();
|
||||
}
|
||||
|
||||
/**
|
||||
* Detaches from the track recording service. Clears the reference to the
|
||||
* outer class to minimize the leak.
|
||||
|
||||
+8
-5
@@ -25,12 +25,13 @@ import android.os.IBinder.DeathRecipient;
|
||||
import android.os.RemoteException;
|
||||
import android.util.Log;
|
||||
|
||||
import de.dennisguse.opentracks.util.TrackRecordingServiceConnectionUtils;
|
||||
import de.dennisguse.opentracks.BuildConfig;
|
||||
import de.dennisguse.opentracks.util.TrackRecordingServiceConnectionUtils;
|
||||
|
||||
/**
|
||||
* Wrapper for the track recording service. This handles service
|
||||
* start/bind/unbind/stop. The service must be started before it can be bound.
|
||||
* Wrapper for the track recording service.
|
||||
* This handles service start/bind/unbind/stop.
|
||||
* The service must be started before it can be bound.
|
||||
* Returns the service if it is started and bound.
|
||||
*
|
||||
* @author Rodrigo Damazio
|
||||
@@ -38,8 +39,10 @@ import de.dennisguse.opentracks.BuildConfig;
|
||||
public class TrackRecordingServiceConnection {
|
||||
|
||||
private static final String TAG = TrackRecordingServiceConnection.class.getSimpleName();
|
||||
|
||||
private final Context context;
|
||||
private final Runnable callback;
|
||||
|
||||
private ITrackRecordingService trackRecordingService;
|
||||
private final DeathRecipient deathRecipient = new DeathRecipient() {
|
||||
@Override
|
||||
@@ -48,6 +51,7 @@ public class TrackRecordingServiceConnection {
|
||||
setTrackRecordingService(null);
|
||||
}
|
||||
};
|
||||
|
||||
private final ServiceConnection serviceConnection = new ServiceConnection() {
|
||||
@Override
|
||||
public void onServiceConnected(ComponentName className, IBinder service) {
|
||||
@@ -130,8 +134,7 @@ public class TrackRecordingServiceConnection {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!startIfNeeded
|
||||
&& !TrackRecordingServiceConnectionUtils.isRecordingServiceRunning(context)) {
|
||||
if (!startIfNeeded && !TrackRecordingServiceConnectionUtils.isRecordingServiceRunning(context)) {
|
||||
Log.d(TAG, "Service is not started. Not binding it.");
|
||||
return;
|
||||
}
|
||||
|
||||
+11
-7
@@ -30,11 +30,11 @@ import android.util.Log;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
|
||||
import java.util.UUID;
|
||||
|
||||
import de.dennisguse.opentracks.content.sensor.SensorDataSet;
|
||||
import de.dennisguse.opentracks.content.sensor.SensorState;
|
||||
|
||||
import java.util.UUID;
|
||||
|
||||
/**
|
||||
* Manages connection to Bluetooth LE heart rate monitor.
|
||||
* TODO: shutdown connection.
|
||||
@@ -42,16 +42,20 @@ import java.util.UUID;
|
||||
public class BluetoothConnectionManager {
|
||||
|
||||
// Message types sent to handler
|
||||
public static final int MESSAGE_DEVICE_NAME = 1;
|
||||
public static final int MESSAGE_READ = 2;
|
||||
public static final String KEY_DEVICE_NAME = "device_name";
|
||||
static final int MESSAGE_DEVICE_NAME = 1;
|
||||
static final int MESSAGE_READ = 2;
|
||||
|
||||
static final String KEY_DEVICE_NAME = "device_name";
|
||||
|
||||
private static final UUID HEART_RATE_SERVICE_UUID = new UUID(0x180D00001000L, 0x800000805f9b34fbL);
|
||||
private static final UUID HEART_RATE_MEASUREMENT_CHAR_UUID = new UUID(0x2A3700001000L, 0x800000805f9b34fbL);
|
||||
private static final UUID CLIENT_CHARACTERISTIC_CONFIG_UUID = new UUID(0x290200001000L, 0x800000805f9b34fbL);
|
||||
|
||||
private static final String TAG = BluetoothConnectionManager.class.getSimpleName();
|
||||
|
||||
private final Context context;
|
||||
private final Handler handler;
|
||||
|
||||
private SensorState sensorState;
|
||||
|
||||
private BluetoothGattCallback connectCallback = new BluetoothGattCallback() {
|
||||
@@ -100,7 +104,7 @@ public class BluetoothConnectionManager {
|
||||
*
|
||||
* @param handler a handler for sending messages back to the UI activity
|
||||
*/
|
||||
public BluetoothConnectionManager(Context context, Handler handler) {
|
||||
BluetoothConnectionManager(Context context, Handler handler) {
|
||||
this.context = context;
|
||||
this.handler = handler;
|
||||
this.sensorState = SensorState.NONE;
|
||||
@@ -109,7 +113,7 @@ public class BluetoothConnectionManager {
|
||||
/**
|
||||
* Gets the sensor state.
|
||||
*/
|
||||
public synchronized SensorState getSensorState() {
|
||||
synchronized SensorState getSensorState() {
|
||||
return sensorState;
|
||||
}
|
||||
|
||||
|
||||
@@ -2,12 +2,12 @@ package de.dennisguse.opentracks.services.sensors;
|
||||
|
||||
import android.bluetooth.BluetoothGattCharacteristic;
|
||||
|
||||
public final class BluetoothLEUtils {
|
||||
final class BluetoothLEUtils {
|
||||
|
||||
private BluetoothLEUtils() {
|
||||
}
|
||||
|
||||
public static int parseHeartRate(BluetoothGattCharacteristic characteristic) {
|
||||
static int parseHeartRate(BluetoothGattCharacteristic characteristic) {
|
||||
//DOCUMENTATION https://www.bluetooth.com/specifications/gatt/characteristics/
|
||||
byte[] raw = characteristic.getValue();
|
||||
int index = ((raw[0] & 0x1) == 1) ? 2 : 1;
|
||||
|
||||
+7
-3
@@ -34,7 +34,6 @@ import de.dennisguse.opentracks.util.UnitConversions;
|
||||
|
||||
/**
|
||||
* Bluetooth sensor manager.
|
||||
* <p>
|
||||
* TODO: Handle a BluetoothGatt.STATE_DISCONNECTED
|
||||
*
|
||||
* @author Sandor Dornbush
|
||||
@@ -42,10 +41,15 @@ import de.dennisguse.opentracks.util.UnitConversions;
|
||||
public class BluetoothRemoteSensorManager extends RemoteSensorManager {
|
||||
|
||||
private static final String TAG = BluetoothConnectionManager.class.getSimpleName();
|
||||
|
||||
private static final BluetoothAdapter bluetoothAdapter = getDefaultBluetoothAdapter();
|
||||
|
||||
private final Context context;
|
||||
|
||||
private final BluetoothConnectionManager bluetoothConnectionManager;
|
||||
|
||||
private SensorDataSet sensorDataSet = null;
|
||||
|
||||
// Handler that gets information back from the bluetoothConnectionManager
|
||||
private final Handler messageHandler = new Handler(Looper.getMainLooper()) {
|
||||
@Override
|
||||
@@ -71,7 +75,7 @@ public class BluetoothRemoteSensorManager extends RemoteSensorManager {
|
||||
/**
|
||||
* @param context the context
|
||||
*/
|
||||
public BluetoothRemoteSensorManager(Context context) {
|
||||
BluetoothRemoteSensorManager(Context context) {
|
||||
this.context = context;
|
||||
bluetoothConnectionManager = new BluetoothConnectionManager(context, messageHandler);
|
||||
}
|
||||
@@ -109,7 +113,6 @@ public class BluetoothRemoteSensorManager extends RemoteSensorManager {
|
||||
|
||||
if (adapters.get(0) == null) {
|
||||
Log.w(TAG, "No bluetooth adapter found.");
|
||||
return null;
|
||||
}
|
||||
return adapters.get(0);
|
||||
}
|
||||
@@ -125,6 +128,7 @@ public class BluetoothRemoteSensorManager extends RemoteSensorManager {
|
||||
Log.w(TAG, "Bluetooth not enabled.");
|
||||
return;
|
||||
}
|
||||
|
||||
String address = PreferencesUtils.getString(context, R.string.bluetooth_sensor_key, PreferencesUtils.BLUETOOTH_SENSOR_DEFAULT);
|
||||
if (PreferencesUtils.BLUETOOTH_SENSOR_DEFAULT.equals(address)) {
|
||||
Log.w(TAG, "No bluetooth address.");
|
||||
|
||||
@@ -78,13 +78,10 @@ public abstract class RemoteSensorManager {
|
||||
setUpChannel();
|
||||
}
|
||||
break;
|
||||
case NONE:
|
||||
case DISCONNECTED:
|
||||
setUpChannel();
|
||||
break;
|
||||
default:
|
||||
// CONNECTED or SENDING
|
||||
break;
|
||||
default: // NONE, CONNECTED or SENDING
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
+3
-1
@@ -26,6 +26,8 @@ import android.content.Context;
|
||||
public class RemoteSensorManagerFactory {
|
||||
|
||||
private static RemoteSensorManager remoteSensorManagerSystem = null;
|
||||
|
||||
//TODO Check if still needed? Is there a missing features?
|
||||
private static RemoteSensorManager remoteSensorManagerTemporary = null;
|
||||
|
||||
private RemoteSensorManagerFactory() {
|
||||
@@ -70,7 +72,7 @@ public class RemoteSensorManagerFactory {
|
||||
/**
|
||||
* Releases the temp sensor manager.
|
||||
*/
|
||||
public static void releaseSensorManagerTemporary() {
|
||||
private static void releaseSensorManagerTemporary() {
|
||||
if (remoteSensorManagerTemporary != null) {
|
||||
remoteSensorManagerTemporary.stopSensor();
|
||||
}
|
||||
|
||||
+34
-37
@@ -27,14 +27,14 @@ import android.util.Log;
|
||||
|
||||
import androidx.annotation.VisibleForTesting;
|
||||
|
||||
import java.util.Locale;
|
||||
|
||||
import de.dennisguse.opentracks.R;
|
||||
import de.dennisguse.opentracks.services.TrackRecordingService;
|
||||
import de.dennisguse.opentracks.stats.TripStatistics;
|
||||
import de.dennisguse.opentracks.util.PreferencesUtils;
|
||||
import de.dennisguse.opentracks.util.StringUtils;
|
||||
import de.dennisguse.opentracks.util.UnitConversions;
|
||||
import de.dennisguse.opentracks.R;
|
||||
|
||||
import java.util.Locale;
|
||||
|
||||
/**
|
||||
* This class will periodically announce the user's trip statistics.
|
||||
@@ -50,7 +50,9 @@ public class AnnouncementPeriodicTask implements PeriodicTask {
|
||||
static final float TTS_SPEECH_RATE = 0.9f;
|
||||
|
||||
private static final String TAG = AnnouncementPeriodicTask.class.getSimpleName();
|
||||
|
||||
private final Context context;
|
||||
|
||||
private final AudioManager audioManager;
|
||||
private final UtteranceProgressListener utteranceListener = new UtteranceProgressListener() {
|
||||
@Override
|
||||
@@ -69,13 +71,16 @@ public class AnnouncementPeriodicTask implements PeriodicTask {
|
||||
public void onError(String utteranceId) {
|
||||
}
|
||||
};
|
||||
|
||||
private TextToSpeech tts;
|
||||
// Response from TTS after its initialization
|
||||
private int initStatus = TextToSpeech.ERROR;
|
||||
// True if TTS engine is ready
|
||||
private boolean ready = false;
|
||||
|
||||
private boolean ttsReady = false;
|
||||
|
||||
// True if speech is allowed
|
||||
private boolean speechAllowed;
|
||||
|
||||
/**
|
||||
* Listener which updates {@link #speechAllowed} when the phone state changes.
|
||||
*/
|
||||
@@ -90,7 +95,7 @@ public class AnnouncementPeriodicTask implements PeriodicTask {
|
||||
}
|
||||
};
|
||||
|
||||
public AnnouncementPeriodicTask(Context context) {
|
||||
AnnouncementPeriodicTask(Context context) {
|
||||
this.context = context;
|
||||
audioManager = (AudioManager) context.getSystemService(Context.AUDIO_SERVICE);
|
||||
}
|
||||
@@ -131,13 +136,13 @@ public class AnnouncementPeriodicTask implements PeriodicTask {
|
||||
}
|
||||
|
||||
synchronized (this) {
|
||||
if (!ready) {
|
||||
ready = initStatus == TextToSpeech.SUCCESS;
|
||||
if (ready) {
|
||||
if (!ttsReady) {
|
||||
ttsReady = initStatus == TextToSpeech.SUCCESS;
|
||||
if (ttsReady) {
|
||||
onTtsReady();
|
||||
}
|
||||
}
|
||||
if (!ready) {
|
||||
if (!ttsReady) {
|
||||
Log.i(TAG, "TTS not ready.");
|
||||
return;
|
||||
}
|
||||
@@ -170,9 +175,8 @@ public class AnnouncementPeriodicTask implements PeriodicTask {
|
||||
Log.w(TAG, "Default locale not available, use English.");
|
||||
locale = Locale.ENGLISH;
|
||||
/*
|
||||
* TODO: instead of using english, load the language if missing and show a
|
||||
* toast if not supported. Not able to change the resource strings to
|
||||
* English.
|
||||
* TODO: instead of using english, load the language if missing and show a toast if not supported.
|
||||
* Not able to change the resource strings to English.
|
||||
*/
|
||||
}
|
||||
tts.setLanguage(locale);
|
||||
@@ -189,8 +193,7 @@ public class AnnouncementPeriodicTask implements PeriodicTask {
|
||||
* @param announcement the announcement
|
||||
*/
|
||||
private void speakAnnouncement(String announcement) {
|
||||
int result = audioManager.requestAudioFocus(
|
||||
null, TextToSpeech.Engine.DEFAULT_STREAM, AudioManager.AUDIOFOCUS_GAIN_TRANSIENT_MAY_DUCK);
|
||||
int result = audioManager.requestAudioFocus(null, TextToSpeech.Engine.DEFAULT_STREAM, AudioManager.AUDIOFOCUS_GAIN_TRANSIENT_MAY_DUCK);
|
||||
if (result == AudioManager.AUDIOFOCUS_REQUEST_FAILED) {
|
||||
Log.w(TAG, "Failed to request audio focus.");
|
||||
}
|
||||
@@ -223,7 +226,7 @@ public class AnnouncementPeriodicTask implements PeriodicTask {
|
||||
boolean metricUnits = PreferencesUtils.isMetricUnits(context);
|
||||
boolean reportSpeed = PreferencesUtils.isReportSpeed(context);
|
||||
double distance = tripStatistics.getTotalDistance() * UnitConversions.M_TO_KM;
|
||||
double speed = tripStatistics.getAverageMovingSpeed() * UnitConversions.MS_TO_KMH;
|
||||
double distancePerTime = tripStatistics.getAverageMovingSpeed() * UnitConversions.MS_TO_KMH;
|
||||
|
||||
if (distance == 0) {
|
||||
return context.getString(R.string.voice_total_distance_zero);
|
||||
@@ -231,26 +234,22 @@ public class AnnouncementPeriodicTask implements PeriodicTask {
|
||||
|
||||
if (!metricUnits) {
|
||||
distance *= UnitConversions.KM_TO_MI;
|
||||
speed *= UnitConversions.KM_TO_MI;
|
||||
distancePerTime *= UnitConversions.KM_TO_MI;
|
||||
}
|
||||
|
||||
String rate;
|
||||
if (reportSpeed) {
|
||||
int speedId = metricUnits ? R.plurals.voiceSpeedKilometersPerHour
|
||||
: R.plurals.voiceSpeedMilesPerHour;
|
||||
rate = context.getResources().getQuantityString(speedId, getQuantityCount(speed), speed);
|
||||
int speedId = metricUnits ? R.plurals.voiceSpeedKilometersPerHour : R.plurals.voiceSpeedMilesPerHour;
|
||||
rate = context.getResources().getQuantityString(speedId, getQuantityCount(distancePerTime), distancePerTime);
|
||||
} else {
|
||||
speed = speed == 0 ? 0.0 : 1 / speed;
|
||||
double timePerDistance = distancePerTime == 0 ? 0.0 : 1 / distancePerTime;
|
||||
int paceId = metricUnits ? R.string.voice_pace_per_kilometer : R.string.voice_pace_per_mile;
|
||||
long time = Math.round(
|
||||
speed * UnitConversions.HR_TO_MIN * UnitConversions.MIN_TO_S * UnitConversions.S_TO_MS);
|
||||
long time = Math.round(timePerDistance * UnitConversions.HR_TO_MIN * UnitConversions.MIN_TO_S * UnitConversions.S_TO_MS);
|
||||
rate = context.getString(paceId, getAnnounceTime(time));
|
||||
}
|
||||
|
||||
int totalDistanceId = metricUnits ? R.plurals.voiceTotalDistanceKilometers
|
||||
: R.plurals.voiceTotalDistanceMiles;
|
||||
String totalDistance = context.getResources()
|
||||
.getQuantityString(totalDistanceId, getQuantityCount(distance), distance);
|
||||
int totalDistanceId = metricUnits ? R.plurals.voiceTotalDistanceKilometers : R.plurals.voiceTotalDistanceMiles;
|
||||
String totalDistance = context.getResources().getQuantityString(totalDistanceId, getQuantityCount(distance), distance);
|
||||
|
||||
return context.getString(R.string.voice_template, totalDistance,
|
||||
getAnnounceTime(tripStatistics.getMovingTime()), rate);
|
||||
@@ -264,8 +263,7 @@ public class AnnouncementPeriodicTask implements PeriodicTask {
|
||||
*/
|
||||
@VisibleForTesting
|
||||
protected void listenToPhoneState(PhoneStateListener listener, int events) {
|
||||
TelephonyManager telephony = (TelephonyManager) context.getSystemService(
|
||||
Context.TELEPHONY_SERVICE);
|
||||
TelephonyManager telephony = (TelephonyManager) context.getSystemService(Context.TELEPHONY_SERVICE);
|
||||
if (telephony != null) {
|
||||
telephony.listen(listener, events);
|
||||
}
|
||||
@@ -297,15 +295,13 @@ public class AnnouncementPeriodicTask implements PeriodicTask {
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the plural count to be used by getQuantityString. getQuantityString
|
||||
* only supports integer quantities, not a double quantity like "2.2".
|
||||
* Gets the plural count to be used by getQuantityString.
|
||||
* getQuantityString only supports integer quantities, not a double quantity like "2.2".
|
||||
* <p>
|
||||
* As a temporary workaround, we convert a double quantity to an integer
|
||||
* quantity. If the double quantity is exactly 0, 1, or 2, then we can return
|
||||
* these integer quantities. Otherwise, we cast the double quantity to an
|
||||
* integer quantity. However, we need to make sure that if the casted value is
|
||||
* 0, 1, or 2, we don't return those, instead, return the next biggest integer
|
||||
* 3.
|
||||
* As a temporary workaround, we convert a double quantity to an integer quantity.
|
||||
* If the double quantity is exactly 0, 1, or 2, then we can return these integer quantities.
|
||||
* Otherwise, we cast the double quantity to an integer quantity.
|
||||
* However, we need to make sure that if the casted value is 0, 1, or 2, we don't return those, instead, return the next biggest integer 3.
|
||||
*
|
||||
* @param d the double value
|
||||
*/
|
||||
@@ -317,6 +313,7 @@ public class AnnouncementPeriodicTask implements PeriodicTask {
|
||||
} else if (d == 2) {
|
||||
return 2;
|
||||
} else {
|
||||
//TODO This seems weird; why not use Math.round(d) or Math.ceil()?
|
||||
int count = (int) d;
|
||||
return count < 3 ? 3 : count;
|
||||
}
|
||||
|
||||
@@ -24,6 +24,7 @@ import de.dennisguse.opentracks.services.TrackRecordingService;
|
||||
* @author Sandor Dornbush
|
||||
*/
|
||||
public interface PeriodicTask {
|
||||
|
||||
/**
|
||||
* Sets up this task for subsequent calls to the run method.
|
||||
*/
|
||||
|
||||
@@ -36,9 +36,10 @@ public class PeriodicTaskExecutor {
|
||||
private final PeriodicTaskFactory periodicTaskFactory;
|
||||
|
||||
/**
|
||||
* The task frequency. A positive value is a time frequency (minutes). A
|
||||
* negative value is a distance frequency (km or mi). A zero value is to turn
|
||||
* off periodic task.
|
||||
* The task frequency.
|
||||
* A positive value is a time frequency (minutes).
|
||||
* A negative value is a distance frequency (km or mi).
|
||||
* A zero value is to turn off periodic task.
|
||||
*/
|
||||
private int taskFrequency = PreferencesUtils.FREQUENCY_OFF;
|
||||
|
||||
@@ -52,8 +53,7 @@ public class PeriodicTaskExecutor {
|
||||
// The next distance for the distance periodic task
|
||||
private double nextTaskDistance = Double.MAX_VALUE;
|
||||
|
||||
public PeriodicTaskExecutor(
|
||||
TrackRecordingService trackRecordingService, PeriodicTaskFactory periodicTaskFactory) {
|
||||
public PeriodicTaskExecutor(TrackRecordingService trackRecordingService, PeriodicTaskFactory periodicTaskFactory) {
|
||||
this.trackRecordingService = trackRecordingService;
|
||||
this.periodicTaskFactory = periodicTaskFactory;
|
||||
}
|
||||
@@ -67,11 +67,9 @@ public class PeriodicTaskExecutor {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!isTimeFrequency()) {
|
||||
if (timerTaskExecutor != null) {
|
||||
timerTaskExecutor.shutdown();
|
||||
timerTaskExecutor = null;
|
||||
}
|
||||
if (!isTimeFrequency() && timerTaskExecutor != null) {
|
||||
timerTaskExecutor.shutdown();
|
||||
timerTaskExecutor = null;
|
||||
}
|
||||
if (taskFrequency == PreferencesUtils.FREQUENCY_OFF) {
|
||||
Log.d(TAG, "Task frequency is off.");
|
||||
@@ -119,12 +117,13 @@ public class PeriodicTaskExecutor {
|
||||
if (!isDistanceFrequency() || periodicTask == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
TripStatistics tripStatistics = trackRecordingService.getTripStatistics();
|
||||
if (tripStatistics == null) {
|
||||
return;
|
||||
}
|
||||
double distance = tripStatistics.getTotalDistance()
|
||||
* UnitConversions.M_TO_KM;
|
||||
|
||||
double distance = tripStatistics.getTotalDistance() * UnitConversions.M_TO_KM;
|
||||
if (!metricUnits) {
|
||||
distance *= UnitConversions.KM_TO_MI;
|
||||
}
|
||||
@@ -159,8 +158,7 @@ public class PeriodicTaskExecutor {
|
||||
* Calculates the next distance for the distance periodic task.
|
||||
*/
|
||||
private void calculateNextTaskDistance() {
|
||||
if (!trackRecordingService.isRecording() || trackRecordingService.isPaused()
|
||||
|| periodicTask == null) {
|
||||
if (!trackRecordingService.isRecording() || trackRecordingService.isPaused() || periodicTask == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -175,11 +173,11 @@ public class PeriodicTaskExecutor {
|
||||
return;
|
||||
}
|
||||
|
||||
double distance = tripStatistics.getTotalDistance()
|
||||
* UnitConversions.M_TO_KM;
|
||||
double distance = tripStatistics.getTotalDistance() * UnitConversions.M_TO_KM;
|
||||
if (!metricUnits) {
|
||||
distance *= UnitConversions.KM_TO_MI;
|
||||
}
|
||||
|
||||
// The index will be negative since the frequency is negative.
|
||||
int index = (int) (distance / taskFrequency);
|
||||
index -= 1;
|
||||
|
||||
@@ -16,19 +16,19 @@
|
||||
|
||||
package de.dennisguse.opentracks.services.tasks;
|
||||
|
||||
import de.dennisguse.opentracks.services.TrackRecordingService;
|
||||
import de.dennisguse.opentracks.stats.TripStatistics;
|
||||
|
||||
import java.util.Date;
|
||||
import java.util.Timer;
|
||||
import java.util.TimerTask;
|
||||
|
||||
import de.dennisguse.opentracks.services.TrackRecordingService;
|
||||
import de.dennisguse.opentracks.stats.TripStatistics;
|
||||
|
||||
/**
|
||||
* This class will periodically perform a task.
|
||||
*
|
||||
* @author Sandor Dornbush
|
||||
*/
|
||||
public class TimerTaskExecutor {
|
||||
class TimerTaskExecutor {
|
||||
|
||||
private final PeriodicTask periodicTask;
|
||||
private final TrackRecordingService trackRecordingService;
|
||||
@@ -42,12 +42,12 @@ public class TimerTaskExecutor {
|
||||
}
|
||||
|
||||
/**
|
||||
* Schedules the periodic task at an interval.
|
||||
* Schedules the periodic task in milliseconds.
|
||||
*
|
||||
* @param interval the interval in milliseconds
|
||||
* @param interval_ms the interval_ms
|
||||
*/
|
||||
public void scheduleTask(long interval) {
|
||||
if (interval <= 0) {
|
||||
void scheduleTask(long interval_ms) {
|
||||
if (interval_ms <= 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -69,14 +69,11 @@ public class TimerTaskExecutor {
|
||||
}
|
||||
};
|
||||
timer = new Timer(TimerTaskExecutor.class.getSimpleName());
|
||||
long next = System.currentTimeMillis() + interval - (tripStatistics.getTotalTime() % interval);
|
||||
timer.scheduleAtFixedRate(timerTask, new Date(next), interval);
|
||||
long next = System.currentTimeMillis() + interval_ms - (tripStatistics.getTotalTime() % interval_ms);
|
||||
timer.scheduleAtFixedRate(timerTask, new Date(next), interval_ms);
|
||||
}
|
||||
|
||||
/**
|
||||
* Shuts down.
|
||||
*/
|
||||
public void shutdown() {
|
||||
void shutdown() {
|
||||
if (timerTask != null) {
|
||||
timerTask.cancel();
|
||||
timerTask = null;
|
||||
|
||||
Reference in New Issue
Block a user