forked from upstream-mirrors/OpenTracks
Refactor Bluetooth LE stack to use autoReconnect.
Removed additional timerTask to reconnect (was not working anymore). Fixed #41.
This commit is contained in:
@@ -8,6 +8,7 @@ public enum SensorState {
|
|||||||
NONE,
|
NONE,
|
||||||
CONNECTING,
|
CONNECTING,
|
||||||
CONNECTED,
|
CONNECTED,
|
||||||
|
DISCONNECTING,
|
||||||
DISCONNECTED;
|
DISCONNECTED;
|
||||||
|
|
||||||
public static String getStateAsString(SensorState state, Context c) {
|
public static String getStateAsString(SensorState state, Context c) {
|
||||||
@@ -18,6 +19,8 @@ public enum SensorState {
|
|||||||
return c.getString(R.string.sensor_state_connecting);
|
return c.getString(R.string.sensor_state_connecting);
|
||||||
case CONNECTED:
|
case CONNECTED:
|
||||||
return c.getString(R.string.sensor_state_connected);
|
return c.getString(R.string.sensor_state_connected);
|
||||||
|
case DISCONNECTING:
|
||||||
|
return c.getString(R.string.sensor_state_disconnected);
|
||||||
case DISCONNECTED:
|
case DISCONNECTED:
|
||||||
return c.getString(R.string.sensor_state_disconnected);
|
return c.getString(R.string.sensor_state_disconnected);
|
||||||
default:
|
default:
|
||||||
|
|||||||
+52
-40
@@ -58,22 +58,37 @@ public class BluetoothConnectionManager {
|
|||||||
|
|
||||||
private SensorState sensorState;
|
private SensorState sensorState;
|
||||||
|
|
||||||
|
private BluetoothGatt bluetoothGatt;
|
||||||
|
private BluetoothDevice bluetoothDevice;
|
||||||
|
|
||||||
private BluetoothGattCallback connectCallback = new BluetoothGattCallback() {
|
private BluetoothGattCallback connectCallback = new BluetoothGattCallback() {
|
||||||
@Override
|
@Override
|
||||||
public void onConnectionStateChange(BluetoothGatt gatt, int status, int newState) {
|
public void onConnectionStateChange(BluetoothGatt gatt, int status, int newState) {
|
||||||
if (newState == BluetoothProfile.STATE_CONNECTED) {
|
switch (newState) {
|
||||||
gatt.discoverServices();
|
case BluetoothProfile.STATE_CONNECTING:
|
||||||
setState(SensorState.CONNECTED);
|
Log.d(TAG, "Connecting to sensor: " + gatt.getDevice());
|
||||||
|
setState(SensorState.CONNECTING);
|
||||||
|
case BluetoothProfile.STATE_CONNECTED:
|
||||||
|
Log.d(TAG, "Connected to sensor: " + gatt.getDevice());
|
||||||
|
setState(SensorState.CONNECTED);
|
||||||
|
|
||||||
//Inform about status change
|
gatt.discoverServices();
|
||||||
Message message = handler.obtainMessage(MESSAGE_DEVICE_NAME);
|
|
||||||
Bundle bundle = new Bundle();
|
//Inform about status change
|
||||||
bundle.putString(KEY_DEVICE_NAME, gatt.getDevice().getName());
|
Message message = handler.obtainMessage(MESSAGE_DEVICE_NAME);
|
||||||
message.setData(bundle);
|
Bundle bundle = new Bundle();
|
||||||
handler.sendMessage(message);
|
bundle.putString(KEY_DEVICE_NAME, gatt.getDevice().getName());
|
||||||
return;
|
message.setData(bundle);
|
||||||
|
handler.sendMessage(message);
|
||||||
|
break;
|
||||||
|
case BluetoothProfile.STATE_DISCONNECTING:
|
||||||
|
Log.d(TAG, "Disconnecting from sensor: " + gatt.getDevice());
|
||||||
|
setState(SensorState.DISCONNECTING);
|
||||||
|
|
||||||
|
case BluetoothProfile.STATE_DISCONNECTED:
|
||||||
|
Log.d(TAG, "Disconnected from sensor: " + gatt.getDevice());
|
||||||
|
setState(SensorState.DISCONNECTED);
|
||||||
}
|
}
|
||||||
Log.d(TAG, "Could not connect to bluetooth sensor: " + gatt.getDevice());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -104,45 +119,42 @@ public class BluetoothConnectionManager {
|
|||||||
*
|
*
|
||||||
* @param handler a handler for sending messages back to the UI activity
|
* @param handler a handler for sending messages back to the UI activity
|
||||||
*/
|
*/
|
||||||
BluetoothConnectionManager(Context context, Handler handler) {
|
BluetoothConnectionManager(@NonNull Context context, @NonNull BluetoothDevice bluetoothDevice, @NonNull Handler handler) {
|
||||||
this.context = context;
|
this.context = context;
|
||||||
|
this.bluetoothDevice = bluetoothDevice;
|
||||||
this.handler = handler;
|
this.handler = handler;
|
||||||
this.sensorState = SensorState.NONE;
|
this.sensorState = SensorState.NONE;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
public synchronized void connect() {
|
||||||
* Gets the sensor state.
|
if (bluetoothGatt != null) {
|
||||||
*/
|
Log.w(TAG, "Already connected; ignoring.");
|
||||||
|
}
|
||||||
|
|
||||||
|
Log.d(TAG, "Connecting to: " + bluetoothDevice);
|
||||||
|
|
||||||
|
setState(SensorState.CONNECTING);
|
||||||
|
bluetoothGatt = bluetoothDevice.connectGatt(this.context, true, this.connectCallback);
|
||||||
|
}
|
||||||
|
|
||||||
|
public synchronized void disconnect() {
|
||||||
|
if (bluetoothGatt == null) {
|
||||||
|
Log.w(TAG, "Cannot disconnect if not connected.");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
bluetoothGatt.close();
|
||||||
|
bluetoothGatt = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
public synchronized boolean isSameBluetoothDevice(BluetoothDevice bluetoothDevice) {
|
||||||
|
return this.bluetoothDevice.equals(bluetoothDevice);
|
||||||
|
}
|
||||||
|
|
||||||
synchronized SensorState getSensorState() {
|
synchronized SensorState getSensorState() {
|
||||||
return sensorState;
|
return sensorState;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Sets the sensor state.
|
|
||||||
*
|
|
||||||
* @param sensorState the sensor state
|
|
||||||
*/
|
|
||||||
private synchronized void setState(SensorState sensorState) {
|
private synchronized void setState(SensorState sensorState) {
|
||||||
this.sensorState = sensorState;
|
this.sensorState = sensorState;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Resets the bluetooth connection manager.
|
|
||||||
*/
|
|
||||||
public synchronized void reset() {
|
|
||||||
//TODO Disconnect
|
|
||||||
setState(SensorState.NONE);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Connects to a bluetooth device.
|
|
||||||
*
|
|
||||||
* @param bluetoothDevice the bluetooth device
|
|
||||||
*/
|
|
||||||
public synchronized void connect(BluetoothDevice bluetoothDevice) {
|
|
||||||
Log.d(TAG, "connect to: " + bluetoothDevice);
|
|
||||||
|
|
||||||
bluetoothDevice.connectGatt(this.context, false, this.connectCallback);
|
|
||||||
setState(SensorState.CONNECTING);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
+25
-20
@@ -23,18 +23,17 @@ import android.os.Handler;
|
|||||||
import android.os.Looper;
|
import android.os.Looper;
|
||||||
import android.os.Message;
|
import android.os.Message;
|
||||||
import android.util.Log;
|
import android.util.Log;
|
||||||
|
import android.widget.Toast;
|
||||||
|
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
|
|
||||||
import de.dennisguse.opentracks.R;
|
import de.dennisguse.opentracks.R;
|
||||||
import de.dennisguse.opentracks.content.sensor.SensorDataSet;
|
import de.dennisguse.opentracks.content.sensor.SensorDataSet;
|
||||||
import de.dennisguse.opentracks.content.sensor.SensorState;
|
|
||||||
import de.dennisguse.opentracks.util.PreferencesUtils;
|
import de.dennisguse.opentracks.util.PreferencesUtils;
|
||||||
import de.dennisguse.opentracks.util.UnitConversions;
|
import de.dennisguse.opentracks.util.UnitConversions;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Bluetooth sensor manager.
|
* Bluetooth sensor manager.
|
||||||
* TODO: Handle a BluetoothGatt.STATE_DISCONNECTED
|
|
||||||
*
|
*
|
||||||
* @author Sandor Dornbush
|
* @author Sandor Dornbush
|
||||||
*/
|
*/
|
||||||
@@ -46,10 +45,6 @@ public class BluetoothRemoteSensorManager extends RemoteSensorManager {
|
|||||||
|
|
||||||
private final Context context;
|
private final Context context;
|
||||||
|
|
||||||
private final BluetoothConnectionManager bluetoothConnectionManager;
|
|
||||||
|
|
||||||
private SensorDataSet sensorDataSet = null;
|
|
||||||
|
|
||||||
// Handler that gets information back from the bluetoothConnectionManager
|
// Handler that gets information back from the bluetoothConnectionManager
|
||||||
private final Handler messageHandler = new Handler(Looper.getMainLooper()) {
|
private final Handler messageHandler = new Handler(Looper.getMainLooper()) {
|
||||||
@Override
|
@Override
|
||||||
@@ -57,6 +52,8 @@ public class BluetoothRemoteSensorManager extends RemoteSensorManager {
|
|||||||
switch (message.what) {
|
switch (message.what) {
|
||||||
case BluetoothConnectionManager.MESSAGE_DEVICE_NAME:
|
case BluetoothConnectionManager.MESSAGE_DEVICE_NAME:
|
||||||
String deviceName = message.getData().getString(BluetoothConnectionManager.KEY_DEVICE_NAME);
|
String deviceName = message.getData().getString(BluetoothConnectionManager.KEY_DEVICE_NAME);
|
||||||
|
String toastMessage = context.getString(R.string.settings_sensor_connected, deviceName);
|
||||||
|
Toast.makeText(context, toastMessage, Toast.LENGTH_LONG).show();
|
||||||
break;
|
break;
|
||||||
case BluetoothConnectionManager.MESSAGE_READ:
|
case BluetoothConnectionManager.MESSAGE_READ:
|
||||||
if (!(message.obj instanceof SensorDataSet)) {
|
if (!(message.obj instanceof SensorDataSet)) {
|
||||||
@@ -72,12 +69,14 @@ public class BluetoothRemoteSensorManager extends RemoteSensorManager {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
private SensorDataSet sensorDataSet = null;
|
||||||
|
private BluetoothConnectionManager bluetoothConnectionManager;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param context the context
|
* @param context the context
|
||||||
*/
|
*/
|
||||||
BluetoothRemoteSensorManager(Context context) {
|
BluetoothRemoteSensorManager(Context context) {
|
||||||
this.context = context;
|
this.context = context;
|
||||||
bluetoothConnectionManager = new BluetoothConnectionManager(context, messageHandler);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private static BluetoothAdapter getDefaultBluetoothAdapter() {
|
private static BluetoothAdapter getDefaultBluetoothAdapter() {
|
||||||
@@ -118,12 +117,7 @@ public class BluetoothRemoteSensorManager extends RemoteSensorManager {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean isEnabled() {
|
public void startSensor() {
|
||||||
return bluetoothAdapter != null && bluetoothAdapter.isEnabled();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected void setUpChannel() {
|
|
||||||
if (!isEnabled()) {
|
if (!isEnabled()) {
|
||||||
Log.w(TAG, "Bluetooth not enabled.");
|
Log.w(TAG, "Bluetooth not enabled.");
|
||||||
return;
|
return;
|
||||||
@@ -134,26 +128,37 @@ public class BluetoothRemoteSensorManager extends RemoteSensorManager {
|
|||||||
Log.w(TAG, "No bluetooth address.");
|
Log.w(TAG, "No bluetooth address.");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
Log.w(TAG, "Connecting to bluetooth address: " + address);
|
Log.i(TAG, "Connecting to bluetooth address: " + address);
|
||||||
|
|
||||||
BluetoothDevice device;
|
BluetoothDevice device;
|
||||||
try {
|
try {
|
||||||
device = bluetoothAdapter.getRemoteDevice(address);
|
device = bluetoothAdapter.getRemoteDevice(address);
|
||||||
} catch (IllegalArgumentException e) {
|
} catch (IllegalArgumentException e) {
|
||||||
Log.d(TAG, "Unable to get remote device for: " + address, e);
|
Log.w(TAG, "Unable to get remote device for: " + address, e);
|
||||||
|
|
||||||
|
String toastMessage = context.getString(R.string.sensor_not_known, address);
|
||||||
|
Toast.makeText(context, toastMessage, Toast.LENGTH_LONG).show();
|
||||||
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
bluetoothConnectionManager.connect(device);
|
|
||||||
|
stopSensor();
|
||||||
|
|
||||||
|
bluetoothConnectionManager = new BluetoothConnectionManager(context, device, messageHandler);
|
||||||
|
bluetoothConnectionManager.connect();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void tearDownChannel() {
|
public void stopSensor() {
|
||||||
bluetoothConnectionManager.reset();
|
if (bluetoothConnectionManager != null) {
|
||||||
|
bluetoothConnectionManager.disconnect();
|
||||||
|
bluetoothConnectionManager = null;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public SensorState getSensorState() {
|
public boolean isEnabled() {
|
||||||
return bluetoothConnectionManager.getSensorState();
|
return bluetoothAdapter != null && bluetoothAdapter.isEnabled();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|||||||
@@ -16,13 +16,7 @@
|
|||||||
|
|
||||||
package de.dennisguse.opentracks.services.sensors;
|
package de.dennisguse.opentracks.services.sensors;
|
||||||
|
|
||||||
import android.util.Log;
|
|
||||||
|
|
||||||
import java.util.Timer;
|
|
||||||
import java.util.TimerTask;
|
|
||||||
|
|
||||||
import de.dennisguse.opentracks.content.sensor.SensorDataSet;
|
import de.dennisguse.opentracks.content.sensor.SensorDataSet;
|
||||||
import de.dennisguse.opentracks.content.sensor.SensorState;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Manage the connection to a remote sensor.
|
* Manage the connection to a remote sensor.
|
||||||
@@ -32,32 +26,14 @@ import de.dennisguse.opentracks.content.sensor.SensorState;
|
|||||||
public abstract class RemoteSensorManager {
|
public abstract class RemoteSensorManager {
|
||||||
|
|
||||||
public static final long MAX_SENSOR_DATE_SET_AGE_MS = 5000;
|
public static final long MAX_SENSOR_DATE_SET_AGE_MS = 5000;
|
||||||
public static final long MAX_SENSOR_STATE_AGE_MS = 20000;
|
|
||||||
|
|
||||||
private static final String TAG = RemoteSensorManager.class.getSimpleName();
|
private static final String TAG = RemoteSensorManager.class.getSimpleName();
|
||||||
private static final int RETRY_PERIOD_MS = 20000;
|
|
||||||
|
|
||||||
private SensorState sensorState = SensorState.NONE;
|
|
||||||
private long sensorStateTimestamp_ms = System.currentTimeMillis();
|
|
||||||
|
|
||||||
private TimerTask timerTask;
|
|
||||||
private Timer timer;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns true if the sensor is enabled.
|
* Returns true if the sensor is enabled.
|
||||||
*/
|
*/
|
||||||
public abstract boolean isEnabled();
|
public abstract boolean isEnabled();
|
||||||
|
|
||||||
/**
|
|
||||||
* Sets up the sensor channel.
|
|
||||||
*/
|
|
||||||
protected abstract void setUpChannel();
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Tears down the sensor channel.
|
|
||||||
*/
|
|
||||||
protected abstract void tearDownChannel();
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Gets the sensor data set.
|
* Gets the sensor data set.
|
||||||
*/
|
*/
|
||||||
@@ -66,61 +42,12 @@ public abstract class RemoteSensorManager {
|
|||||||
/**
|
/**
|
||||||
* Starts the sensor.
|
* Starts the sensor.
|
||||||
*/
|
*/
|
||||||
public void startSensor() {
|
public abstract void startSensor();
|
||||||
setUpChannel();
|
|
||||||
timerTask = new TimerTask() {
|
|
||||||
@Override
|
|
||||||
public void run() {
|
|
||||||
switch (getSensorState()) {
|
|
||||||
case CONNECTING:
|
|
||||||
if (System.currentTimeMillis() - sensorStateTimestamp_ms > MAX_SENSOR_STATE_AGE_MS) {
|
|
||||||
Log.i(TAG, "Retry setUpChannel");
|
|
||||||
setUpChannel();
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case DISCONNECTED:
|
|
||||||
setUpChannel();
|
|
||||||
break;
|
|
||||||
default: // NONE, CONNECTED or SENDING
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
timer = new Timer(RemoteSensorManager.class.getSimpleName());
|
|
||||||
timer.schedule(timerTask, RETRY_PERIOD_MS, RETRY_PERIOD_MS);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Stops the sensor.
|
* Stops the sensor.
|
||||||
*/
|
*/
|
||||||
public void stopSensor() {
|
public abstract void stopSensor();
|
||||||
if (timerTask != null) {
|
|
||||||
timerTask.cancel();
|
|
||||||
timerTask = null;
|
|
||||||
}
|
|
||||||
if (timer != null) {
|
|
||||||
timer.cancel();
|
|
||||||
timer.purge();
|
|
||||||
timer = null;
|
|
||||||
}
|
|
||||||
tearDownChannel();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Gets the sensor state.
|
|
||||||
*/
|
|
||||||
public SensorState getSensorState() {
|
|
||||||
return sensorState;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Sets the sensor state.
|
|
||||||
*
|
|
||||||
* @param sensorState the sensor state
|
|
||||||
*/
|
|
||||||
public void setSensorState(SensorState sensorState) {
|
|
||||||
sensorStateTimestamp_ms = System.currentTimeMillis();
|
|
||||||
this.sensorState = sensorState;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns true if the sensor data set is valid.
|
* Returns true if the sensor data set is valid.
|
||||||
|
|||||||
@@ -1054,7 +1054,9 @@ limitations under the License.
|
|||||||
sending data.">
|
sending data.">
|
||||||
Sending
|
Sending
|
||||||
</string>
|
</string>
|
||||||
|
<string name="sensor_not_known" translation_description="Error message if sensor is not available in Android (e.g., pairing was removed in Bluetooth Settings).">
|
||||||
|
Sensor %1$s is not known. Please check the settings.
|
||||||
|
</string>
|
||||||
<!-- Settings -->
|
<!-- Settings -->
|
||||||
<string name="settings_not_while_recording" translation_description="In settings, the summary for an option that is not available during a
|
<string name="settings_not_while_recording" translation_description="In settings, the summary for an option that is not available during a
|
||||||
track recording.">
|
track recording.">
|
||||||
|
|||||||
Reference in New Issue
Block a user