forked from upstream-mirrors/OpenTracks
@@ -87,10 +87,10 @@ public class BluetoothUtilsTest {
|
||||
characteristic.setValue(new byte[]{0, 0, 40, 0});
|
||||
|
||||
// when
|
||||
SensorDataCyclingPower.Data powerCadence = BluetoothUtils.parseCyclingPower(characteristic);
|
||||
SensorDataCyclingPower.Data powerCadence = BluetoothUtils.parseCyclingPower("", "", characteristic);
|
||||
|
||||
// then
|
||||
assertEquals(40, powerCadence.getPower().getW(), 0.01);
|
||||
assertEquals(40, powerCadence.getPower().getValue().getW(), 0.01);
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -99,10 +99,10 @@ public class BluetoothUtilsTest {
|
||||
characteristic.setValue(new byte[]{0x2C, 0x00, 0x00, 0x00, (byte) 0x9F, 0x00, 0x0C, 0x00, (byte) 0xE5, 0x42});
|
||||
|
||||
// when
|
||||
SensorDataCyclingPower.Data powerCadence = BluetoothUtils.parseCyclingPower(characteristic);
|
||||
SensorDataCyclingPower.Data powerCadence = BluetoothUtils.parseCyclingPower("", "", characteristic);
|
||||
|
||||
// then
|
||||
assertEquals(0, powerCadence.getPower().getW(), 0.01);
|
||||
assertEquals(0, powerCadence.getPower().getValue().getW(), 0.01);
|
||||
|
||||
assertEquals(12, powerCadence.getCadence().getCrankRevolutionsCount());
|
||||
assertEquals(17125, powerCadence.getCadence().getCrankRevolutionsTime());
|
||||
|
||||
@@ -31,10 +31,10 @@ import android.util.Log;
|
||||
import androidx.annotation.NonNull;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Optional;
|
||||
import java.util.UUID;
|
||||
|
||||
import de.dennisguse.opentracks.data.models.HeartRate;
|
||||
import de.dennisguse.opentracks.sensors.sensorData.SensorData;
|
||||
import de.dennisguse.opentracks.sensors.sensorData.SensorDataHeartRate;
|
||||
|
||||
/**
|
||||
* Manages connection to a Bluetooth LE sensor and subscribes for onChange-notifications.
|
||||
@@ -111,8 +111,16 @@ public abstract class BluetoothConnectionManager<DataType> {
|
||||
|
||||
@Override
|
||||
public void onCharacteristicChanged(BluetoothGatt gatt, @NonNull BluetoothGattCharacteristic characteristic) {
|
||||
Log.d(TAG, "Received data from " + gatt.getDevice().getAddress());
|
||||
SensorData<DataType> sensorData = parsePayload(gatt.getDevice().getName(), gatt.getDevice().getAddress(), characteristic);
|
||||
UUID serviceUUID = characteristic.getService().getUuid();
|
||||
Log.d(TAG, "Received data from " + gatt.getDevice().getAddress() + " with service " + serviceUUID + " and characteristics " + characteristic.getUuid());
|
||||
Optional<ServiceMeasurementUUID> serviceMeasurementUUID = serviceMeasurementUUIDs.stream()
|
||||
.filter(s -> s.getServiceUUID().equals(characteristic.getService().getUuid())).findFirst();
|
||||
if (serviceMeasurementUUID.isEmpty()) {
|
||||
Log.e(TAG, "Unknown service UUID; not supported?");
|
||||
return;
|
||||
}
|
||||
|
||||
SensorData<DataType> sensorData = parsePayload(serviceMeasurementUUID.get(), gatt.getDevice().getName(), gatt.getDevice().getAddress(), characteristic);
|
||||
if (sensorData != null) {
|
||||
Log.d(TAG, "Decoded data from " + gatt.getDevice().getAddress() + ": " + sensorData);
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
|
||||
@@ -180,26 +188,7 @@ public abstract class BluetoothConnectionManager<DataType> {
|
||||
/**
|
||||
* @return null if data could not be parsed.
|
||||
*/
|
||||
protected abstract SensorData<DataType> parsePayload(String sensorName, String address, BluetoothGattCharacteristic characteristic);
|
||||
|
||||
public static class HeartRateConnectionManager extends BluetoothConnectionManager<HeartRate> {
|
||||
|
||||
HeartRateConnectionManager(@NonNull SensorDataObserver observer) {
|
||||
super(BluetoothUtils.HEART_RATE_SUPPORTING_DEVICES, observer);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected SensorDataHeartRate createEmptySensorData(String address) {
|
||||
return new SensorDataHeartRate(address);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected SensorDataHeartRate parsePayload(String sensorName, String address, BluetoothGattCharacteristic characteristic) {
|
||||
Integer heartRate = BluetoothUtils.parseHeartRate(characteristic);
|
||||
|
||||
return heartRate != null ? new SensorDataHeartRate(address, sensorName, HeartRate.of(heartRate)) : null;
|
||||
}
|
||||
}
|
||||
protected abstract SensorData<DataType> parsePayload(@NonNull ServiceMeasurementUUID serviceMeasurementUUID, String sensorName, String address, @NonNull BluetoothGattCharacteristic characteristic);
|
||||
|
||||
interface SensorDataObserver {
|
||||
|
||||
|
||||
+23
-9
@@ -1,15 +1,19 @@
|
||||
package de.dennisguse.opentracks.sensors;
|
||||
|
||||
import android.bluetooth.BluetoothGattCharacteristic;
|
||||
import android.util.Log;
|
||||
|
||||
import de.dennisguse.opentracks.data.models.Cadence;
|
||||
import de.dennisguse.opentracks.sensors.sensorData.SensorDataCyclingCadence;
|
||||
import de.dennisguse.opentracks.sensors.sensorData.SensorDataCyclingCadenceAndDistanceSpeed;
|
||||
import de.dennisguse.opentracks.sensors.sensorData.SensorDataCyclingPower;
|
||||
|
||||
public class BluetoothRemoteSensorManagerCyclingCadence extends BluetoothConnectionManager<Cadence> {
|
||||
|
||||
private static final String TAG = BluetoothRemoteSensorManagerCyclingCadence.class.getSimpleName();
|
||||
|
||||
BluetoothRemoteSensorManagerCyclingCadence(SensorDataObserver observer) {
|
||||
super(BluetoothUtils.CYCLING_SPEED_CADENCE, observer);
|
||||
super(BluetoothUtils.CYCLING_CADENCE, observer);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -18,16 +22,26 @@ public class BluetoothRemoteSensorManagerCyclingCadence extends BluetoothConnect
|
||||
}
|
||||
|
||||
@Override
|
||||
protected SensorDataCyclingCadence parsePayload(String sensorName, String address, BluetoothGattCharacteristic characteristic) {
|
||||
SensorDataCyclingCadenceAndDistanceSpeed cadenceAndSpeed = BluetoothUtils.parseCyclingCrankAndWheel(address, sensorName, characteristic);
|
||||
if (cadenceAndSpeed == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
if (cadenceAndSpeed.getCadence() != null) {
|
||||
return cadenceAndSpeed.getCadence();
|
||||
protected SensorDataCyclingCadence parsePayload(ServiceMeasurementUUID serviceMeasurementUUID, String sensorName, String address, BluetoothGattCharacteristic characteristic) {
|
||||
|
||||
//TODO Implement to ServiceMeasurement.parse()?
|
||||
if (serviceMeasurementUUID.equals(BluetoothUtils.CYCLING_POWER)) {
|
||||
SensorDataCyclingPower.Data data = BluetoothUtils.parseCyclingPower(address, sensorName, characteristic);
|
||||
if (data!= null) {
|
||||
return data.getCadence();
|
||||
}
|
||||
} else if (serviceMeasurementUUID.equals(BluetoothUtils.CYCLING_SPEED_CADENCE)) {
|
||||
SensorDataCyclingCadenceAndDistanceSpeed cadenceAndSpeed = BluetoothUtils.parseCyclingCrankAndWheel(address, sensorName, characteristic);
|
||||
if (cadenceAndSpeed == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
if (cadenceAndSpeed.getCadence() != null) {
|
||||
return cadenceAndSpeed.getCadence();
|
||||
}
|
||||
}
|
||||
|
||||
Log.e(TAG, "Don't know how to decode this payload.");
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
+1
-1
@@ -17,7 +17,7 @@ public class BluetoothRemoteSensorManagerCyclingDistanceSpeed extends BluetoothC
|
||||
}
|
||||
|
||||
@Override
|
||||
protected SensorDataCyclingDistanceSpeed parsePayload(String sensorName, String address, BluetoothGattCharacteristic characteristic) {
|
||||
protected SensorDataCyclingDistanceSpeed parsePayload(ServiceMeasurementUUID serviceMeasurementUUID, String sensorName, String address, BluetoothGattCharacteristic characteristic) {
|
||||
SensorDataCyclingCadenceAndDistanceSpeed cadenceAndSpeed = BluetoothUtils.parseCyclingCrankAndWheel(address, sensorName, characteristic);
|
||||
if (cadenceAndSpeed == null) {
|
||||
return null;
|
||||
|
||||
+3
-3
@@ -19,9 +19,9 @@ public class BluetoothRemoteSensorManagerCyclingPower extends BluetoothConnectio
|
||||
}
|
||||
|
||||
@Override
|
||||
protected SensorDataCyclingPower parsePayload(String sensorName, String address, BluetoothGattCharacteristic characteristic) {
|
||||
SensorDataCyclingPower.Data cyclingPower = BluetoothUtils.parseCyclingPower(characteristic);
|
||||
protected SensorDataCyclingPower parsePayload(@NonNull ServiceMeasurementUUID serviceMeasurementUUID, String sensorName, String address, BluetoothGattCharacteristic characteristic) {
|
||||
SensorDataCyclingPower.Data cyclingPower = BluetoothUtils.parseCyclingPower(address, sensorName, characteristic);
|
||||
|
||||
return cyclingPower != null ? new SensorDataCyclingPower(address, sensorName, cyclingPower.getPower()) : null;
|
||||
return cyclingPower != null ? cyclingPower.getPower() : null;
|
||||
}
|
||||
}
|
||||
|
||||
+1
-1
@@ -19,7 +19,7 @@ public class BluetoothRemoteSensorManagerHeartRate extends BluetoothConnectionMa
|
||||
}
|
||||
|
||||
@Override
|
||||
protected SensorDataHeartRate parsePayload(String sensorName, String address, BluetoothGattCharacteristic characteristic) {
|
||||
protected SensorDataHeartRate parsePayload(@NonNull ServiceMeasurementUUID serviceMeasurementUUID, String sensorName, String address, BluetoothGattCharacteristic characteristic) {
|
||||
Integer heartRate = BluetoothUtils.parseHeartRate(characteristic);
|
||||
|
||||
return heartRate != null ? new SensorDataHeartRate(address, sensorName, HeartRate.of(heartRate)) : null;
|
||||
|
||||
+1
-1
@@ -18,7 +18,7 @@ public class BluetoothRemoteSensorManagerRunningSpeedAndCadence extends Bluetoot
|
||||
}
|
||||
|
||||
@Override
|
||||
protected SensorDataRunning parsePayload(String sensorName, String address, BluetoothGattCharacteristic characteristic) {
|
||||
protected SensorDataRunning parsePayload(@NonNull ServiceMeasurementUUID serviceMeasurementUUID, String sensorName, String address, BluetoothGattCharacteristic characteristic) {
|
||||
return BluetoothUtils.parseRunningSpeedAndCadence(address, sensorName, characteristic);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -119,7 +119,7 @@ public class BluetoothUtils {
|
||||
return null;
|
||||
}
|
||||
|
||||
public static SensorDataCyclingPower.Data parseCyclingPower(BluetoothGattCharacteristic characteristic) {
|
||||
public static SensorDataCyclingPower.Data parseCyclingPower(String address, String sensorName, BluetoothGattCharacteristic characteristic) {
|
||||
// DOCUMENTATION https://www.bluetooth.com/wp-content/uploads/Sitecore-Media-Library/Gatt/Xml/Characteristics/org.bluetooth.characteristic.cycling_power_measurement.xml
|
||||
int valueLength = characteristic.getValue().length;
|
||||
if (valueLength == 0) {
|
||||
@@ -154,10 +154,11 @@ public class BluetoothUtils {
|
||||
|
||||
int crankTime = characteristic.getIntValue(BluetoothGattCharacteristic.FORMAT_UINT16, index); // 1/1024s
|
||||
|
||||
cadence = new SensorDataCyclingCadence("", "", crankCount, crankTime);
|
||||
cadence = new SensorDataCyclingCadence(address, sensorName, crankCount, crankTime);
|
||||
}
|
||||
|
||||
return new SensorDataCyclingPower.Data(Power.of(instantaneousPower), cadence);
|
||||
|
||||
return new SensorDataCyclingPower.Data(new SensorDataCyclingPower(sensorName, address, Power.of(instantaneousPower)), cadence);
|
||||
}
|
||||
|
||||
public static SensorDataCyclingCadenceAndDistanceSpeed parseCyclingCrankAndWheel(String address, String sensorName, @NonNull BluetoothGattCharacteristic characteristic) {
|
||||
|
||||
+3
-3
@@ -29,15 +29,15 @@ public class SensorDataCyclingPower extends SensorData<Power> {
|
||||
}
|
||||
|
||||
public static class Data {
|
||||
private final Power power;
|
||||
private final SensorDataCyclingPower power;
|
||||
private final SensorDataCyclingCadence cadence;
|
||||
|
||||
public Data(Power power, @Nullable SensorDataCyclingCadence cadence) {
|
||||
public Data(SensorDataCyclingPower power, @Nullable SensorDataCyclingCadence cadence) {
|
||||
this.power = power;
|
||||
this.cadence = cadence;
|
||||
}
|
||||
|
||||
public Power getPower() {
|
||||
public SensorDataCyclingPower getPower() {
|
||||
return power;
|
||||
}
|
||||
|
||||
|
||||
+32
@@ -0,0 +1,32 @@
|
||||
package de.dennisguse.opentracks.settings.bluetooth;
|
||||
|
||||
import android.content.Context;
|
||||
import android.util.AttributeSet;
|
||||
|
||||
import androidx.preference.PreferenceDialogFragmentCompat;
|
||||
|
||||
import de.dennisguse.opentracks.sensors.BluetoothUtils;
|
||||
|
||||
public class BluetoothLeCyclingCadencePreference extends BluetoothLeSensorPreference {
|
||||
|
||||
public BluetoothLeCyclingCadencePreference(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
|
||||
super(context, attrs, defStyleAttr, defStyleRes);
|
||||
}
|
||||
|
||||
public BluetoothLeCyclingCadencePreference(Context context, AttributeSet attrs, int defStyleAttr) {
|
||||
super(context, attrs, defStyleAttr);
|
||||
}
|
||||
|
||||
public BluetoothLeCyclingCadencePreference(Context context, AttributeSet attrs) {
|
||||
super(context, attrs);
|
||||
}
|
||||
|
||||
public BluetoothLeCyclingCadencePreference(Context context) {
|
||||
super(context);
|
||||
}
|
||||
|
||||
@Override
|
||||
public PreferenceDialogFragmentCompat createInstance() {
|
||||
return BluetoothLeSensorPreferenceDialog.newInstance(getKey(), BluetoothUtils.CYCLING_CADENCE);
|
||||
}
|
||||
}
|
||||
+5
-5
@@ -7,21 +7,21 @@ import androidx.preference.PreferenceDialogFragmentCompat;
|
||||
|
||||
import de.dennisguse.opentracks.sensors.BluetoothUtils;
|
||||
|
||||
public class BluetoothLeCyclingCadenceAndSpeedPreference extends BluetoothLeSensorPreference {
|
||||
public class BluetoothLeCyclingSpeedPreference extends BluetoothLeSensorPreference {
|
||||
|
||||
public BluetoothLeCyclingCadenceAndSpeedPreference(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
|
||||
public BluetoothLeCyclingSpeedPreference(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
|
||||
super(context, attrs, defStyleAttr, defStyleRes);
|
||||
}
|
||||
|
||||
public BluetoothLeCyclingCadenceAndSpeedPreference(Context context, AttributeSet attrs, int defStyleAttr) {
|
||||
public BluetoothLeCyclingSpeedPreference(Context context, AttributeSet attrs, int defStyleAttr) {
|
||||
super(context, attrs, defStyleAttr);
|
||||
}
|
||||
|
||||
public BluetoothLeCyclingCadenceAndSpeedPreference(Context context, AttributeSet attrs) {
|
||||
public BluetoothLeCyclingSpeedPreference(Context context, AttributeSet attrs) {
|
||||
super(context, attrs);
|
||||
}
|
||||
|
||||
public BluetoothLeCyclingCadenceAndSpeedPreference(Context context) {
|
||||
public BluetoothLeCyclingSpeedPreference(Context context) {
|
||||
super(context);
|
||||
}
|
||||
|
||||
@@ -12,7 +12,7 @@
|
||||
</PreferenceCategory>
|
||||
|
||||
<PreferenceCategory android:title="@string/settings_cycling_sensor">
|
||||
<de.dennisguse.opentracks.settings.bluetooth.BluetoothLeCyclingCadenceAndSpeedPreference
|
||||
<de.dennisguse.opentracks.settings.bluetooth.BluetoothLeCyclingCadencePreference
|
||||
android:defaultValue="@string/sensor_type_value_none"
|
||||
android:key="@string/settings_sensor_bluetooth_cycling_cadence_key"
|
||||
android:title="@string/settings_sensor_cycling_cadence" />
|
||||
@@ -20,7 +20,7 @@
|
||||
android:defaultValue="@string/sensor_type_value_none"
|
||||
android:key="@string/settings_sensor_bluetooth_cycling_power_key"
|
||||
android:title="@string/sensor_state_power" />
|
||||
<de.dennisguse.opentracks.settings.bluetooth.BluetoothLeCyclingCadenceAndSpeedPreference
|
||||
<de.dennisguse.opentracks.settings.bluetooth.BluetoothLeCyclingSpeedPreference
|
||||
android:defaultValue="@string/sensor_type_value_none"
|
||||
android:key="@string/settings_sensor_bluetooth_cycling_speed_key"
|
||||
android:title="@string/settings_sensor_cycling_distance_speed" />
|
||||
|
||||
Reference in New Issue
Block a user