Skip to content

Commit

Permalink
Implement gain/loss via Bluetooth LE Environmental Sensing Sensor.
Browse files Browse the repository at this point in the history
Fixes #1424.
  • Loading branch information
dennisguse committed Dec 29, 2023
1 parent 8eed8e9 commit a5a44c6
Show file tree
Hide file tree
Showing 11 changed files with 138 additions and 107 deletions.
3 changes: 1 addition & 2 deletions src/main/java/de/dennisguse/opentracks/Startup.java
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@
import android.app.Application;
import android.content.Context;
import android.os.Build;
import android.os.StrictMode;
import android.util.Log;

import com.google.android.material.color.DynamicColors;
Expand Down Expand Up @@ -33,7 +32,7 @@ public void onCreate() {
// In debug builds: show thread and VM warnings.
if (BuildConfig.DEBUG) {
Log.d(TAG, "Enabling strict mode");
StrictMode.enableDefaults();
// StrictMode.enableDefaults();
}

PreferencesUtils.initPreferences(this, getResources());
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
package de.dennisguse.opentracks.sensors;

import android.annotation.SuppressLint;
import android.bluetooth.BluetoothAdapter;
import android.bluetooth.BluetoothDevice;
import android.bluetooth.BluetoothGatt;
import android.bluetooth.BluetoothGattCallback;
Expand All @@ -33,19 +34,22 @@
import java.util.Optional;
import java.util.UUID;

import de.dennisguse.opentracks.sensors.driver.Driver;
import de.dennisguse.opentracks.sensors.sensorData.SensorHandlerInterface;

/**
* Manages connection to a Bluetooth LE sensor and subscribes for onChange-notifications.
*/
@SuppressLint("MissingPermission")
public class BluetoothConnectionManager {
public class BluetoothConnectionManager implements Driver {

private static final String TAG = BluetoothConnectionManager.class.getSimpleName();

private final SensorManager.SensorDataChangedObserver observer;

private final SensorHandlerInterface sensorHandler;

private final BluetoothAdapter bluetoothAdapter;
private BluetoothGatt bluetoothGatt;
private final BluetoothGattCallback connectCallback = new BluetoothGattCallback() {
@Override
Expand Down Expand Up @@ -127,17 +131,46 @@ public void onCharacteristicChanged(BluetoothGatt gatt, @NonNull BluetoothGattCh
}
};

BluetoothConnectionManager(SensorManager.SensorDataChangedObserver observer, SensorHandlerInterface sensorHandler) {
BluetoothConnectionManager(BluetoothAdapter bluetoothAdapter, SensorManager.SensorDataChangedObserver observer, SensorHandlerInterface sensorHandler) {
this.bluetoothAdapter = bluetoothAdapter;
this.observer = observer;
this.sensorHandler = sensorHandler;
}

synchronized void connect(Context context, Handler handler, @NonNull BluetoothDevice device) {
if (bluetoothGatt != null) {
@Override
public synchronized void connect(Context context, Handler handler, @NonNull String address) {
if (!isBluetoothEnabled()) {
Log.w(TAG, "Bluetooth not enabled.");
return;
}

if (SensorType.NONE.getPreferenceValue().equals(address)) {
Log.w(TAG, "NONE: going to disconnect");
if (isConnected()) {
disconnect();
}
return;
}

if (isConnected()) {
Log.w(TAG, "Already connected; ignoring.");
return;
}

if (isSameBluetoothDevice(address)) {
return;
} else {
disconnect();
}

BluetoothDevice device;
try {
device = bluetoothAdapter.getRemoteDevice(address);
} catch (IllegalArgumentException e) {
Log.e(TAG, address + ": Unable to get remote device for", e);
return;
}

Log.d(TAG, device + ": trying to connect");

bluetoothGatt = device.connectGatt(context, false, connectCallback, BluetoothDevice.TRANSPORT_AUTO, 0, handler);
Expand All @@ -149,8 +182,10 @@ private synchronized void clearData() {
observer.onDisconnect(sensorHandler.createEmptySensorData(bluetoothGatt.getDevice().getAddress(), bluetoothGatt.getDevice().getName()));
}

synchronized void disconnect() {
if (bluetoothGatt == null) {
@Override
public synchronized void disconnect() {
if (!isConnected()) {
Log.w(TAG, "Not connected; no need to re-connect.");
return;
}
Log.i(TAG, bluetoothGatt.getDevice() + ": start disconnect");
Expand All @@ -161,11 +196,21 @@ synchronized void disconnect() {
bluetoothGatt = null;
}

synchronized boolean isSameBluetoothDevice(String address) {
private synchronized boolean isSameBluetoothDevice(String address) {
if (bluetoothGatt == null) {
return false;
}

return address.equals(bluetoothGatt.getDevice().getAddress());
}


private boolean isBluetoothEnabled() {
return bluetoothAdapter != null && bluetoothAdapter.isEnabled();
}

@Override
public boolean isConnected() {
return bluetoothGatt != null;
}
}
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package de.dennisguse.opentracks.sensors;

import android.bluetooth.BluetoothGattCharacteristic;
import android.util.Log;

import java.util.List;
import java.util.UUID;
Expand Down Expand Up @@ -32,9 +33,15 @@ public List<ServiceMeasurementUUID> getServices() {
public void handlePayload(SensorManager.SensorDataChangedObserver observer, ServiceMeasurementUUID serviceMeasurementUUID, String sensorName, String address, BluetoothGattCharacteristic characteristic) {
AtmosphericPressure value = parseEnvironmentalSensing(characteristic);
if (value == null) return;
Log.i("ZXCV", "VALUE: " + value);
observer.onChange(new Raw<>(value));
}

/**
* Decoding:
* org.bluetooth.service.environmental_sensing.xml
* org.bluetooth.characteristic.pressure.xml
*/
public static AtmosphericPressure parseEnvironmentalSensing(BluetoothGattCharacteristic characteristic) {
byte[] raw = characteristic.getValue();

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,11 +17,9 @@
package de.dennisguse.opentracks.sensors;

import android.bluetooth.BluetoothAdapter;
import android.bluetooth.BluetoothDevice;
import android.content.Context;
import android.content.SharedPreferences;
import android.os.Handler;
import android.util.Log;

import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
Expand All @@ -30,7 +28,6 @@

import de.dennisguse.opentracks.R;
import de.dennisguse.opentracks.settings.PreferencesUtils;
import de.dennisguse.opentracks.util.PermissionRequester;

/**
* Bluetooth LE sensor manager: manages connections to Bluetooth LE sensors.
Expand All @@ -45,7 +42,6 @@ public class BluetoothRemoteSensorManager implements SensorConnector, SharedPref

public static final Duration MAX_SENSOR_DATE_SET_AGE = Duration.ofSeconds(5);

private final BluetoothAdapter bluetoothAdapter;
private final Context context;
private final Handler handler;
private boolean started = false;
Expand All @@ -59,14 +55,13 @@ public class BluetoothRemoteSensorManager implements SensorConnector, SharedPref
public BluetoothRemoteSensorManager(@NonNull Context context, @NonNull Handler handler, @Nullable SensorManager.SensorDataChangedObserver observer) {
this.context = context;
this.handler = handler;
bluetoothAdapter = BluetoothUtils.getAdapter(context);

this.heartRate = new BluetoothConnectionManager(observer, new BluetoothHandlerManagerHeartRate());
this.cyclingCadence = new BluetoothConnectionManager(observer, new BluetoothHandlerCyclingCadence());
this.cyclingSpeed = new BluetoothConnectionManager(observer, new BluetoothHandlerCyclingDistanceSpeed());
this.cyclingPower = new BluetoothConnectionManager(observer, new BluetoothHandlerManagerCyclingPower());
this.runningSpeedAndCadence = new BluetoothConnectionManager(observer, new BluetoothHandlerRunningSpeedAndCadence());

BluetoothAdapter bluetoothAdapter = BluetoothUtils.getAdapter(context);
this.heartRate = new BluetoothConnectionManager(bluetoothAdapter, observer, new BluetoothHandlerManagerHeartRate());
this.cyclingCadence = new BluetoothConnectionManager(bluetoothAdapter, observer, new BluetoothHandlerCyclingCadence());
this.cyclingSpeed = new BluetoothConnectionManager(bluetoothAdapter, observer, new BluetoothHandlerCyclingDistanceSpeed());
this.cyclingPower = new BluetoothConnectionManager(bluetoothAdapter, observer, new BluetoothHandlerManagerCyclingPower());
this.runningSpeedAndCadence = new BluetoothConnectionManager(bluetoothAdapter, observer, new BluetoothHandlerRunningSpeedAndCadence());
}

@Override
Expand All @@ -88,39 +83,8 @@ public synchronized void stop(Context context) {
started = false;
}

public boolean isEnabled() {
return bluetoothAdapter != null && bluetoothAdapter.isEnabled();
}

private synchronized void connect(BluetoothConnectionManager connectionManager, String address) {
if (!isEnabled()) {
Log.w(TAG, "Bluetooth not enabled.");
return;
}

if (SensorType.NONE.getPreferenceValue().equals(address)) {
Log.w(TAG, "No Bluetooth address.");
connectionManager.disconnect();
return;
}

// Check if there is an ongoing connection; if yes, check if the address changed.
if (connectionManager.isSameBluetoothDevice(address)) {
return;
} else {
connectionManager.disconnect();
}
if (!PermissionRequester.BLUETOOTH.hasPermission(context)) {
Log.w(TAG, "BLUETOOTH_SCAN and/or BLUETOOTH_CONNECT not granted; not connecting.");
}

Log.i(TAG, "Connecting to bluetooth address: " + address);
try {
BluetoothDevice device = bluetoothAdapter.getRemoteDevice(address);
connectionManager.connect(context, handler, device);
} catch (IllegalArgumentException e) {
Log.e(TAG, "Unable to get remote device for: " + address, e);
}
connectionManager.connect(context, handler, address);
}

@Override
Expand Down
37 changes: 15 additions & 22 deletions src/main/java/de/dennisguse/opentracks/sensors/GainManager.java
Original file line number Diff line number Diff line change
Expand Up @@ -5,12 +5,9 @@
import android.os.Handler;
import android.util.Log;

import androidx.annotation.NonNull;

import de.dennisguse.opentracks.data.models.AtmosphericPressure;
import de.dennisguse.opentracks.sensors.driver.BarometerInternal;
import de.dennisguse.opentracks.sensors.driver.Driver;
import de.dennisguse.opentracks.sensors.sensorData.AggregatorBarometer;
import de.dennisguse.opentracks.sensors.sensorData.Raw;
import de.dennisguse.opentracks.settings.PreferencesUtils;

/**
Expand All @@ -20,7 +17,6 @@ public class GainManager implements SensorConnector {

private static final String TAG = GainManager.class.getSimpleName();

private BarometerInternal driver;

private final SensorManager.SensorDataChangedObserver listener;

Expand All @@ -30,10 +26,10 @@ public class GainManager implements SensorConnector {

private Context context;
private Handler handler;
private Driver driver;

public GainManager(SensorManager.SensorDataChangedObserver listener) {
this.listener = listener;
driver = new BarometerInternal();
}

public void start(Context context, Handler handler) {
Expand All @@ -48,37 +44,34 @@ public void stop(Context context) {
this.handler = null;
PreferencesUtils.unregisterOnSharedPreferenceChangeListener(sharedPreferenceChangeListener);

onDisconnect(context);
}

public void onSensorValueChanged(AtmosphericPressure currentSensorValue) {
listener.onChange(new Raw<>(currentSensorValue));
onDisconnect();
}

private void connect() {
onDisconnect(context);
onDisconnect();

String address = PreferencesUtils.getBarometerSensorAddress();
switch (PreferencesUtils.getSensorType(address)) {
case NONE -> driver = null;
case INTERNAL -> driver = new BarometerInternal();
case REMOTE -> throw new RuntimeException("Not implemented"); //TODO #1424
case INTERNAL -> driver = new BarometerInternal(listener);
case REMOTE -> driver =
new BluetoothConnectionManager(
BluetoothUtils.getAdapter(context),
listener,
new BluetoothHandlerBarometricPressure()
);
default -> throw new RuntimeException("Not implemented");
}

if (driver != null) {
driver.connect(context, handler, this);

if (driver.isConnected()) {
listener.onConnect(new AggregatorBarometer("internal", null));
}
driver.connect(context, handler, address);
}
}

private void onDisconnect(@NonNull Context context) {
private void onDisconnect() {
if (driver == null) return;

driver.disconnect(context);
listener.onDisconnect(new AggregatorBarometer("internal", null));
driver.disconnect();
listener.onDisconnect(new AggregatorBarometer("GainManager", null));
}
}
Loading

0 comments on commit a5a44c6

Please sign in to comment.