From 989fe398490477f12aa1cc7aa24d5bc638d01b67 Mon Sep 17 00:00:00 2001 From: Dennis Guse Date: Sun, 4 Sep 2022 11:31:42 +0200 Subject: [PATCH 1/8] Cleanup: BLE preferences. Part of #1352. --- .../settings/SensorsSettingsFragment.java | 19 ++----------------- ...othLeCyclingCadenceAndSpeedPreference.java | 9 +++++++++ .../BluetoothLeCyclingPowerPreference.java | 9 +++++++++ .../BluetoothLeHeartRatePreference.java | 9 +++++++++ ...othLeRunningSpeedAndCadencePreference.java | 9 +++++++++ .../BluetoothLeSensorPreference.java | 2 ++ 6 files changed, 40 insertions(+), 17 deletions(-) diff --git a/src/main/java/de/dennisguse/opentracks/settings/SensorsSettingsFragment.java b/src/main/java/de/dennisguse/opentracks/settings/SensorsSettingsFragment.java index af6cb580e..6650fa3e1 100644 --- a/src/main/java/de/dennisguse/opentracks/settings/SensorsSettingsFragment.java +++ b/src/main/java/de/dennisguse/opentracks/settings/SensorsSettingsFragment.java @@ -9,11 +9,6 @@ import androidx.preference.Preference; import androidx.preference.PreferenceFragmentCompat; import de.dennisguse.opentracks.R; -import de.dennisguse.opentracks.sensors.BluetoothUtils; -import de.dennisguse.opentracks.settings.bluetooth.BluetoothLeCyclingCadenceAndSpeedPreference; -import de.dennisguse.opentracks.settings.bluetooth.BluetoothLeCyclingPowerPreference; -import de.dennisguse.opentracks.settings.bluetooth.BluetoothLeHeartRatePreference; -import de.dennisguse.opentracks.settings.bluetooth.BluetoothLeRunningSpeedAndCadencePreference; import de.dennisguse.opentracks.settings.bluetooth.BluetoothLeSensorPreference; public class SensorsSettingsFragment extends PreferenceFragmentCompat { @@ -34,18 +29,8 @@ public class SensorsSettingsFragment extends PreferenceFragmentCompat { @Override public void onDisplayPreferenceDialog(Preference preference) { - DialogFragment dialogFragment = null; - if (preference instanceof BluetoothLeHeartRatePreference) { - dialogFragment = BluetoothLeSensorPreference.BluetoothLeSensorPreferenceDialog.newInstance(preference.getKey(), BluetoothUtils.HEART_RATE_SUPPORTING_DEVICES); - } else if (preference instanceof BluetoothLeCyclingCadenceAndSpeedPreference) { - dialogFragment = BluetoothLeSensorPreference.BluetoothLeSensorPreferenceDialog.newInstance(preference.getKey(), BluetoothUtils.CYCLING_SPEED_CADENCE_SERVICE_UUID); - } else if (preference instanceof BluetoothLeCyclingPowerPreference) { - dialogFragment = BluetoothLeSensorPreference.BluetoothLeSensorPreferenceDialog.newInstance(preference.getKey(), BluetoothUtils.CYCLING_POWER_UUID); - } else if (preference instanceof BluetoothLeRunningSpeedAndCadencePreference) { - dialogFragment = BluetoothLeSensorPreference.BluetoothLeSensorPreferenceDialog.newInstance(preference.getKey(), BluetoothUtils.RUNNING_RUNNING_SPEED_CADENCE_UUID); - } - - if (dialogFragment != null) { + if (preference instanceof BluetoothLeSensorPreference) { + DialogFragment dialogFragment = ((BluetoothLeSensorPreference) preference).createInstance(); dialogFragment.setTargetFragment(this, 0); dialogFragment.show(getParentFragmentManager(), getClass().getSimpleName()); return; diff --git a/src/main/java/de/dennisguse/opentracks/settings/bluetooth/BluetoothLeCyclingCadenceAndSpeedPreference.java b/src/main/java/de/dennisguse/opentracks/settings/bluetooth/BluetoothLeCyclingCadenceAndSpeedPreference.java index aba91ba79..6a6369ff3 100644 --- a/src/main/java/de/dennisguse/opentracks/settings/bluetooth/BluetoothLeCyclingCadenceAndSpeedPreference.java +++ b/src/main/java/de/dennisguse/opentracks/settings/bluetooth/BluetoothLeCyclingCadenceAndSpeedPreference.java @@ -3,6 +3,10 @@ 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 BluetoothLeCyclingCadenceAndSpeedPreference extends BluetoothLeSensorPreference { public BluetoothLeCyclingCadenceAndSpeedPreference(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) { @@ -20,4 +24,9 @@ public class BluetoothLeCyclingCadenceAndSpeedPreference extends BluetoothLeSens public BluetoothLeCyclingCadenceAndSpeedPreference(Context context) { super(context); } + + @Override + public PreferenceDialogFragmentCompat createInstance() { + return BluetoothLeSensorPreference.BluetoothLeSensorPreferenceDialog.newInstance(getKey(), BluetoothUtils.CYCLING_SPEED_CADENCE_SERVICE_UUID); + } } diff --git a/src/main/java/de/dennisguse/opentracks/settings/bluetooth/BluetoothLeCyclingPowerPreference.java b/src/main/java/de/dennisguse/opentracks/settings/bluetooth/BluetoothLeCyclingPowerPreference.java index 486136bea..eafd65608 100644 --- a/src/main/java/de/dennisguse/opentracks/settings/bluetooth/BluetoothLeCyclingPowerPreference.java +++ b/src/main/java/de/dennisguse/opentracks/settings/bluetooth/BluetoothLeCyclingPowerPreference.java @@ -3,6 +3,10 @@ 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 BluetoothLeCyclingPowerPreference extends BluetoothLeSensorPreference { public BluetoothLeCyclingPowerPreference(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) { @@ -20,4 +24,9 @@ public class BluetoothLeCyclingPowerPreference extends BluetoothLeSensorPreferen public BluetoothLeCyclingPowerPreference(Context context) { super(context); } + + @Override + public PreferenceDialogFragmentCompat createInstance() { + return BluetoothLeSensorPreference.BluetoothLeSensorPreferenceDialog.newInstance(getKey(), BluetoothUtils.CYCLING_POWER_UUID); + } } \ No newline at end of file diff --git a/src/main/java/de/dennisguse/opentracks/settings/bluetooth/BluetoothLeHeartRatePreference.java b/src/main/java/de/dennisguse/opentracks/settings/bluetooth/BluetoothLeHeartRatePreference.java index 8021228a9..db6df1979 100644 --- a/src/main/java/de/dennisguse/opentracks/settings/bluetooth/BluetoothLeHeartRatePreference.java +++ b/src/main/java/de/dennisguse/opentracks/settings/bluetooth/BluetoothLeHeartRatePreference.java @@ -3,6 +3,10 @@ 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 BluetoothLeHeartRatePreference extends BluetoothLeSensorPreference { public BluetoothLeHeartRatePreference(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) { @@ -20,4 +24,9 @@ public class BluetoothLeHeartRatePreference extends BluetoothLeSensorPreference public BluetoothLeHeartRatePreference(Context context) { super(context); } + + @Override + public PreferenceDialogFragmentCompat createInstance() { + return BluetoothLeSensorPreference.BluetoothLeSensorPreferenceDialog.newInstance(getKey(), BluetoothUtils.HEART_RATE_SUPPORTING_DEVICES); + } } \ No newline at end of file diff --git a/src/main/java/de/dennisguse/opentracks/settings/bluetooth/BluetoothLeRunningSpeedAndCadencePreference.java b/src/main/java/de/dennisguse/opentracks/settings/bluetooth/BluetoothLeRunningSpeedAndCadencePreference.java index 055fcb29f..a7653a400 100644 --- a/src/main/java/de/dennisguse/opentracks/settings/bluetooth/BluetoothLeRunningSpeedAndCadencePreference.java +++ b/src/main/java/de/dennisguse/opentracks/settings/bluetooth/BluetoothLeRunningSpeedAndCadencePreference.java @@ -3,6 +3,10 @@ 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 BluetoothLeRunningSpeedAndCadencePreference extends BluetoothLeSensorPreference { public BluetoothLeRunningSpeedAndCadencePreference(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) { @@ -20,4 +24,9 @@ public class BluetoothLeRunningSpeedAndCadencePreference extends BluetoothLeSens public BluetoothLeRunningSpeedAndCadencePreference(Context context) { super(context); } + + @Override + public PreferenceDialogFragmentCompat createInstance() { + return BluetoothLeSensorPreference.BluetoothLeSensorPreferenceDialog.newInstance(getKey(), BluetoothUtils.RUNNING_RUNNING_SPEED_CADENCE_UUID); + } } diff --git a/src/main/java/de/dennisguse/opentracks/settings/bluetooth/BluetoothLeSensorPreference.java b/src/main/java/de/dennisguse/opentracks/settings/bluetooth/BluetoothLeSensorPreference.java index c3d759eb1..50d75ca9a 100644 --- a/src/main/java/de/dennisguse/opentracks/settings/bluetooth/BluetoothLeSensorPreference.java +++ b/src/main/java/de/dennisguse/opentracks/settings/bluetooth/BluetoothLeSensorPreference.java @@ -92,6 +92,8 @@ public abstract class BluetoothLeSensorPreference extends DialogPreference { return getValue(); } + public abstract PreferenceDialogFragmentCompat createInstance(); + public static class BluetoothLeSensorPreferenceDialog extends PreferenceDialogFragmentCompat { private AnimatedVectorDrawableCompat bluetoothIcon; From 8696a15ce7ec0849bf8853edb6ca6fbd9b54bd13 Mon Sep 17 00:00:00 2001 From: Dennis Guse Date: Sun, 4 Sep 2022 11:54:10 +0200 Subject: [PATCH 2/8] Cleanup: introduced ServiceMeasurementUUID. Part of #1352. --- .../sensors/BluetoothUtilsTest.java | 14 +++--- .../sensors/BluetoothConnectionManager.java | 28 +++++------ .../opentracks/sensors/BluetoothUtils.java | 35 +++++++++----- .../sensors/ServiceMeasurementUUID.java | 47 +++++++++++++++++++ ...othLeCyclingCadenceAndSpeedPreference.java | 2 +- .../BluetoothLeCyclingPowerPreference.java | 2 +- ...othLeRunningSpeedAndCadencePreference.java | 2 +- .../BluetoothLeSensorPreference.java | 11 +++-- 8 files changed, 99 insertions(+), 42 deletions(-) create mode 100644 src/main/java/de/dennisguse/opentracks/sensors/ServiceMeasurementUUID.java diff --git a/src/androidTest/java/de/dennisguse/opentracks/sensors/BluetoothUtilsTest.java b/src/androidTest/java/de/dennisguse/opentracks/sensors/BluetoothUtilsTest.java index 01a5a8828..d225e9036 100644 --- a/src/androidTest/java/de/dennisguse/opentracks/sensors/BluetoothUtilsTest.java +++ b/src/androidTest/java/de/dennisguse/opentracks/sensors/BluetoothUtilsTest.java @@ -18,7 +18,7 @@ public class BluetoothUtilsTest { @Test public void parseHeartRate_uint8() { // given - BluetoothGattCharacteristic characteristic = new BluetoothGattCharacteristic(BluetoothUtils.HEART_RATE_SERVICE_UUID, 0, 0); + BluetoothGattCharacteristic characteristic = new BluetoothGattCharacteristic(BluetoothUtils.HEARTRATE.getServiceUUID(), 0, 0); characteristic.setValue(new byte[]{0x02, 0x3C}); // when @@ -31,7 +31,7 @@ public class BluetoothUtilsTest { @Test public void parseHeartRate_uint16() { // given - BluetoothGattCharacteristic characteristic = new BluetoothGattCharacteristic(BluetoothUtils.HEART_RATE_SERVICE_UUID, 0, 0); + BluetoothGattCharacteristic characteristic = new BluetoothGattCharacteristic(BluetoothUtils.HEARTRATE.getServiceUUID(), 0, 0); characteristic.setValue(new byte[]{0x01, 0x01, 0x01}); // when @@ -43,7 +43,7 @@ public class BluetoothUtilsTest { @Test public void parseCyclingSpeedCadence_crankOnly() { - BluetoothGattCharacteristic characteristic = new BluetoothGattCharacteristic(BluetoothUtils.CYCLING_SPEED_CADENCE_SERVICE_UUID, 0, 0); + BluetoothGattCharacteristic characteristic = new BluetoothGattCharacteristic(BluetoothUtils.CYCLING_SPEED_CADENCE.getServiceUUID(), 0, 0); characteristic.setValue(new byte[]{0x02, (byte) 0xC8, 0x00, 0x00, 0x00, 0x06, (byte) 0x99}); // when @@ -56,7 +56,7 @@ public class BluetoothUtilsTest { @Test public void parseCyclingSpeedCadence_wheelOnly() { - BluetoothGattCharacteristic characteristic = new BluetoothGattCharacteristic(BluetoothUtils.CYCLING_SPEED_CADENCE_SERVICE_UUID, 0, 0); + BluetoothGattCharacteristic characteristic = new BluetoothGattCharacteristic(BluetoothUtils.CYCLING_SPEED_CADENCE.getServiceUUID(), 0, 0); characteristic.setValue(new byte[]{0x01, (byte) 0xFF, (byte) 0xFF, 0, 1, 0x45, (byte) 0x99}); // when @@ -69,7 +69,7 @@ public class BluetoothUtilsTest { @Test public void parseCyclingSpeedCadence_crankWheel() { - BluetoothGattCharacteristic characteristic = new BluetoothGattCharacteristic(BluetoothUtils.CYCLING_SPEED_CADENCE_SERVICE_UUID, 0, 0); + BluetoothGattCharacteristic characteristic = new BluetoothGattCharacteristic(BluetoothUtils.CYCLING_SPEED_CADENCE.getServiceUUID(), 0, 0); characteristic.setValue(new byte[]{0x03, (byte) 0xC8, 0x00, 0x00, 0x01, 0x06, (byte) 0x99, (byte) 0xE1, 0x00, 0x45, (byte) 0x99}); // when @@ -82,7 +82,7 @@ public class BluetoothUtilsTest { @Test public void parseCyclingPower_power() { - BluetoothGattCharacteristic characteristic = new BluetoothGattCharacteristic(BluetoothUtils.CYCLING_POWER_MEASUREMENT_CHAR_UUID, 0, 0); + BluetoothGattCharacteristic characteristic = new BluetoothGattCharacteristic(BluetoothUtils.CYCLING_POWER.getServiceUUID(), 0, 0); characteristic.setValue(new byte[]{0, 0, 40, 0}); // when @@ -94,7 +94,7 @@ public class BluetoothUtilsTest { @Test public void parseRunningSpeedAndCadence_with_distance() { - BluetoothGattCharacteristic characteristic = new BluetoothGattCharacteristic(BluetoothUtils.RUNNING_RUNNING_SPEED_CADENCE_CHAR_UUID, 0, 0); + BluetoothGattCharacteristic characteristic = new BluetoothGattCharacteristic(BluetoothUtils.RUNNING_SPEED_CADENCE.getServiceUUID(), 0, 0); characteristic.setValue(new byte[]{2, 0, 5, 80, (byte) 0xFF, (byte) 0xFF, 0, 1}); // when diff --git a/src/main/java/de/dennisguse/opentracks/sensors/BluetoothConnectionManager.java b/src/main/java/de/dennisguse/opentracks/sensors/BluetoothConnectionManager.java index e4987c19b..bbefe34ab 100644 --- a/src/main/java/de/dennisguse/opentracks/sensors/BluetoothConnectionManager.java +++ b/src/main/java/de/dennisguse/opentracks/sensors/BluetoothConnectionManager.java @@ -30,8 +30,6 @@ import android.util.Log; import androidx.annotation.NonNull; -import java.util.UUID; - import de.dennisguse.opentracks.data.models.Cadence; import de.dennisguse.opentracks.data.models.HeartRate; import de.dennisguse.opentracks.data.models.Power; @@ -51,8 +49,7 @@ public abstract class BluetoothConnectionManager { private final SensorDataObserver observer; - private final UUID serviceUUUID; - private final UUID measurementUUID; + private final ServiceMeasurementUUID sensor; private BluetoothGatt bluetoothGatt; private final BluetoothGattCallback connectCallback = new BluetoothGattCallback() { @@ -81,15 +78,15 @@ public abstract class BluetoothConnectionManager { @Override public void onServicesDiscovered(@NonNull BluetoothGatt gatt, int status) { - BluetoothGattService service = gatt.getService(serviceUUUID); + BluetoothGattService service = gatt.getService(sensor.getServiceUUID()); if (service == null) { - Log.e(TAG, "Could not get service for address=" + gatt.getDevice().getAddress() + " serviceUUID=" + serviceUUUID); + Log.e(TAG, "Could not get service for address=" + gatt.getDevice().getAddress() + " serviceUUID=" + sensor); return; } - BluetoothGattCharacteristic characteristic = service.getCharacteristic(measurementUUID); + BluetoothGattCharacteristic characteristic = service.getCharacteristic(sensor.getMeasurementUUID()); if (characteristic == null) { - Log.e(TAG, "Could not get BluetoothCharacteristic for address=" + gatt.getDevice().getAddress() + " serviceUUID=" + serviceUUUID + " characteristicUUID=" + measurementUUID); + Log.e(TAG, "Could not get BluetoothCharacteristic for address=" + gatt.getDevice().getAddress() + " serviceUUID=" + sensor + " characteristicUUID=" + sensor.getMeasurementUUID()); return; } gatt.setCharacteristicNotification(characteristic, true); @@ -122,9 +119,8 @@ public abstract class BluetoothConnectionManager { } }; - BluetoothConnectionManager(UUID serviceUUUID, UUID measurementUUID, SensorDataObserver observer) { - this.serviceUUUID = serviceUUUID; - this.measurementUUID = measurementUUID; + BluetoothConnectionManager(ServiceMeasurementUUID serviceUUUID, SensorDataObserver observer) { + this.sensor = serviceUUUID; this.observer = observer; } @@ -177,7 +173,7 @@ public abstract class BluetoothConnectionManager { public static class HeartRateConnectionManager extends BluetoothConnectionManager { HeartRateConnectionManager(@NonNull SensorDataObserver observer) { - super(BluetoothUtils.HEART_RATE_SERVICE_UUID, BluetoothUtils.HEART_RATE_MEASUREMENT_CHAR_UUID, observer); + super(BluetoothUtils.HEARTRATE, observer); } @Override @@ -196,7 +192,7 @@ public abstract class BluetoothConnectionManager { public static class CyclingCadence extends BluetoothConnectionManager { CyclingCadence(SensorDataObserver observer) { - super(BluetoothUtils.CYCLING_SPEED_CADENCE_SERVICE_UUID, BluetoothUtils.CYCLING_SPEED_CADENCE_MEASUREMENT_CHAR_UUID, observer); + super(BluetoothUtils.CYCLING_SPEED_CADENCE, observer); } @Override @@ -222,7 +218,7 @@ public abstract class BluetoothConnectionManager { public static class CyclingDistanceSpeed extends BluetoothConnectionManager { CyclingDistanceSpeed(SensorDataObserver observer) { - super(BluetoothUtils.CYCLING_SPEED_CADENCE_SERVICE_UUID, BluetoothUtils.CYCLING_SPEED_CADENCE_MEASUREMENT_CHAR_UUID, observer); + super(BluetoothUtils.CYCLING_SPEED_CADENCE, observer); } @Override @@ -248,7 +244,7 @@ public abstract class BluetoothConnectionManager { public static class CyclingPower extends BluetoothConnectionManager { CyclingPower(@NonNull SensorDataObserver observer) { - super(BluetoothUtils.CYCLING_POWER_UUID, BluetoothUtils.CYCLING_POWER_MEASUREMENT_CHAR_UUID, observer); + super(BluetoothUtils.CYCLING_POWER, observer); } @Override @@ -267,7 +263,7 @@ public abstract class BluetoothConnectionManager { public static class RunningSpeedAndCadence extends BluetoothConnectionManager { RunningSpeedAndCadence(@NonNull SensorDataObserver observer) { - super(BluetoothUtils.RUNNING_RUNNING_SPEED_CADENCE_UUID, BluetoothUtils.RUNNING_RUNNING_SPEED_CADENCE_CHAR_UUID, observer); + super(BluetoothUtils.RUNNING_SPEED_CADENCE, observer); } @Override diff --git a/src/main/java/de/dennisguse/opentracks/sensors/BluetoothUtils.java b/src/main/java/de/dennisguse/opentracks/sensors/BluetoothUtils.java index df4c687e1..050974e60 100644 --- a/src/main/java/de/dennisguse/opentracks/sensors/BluetoothUtils.java +++ b/src/main/java/de/dennisguse/opentracks/sensors/BluetoothUtils.java @@ -43,23 +43,34 @@ public class BluetoothUtils { public static final UUID CLIENT_CHARACTERISTIC_CONFIG_UUID = new UUID(0x290200001000L, 0x800000805f9b34fbL); - public static final UUID HEART_RATE_SERVICE_UUID = new UUID(0x180D00001000L, 0x800000805f9b34fbL); - public static final UUID HEART_RATE_MEASUREMENT_CHAR_UUID = new UUID(0x2A3700001000L, 0x800000805f9b34fbL); + public static final ServiceMeasurementUUID HEARTRATE = new ServiceMeasurementUUID( + new UUID(0x180D00001000L, 0x800000805f9b34fbL), + new UUID(0x2A3700001000L, 0x800000805f9b34fbL) + ); - public static final List HEART_RATE_SUPPORTING_DEVICES = Collections.unmodifiableList(Arrays.asList( - BluetoothUtils.HEART_RATE_SERVICE_UUID, + public static final List HEART_RATE_SUPPORTING_DEVICES = Collections.unmodifiableList(Arrays.asList( + HEARTRATE, //Devices that support HEART_RATE_SERVICE_UUID, but do not announce HEART_RATE_SERVICE_UUID in there BLE announcement messages (during device discovery). - UUID.fromString("0000fee0-0000-1000-8000-00805f9b34fb") //Miband3 - )); + new ServiceMeasurementUUID( + UUID.fromString("0000fee0-0000-1000-8000-00805f9b34fb"), //Miband3 + HEARTRATE.getMeasurementUUID() + )) + ); - public static final UUID CYCLING_POWER_UUID = new UUID(0x181800001000L, 0x800000805f9b34fbL); - public static final UUID CYCLING_POWER_MEASUREMENT_CHAR_UUID = new UUID(0x2A6300001000L, 0x800000805f9b34fbL); + public static final ServiceMeasurementUUID CYCLING_POWER = new ServiceMeasurementUUID( + new UUID(0x181800001000L, 0x800000805f9b34fbL), + new UUID(0x2A6300001000L, 0x800000805f9b34fbL) + ); - public static final UUID CYCLING_SPEED_CADENCE_SERVICE_UUID = new UUID(0x181600001000L, 0x800000805f9b34fbL); - public static final UUID CYCLING_SPEED_CADENCE_MEASUREMENT_CHAR_UUID = new UUID(0x2A5B00001000L, 0x800000805f9b34fbL); + public static final ServiceMeasurementUUID CYCLING_SPEED_CADENCE = new ServiceMeasurementUUID( + new UUID(0x181600001000L, 0x800000805f9b34fbL), + new UUID(0x2A5B00001000L, 0x800000805f9b34fbL) + ); - public static final UUID RUNNING_RUNNING_SPEED_CADENCE_UUID = new UUID(0x181400001000L, 0x800000805f9b34fbL); - public static final UUID RUNNING_RUNNING_SPEED_CADENCE_CHAR_UUID = new UUID(0x2A5300001000L, 0x800000805f9b34fbL); + public static final ServiceMeasurementUUID RUNNING_SPEED_CADENCE = new ServiceMeasurementUUID( + new UUID(0x181400001000L, 0x800000805f9b34fbL), + new UUID(0x2A5300001000L, 0x800000805f9b34fbL) + ); private static final String TAG = BluetoothUtils.class.getSimpleName(); diff --git a/src/main/java/de/dennisguse/opentracks/sensors/ServiceMeasurementUUID.java b/src/main/java/de/dennisguse/opentracks/sensors/ServiceMeasurementUUID.java new file mode 100644 index 000000000..cc137014b --- /dev/null +++ b/src/main/java/de/dennisguse/opentracks/sensors/ServiceMeasurementUUID.java @@ -0,0 +1,47 @@ +package de.dennisguse.opentracks.sensors; + +import androidx.annotation.NonNull; + +import java.util.Objects; +import java.util.UUID; + +public class ServiceMeasurementUUID { + + private final UUID serviceUUID; + private final UUID measurementUUID; + + public ServiceMeasurementUUID(UUID serviceUUID, UUID measurementUUID) { + this.serviceUUID = serviceUUID; + this.measurementUUID = measurementUUID; + } + + public UUID getServiceUUID() { + return serviceUUID; + } + + public UUID getMeasurementUUID() { + return measurementUUID; + } + + @NonNull + @Override + public String toString() { + return "ServiceMeasurementUUID{" + + "serviceUUID=" + serviceUUID + + ", measurementUUID=" + measurementUUID + + '}'; + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + ServiceMeasurementUUID that = (ServiceMeasurementUUID) o; + return Objects.equals(serviceUUID, that.serviceUUID) && Objects.equals(measurementUUID, that.measurementUUID); + } + + @Override + public int hashCode() { + return Objects.hash(serviceUUID, measurementUUID); + } +} diff --git a/src/main/java/de/dennisguse/opentracks/settings/bluetooth/BluetoothLeCyclingCadenceAndSpeedPreference.java b/src/main/java/de/dennisguse/opentracks/settings/bluetooth/BluetoothLeCyclingCadenceAndSpeedPreference.java index 6a6369ff3..a4e234ead 100644 --- a/src/main/java/de/dennisguse/opentracks/settings/bluetooth/BluetoothLeCyclingCadenceAndSpeedPreference.java +++ b/src/main/java/de/dennisguse/opentracks/settings/bluetooth/BluetoothLeCyclingCadenceAndSpeedPreference.java @@ -27,6 +27,6 @@ public class BluetoothLeCyclingCadenceAndSpeedPreference extends BluetoothLeSens @Override public PreferenceDialogFragmentCompat createInstance() { - return BluetoothLeSensorPreference.BluetoothLeSensorPreferenceDialog.newInstance(getKey(), BluetoothUtils.CYCLING_SPEED_CADENCE_SERVICE_UUID); + return BluetoothLeSensorPreference.BluetoothLeSensorPreferenceDialog.newInstance(getKey(), BluetoothUtils.CYCLING_SPEED_CADENCE); } } diff --git a/src/main/java/de/dennisguse/opentracks/settings/bluetooth/BluetoothLeCyclingPowerPreference.java b/src/main/java/de/dennisguse/opentracks/settings/bluetooth/BluetoothLeCyclingPowerPreference.java index eafd65608..af020ce07 100644 --- a/src/main/java/de/dennisguse/opentracks/settings/bluetooth/BluetoothLeCyclingPowerPreference.java +++ b/src/main/java/de/dennisguse/opentracks/settings/bluetooth/BluetoothLeCyclingPowerPreference.java @@ -27,6 +27,6 @@ public class BluetoothLeCyclingPowerPreference extends BluetoothLeSensorPreferen @Override public PreferenceDialogFragmentCompat createInstance() { - return BluetoothLeSensorPreference.BluetoothLeSensorPreferenceDialog.newInstance(getKey(), BluetoothUtils.CYCLING_POWER_UUID); + return BluetoothLeSensorPreference.BluetoothLeSensorPreferenceDialog.newInstance(getKey(), BluetoothUtils.CYCLING_POWER); } } \ No newline at end of file diff --git a/src/main/java/de/dennisguse/opentracks/settings/bluetooth/BluetoothLeRunningSpeedAndCadencePreference.java b/src/main/java/de/dennisguse/opentracks/settings/bluetooth/BluetoothLeRunningSpeedAndCadencePreference.java index a7653a400..856c79b92 100644 --- a/src/main/java/de/dennisguse/opentracks/settings/bluetooth/BluetoothLeRunningSpeedAndCadencePreference.java +++ b/src/main/java/de/dennisguse/opentracks/settings/bluetooth/BluetoothLeRunningSpeedAndCadencePreference.java @@ -27,6 +27,6 @@ public class BluetoothLeRunningSpeedAndCadencePreference extends BluetoothLeSens @Override public PreferenceDialogFragmentCompat createInstance() { - return BluetoothLeSensorPreference.BluetoothLeSensorPreferenceDialog.newInstance(getKey(), BluetoothUtils.RUNNING_RUNNING_SPEED_CADENCE_UUID); + return BluetoothLeSensorPreference.BluetoothLeSensorPreferenceDialog.newInstance(getKey(), BluetoothUtils.RUNNING_SPEED_CADENCE); } } diff --git a/src/main/java/de/dennisguse/opentracks/settings/bluetooth/BluetoothLeSensorPreference.java b/src/main/java/de/dennisguse/opentracks/settings/bluetooth/BluetoothLeSensorPreference.java index 50d75ca9a..b894cd760 100644 --- a/src/main/java/de/dennisguse/opentracks/settings/bluetooth/BluetoothLeSensorPreference.java +++ b/src/main/java/de/dennisguse/opentracks/settings/bluetooth/BluetoothLeSensorPreference.java @@ -23,11 +23,11 @@ import androidx.vectordrawable.graphics.drawable.AnimatedVectorDrawableCompat; import java.util.ArrayList; import java.util.Collections; import java.util.List; -import java.util.UUID; import java.util.stream.Collectors; import de.dennisguse.opentracks.R; import de.dennisguse.opentracks.sensors.BluetoothUtils; +import de.dennisguse.opentracks.sensors.ServiceMeasurementUUID; import de.dennisguse.opentracks.settings.PreferencesUtils; import de.dennisguse.opentracks.util.PermissionRequester; @@ -125,15 +125,18 @@ public abstract class BluetoothLeSensorPreference extends DialogPreference { } }; - public static BluetoothLeSensorPreferenceDialog newInstance(String preferenceKey, UUID sensorUUID) { + public static BluetoothLeSensorPreferenceDialog newInstance(String preferenceKey, ServiceMeasurementUUID sensorUUID) { return newInstance(preferenceKey, Collections.singletonList(sensorUUID)); } - public static BluetoothLeSensorPreferenceDialog newInstance(String preferenceKey, List sensorUUIDs) { + public static BluetoothLeSensorPreferenceDialog newInstance(String preferenceKey, List sensorUUIDs) { final BluetoothLeSensorPreferenceDialog fragment = new BluetoothLeSensorPreferenceDialog(); final Bundle b = new Bundle(1); b.putString(ARG_KEY, preferenceKey); - b.putParcelableArrayList(ARG_BLE_SERVICE_UUIDS, new ArrayList<>(sensorUUIDs.stream().map(ParcelUuid::new).collect(Collectors.toList()))); + b.putParcelableArrayList(ARG_BLE_SERVICE_UUIDS, new ArrayList<>(sensorUUIDs.stream() + .map(ServiceMeasurementUUID::getServiceUUID) + .map(ParcelUuid::new) + .collect(Collectors.toList()))); fragment.setArguments(b); return fragment; } From 7d804b603d0c07dcafd6d7ce1330614fbbaff7f3 Mon Sep 17 00:00:00 2001 From: Dennis Guse Date: Tue, 16 Aug 2022 20:33:54 +0200 Subject: [PATCH 3/8] BLE: decode cadence from cycling powermeter. Part of #1352. --- .../sensors/BluetoothUtilsTest.java | 20 +++++++++- .../sensors/BluetoothConnectionManager.java | 4 +- .../opentracks/sensors/BluetoothUtils.java | 39 +++++++++++++++++-- .../sensors/sensorData/SensorDataCycling.java | 7 ---- .../sensorData/SensorDataCyclingPower.java | 36 +++++++++++++++-- 5 files changed, 88 insertions(+), 18 deletions(-) diff --git a/src/androidTest/java/de/dennisguse/opentracks/sensors/BluetoothUtilsTest.java b/src/androidTest/java/de/dennisguse/opentracks/sensors/BluetoothUtilsTest.java index d225e9036..682af3880 100644 --- a/src/androidTest/java/de/dennisguse/opentracks/sensors/BluetoothUtilsTest.java +++ b/src/androidTest/java/de/dennisguse/opentracks/sensors/BluetoothUtilsTest.java @@ -11,6 +11,7 @@ import de.dennisguse.opentracks.data.models.Cadence; import de.dennisguse.opentracks.data.models.Distance; import de.dennisguse.opentracks.data.models.Speed; import de.dennisguse.opentracks.sensors.sensorData.SensorDataCycling; +import de.dennisguse.opentracks.sensors.sensorData.SensorDataCyclingPower; import de.dennisguse.opentracks.sensors.sensorData.SensorDataRunning; public class BluetoothUtilsTest { @@ -86,10 +87,25 @@ public class BluetoothUtilsTest { characteristic.setValue(new byte[]{0, 0, 40, 0}); // when - int power_w = BluetoothUtils.parseCyclingPower(characteristic); + SensorDataCyclingPower.Data powerCadence = BluetoothUtils.parseCyclingPower(characteristic); // then - assertEquals(40, power_w); + assertEquals(40, powerCadence.getPower().getW(), 0.01); + } + + @Test + public void parseCyclingPower_power_with_cadence() { + BluetoothGattCharacteristic characteristic = new BluetoothGattCharacteristic(BluetoothUtils.CYCLING_POWER.getServiceUUID(), 0, 0); + characteristic.setValue(new byte[]{0x2C, 0x00, 0x00, 0x00, (byte) 0x9F, 0x00, 0x0C, 0x00, (byte) 0xE5, 0x42}); + + // when + SensorDataCyclingPower.Data powerCadence = BluetoothUtils.parseCyclingPower(characteristic); + + // then + assertEquals(0, powerCadence.getPower().getW(), 0.01); + + assertEquals(12, powerCadence.getCadence().getCrankRevolutionsCount()); + assertEquals(17125, powerCadence.getCadence().getCrankRevolutionsTime()); } @Test diff --git a/src/main/java/de/dennisguse/opentracks/sensors/BluetoothConnectionManager.java b/src/main/java/de/dennisguse/opentracks/sensors/BluetoothConnectionManager.java index bbefe34ab..de245d601 100644 --- a/src/main/java/de/dennisguse/opentracks/sensors/BluetoothConnectionManager.java +++ b/src/main/java/de/dennisguse/opentracks/sensors/BluetoothConnectionManager.java @@ -254,9 +254,9 @@ public abstract class BluetoothConnectionManager { @Override protected SensorDataCyclingPower parsePayload(String sensorName, String address, BluetoothGattCharacteristic characteristic) { - Integer cyclingPower = BluetoothUtils.parseCyclingPower(characteristic); + SensorDataCyclingPower.Data cyclingPower = BluetoothUtils.parseCyclingPower(characteristic); - return cyclingPower != null ? new SensorDataCyclingPower(address, sensorName, Power.of(cyclingPower)) : null; + return cyclingPower != null ? new SensorDataCyclingPower(address, sensorName, cyclingPower.getPower()) : null; } } diff --git a/src/main/java/de/dennisguse/opentracks/sensors/BluetoothUtils.java b/src/main/java/de/dennisguse/opentracks/sensors/BluetoothUtils.java index 050974e60..722aafd13 100644 --- a/src/main/java/de/dennisguse/opentracks/sensors/BluetoothUtils.java +++ b/src/main/java/de/dennisguse/opentracks/sensors/BluetoothUtils.java @@ -30,8 +30,10 @@ import java.util.UUID; import de.dennisguse.opentracks.data.models.Cadence; import de.dennisguse.opentracks.data.models.Distance; +import de.dennisguse.opentracks.data.models.Power; import de.dennisguse.opentracks.data.models.Speed; import de.dennisguse.opentracks.sensors.sensorData.SensorDataCycling; +import de.dennisguse.opentracks.sensors.sensorData.SensorDataCyclingPower; import de.dennisguse.opentracks.sensors.sensorData.SensorDataRunning; /** @@ -109,14 +111,45 @@ public class BluetoothUtils { return null; } - public static Integer parseCyclingPower(BluetoothGattCharacteristic characteristic) { + public static SensorDataCyclingPower.Data parseCyclingPower(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 < 4) { + if (valueLength == 0) { return null; } - return characteristic.getIntValue(BluetoothGattCharacteristic.FORMAT_SINT16, 2); + int index = 0; + int flags1 = characteristic.getIntValue(BluetoothGattCharacteristic.FORMAT_UINT8, index++); + int flags2 = characteristic.getIntValue(BluetoothGattCharacteristic.FORMAT_UINT8, index++); + boolean hasPedalPowerBalance = (flags1 & 0x01) > 0; + boolean hasAccumulatedTorque = (flags1 & 0x04) > 0; + boolean hasWheel = (flags1 & 16) > 0; + boolean hasCrank = (flags1 & 32) > 0; + + Integer instantaneousPower = characteristic.getIntValue(BluetoothGattCharacteristic.FORMAT_SINT16, index); + index += 2; + + if (hasPedalPowerBalance) { + index += 1; + } + if (hasAccumulatedTorque) { + index += 2; + } + if (hasWheel) { + index += 2 + 2; + } + + SensorDataCycling.CyclingCadence cadence = null; + if (hasCrank && valueLength - index >= 4) { + long crankCount = characteristic.getIntValue(BluetoothGattCharacteristic.FORMAT_UINT16, index); + index += 2; + + int crankTime = characteristic.getIntValue(BluetoothGattCharacteristic.FORMAT_UINT16, index); // 1/1024s + + cadence = new SensorDataCycling.CyclingCadence("", "", crankCount, crankTime); + } + + return new SensorDataCyclingPower.Data(Power.of(instantaneousPower), cadence); } public static SensorDataCycling.CadenceAndSpeed parseCyclingCrankAndWheel(String address, String sensorName, @NonNull BluetoothGattCharacteristic characteristic) { diff --git a/src/main/java/de/dennisguse/opentracks/sensors/sensorData/SensorDataCycling.java b/src/main/java/de/dennisguse/opentracks/sensors/sensorData/SensorDataCycling.java index 1d3ad400b..7827127ca 100644 --- a/src/main/java/de/dennisguse/opentracks/sensors/sensorData/SensorDataCycling.java +++ b/src/main/java/de/dennisguse/opentracks/sensors/sensorData/SensorDataCycling.java @@ -40,13 +40,6 @@ public final class SensorDataCycling { this.crankRevolutionsTime = crankRevolutionsTime; } - /** - * Workaround for Wahoo CADENCE: provides speed instead of cadence - */ - public CyclingCadence(@NonNull DistanceSpeed speed) { - this(speed.getSensorAddress(), speed.getSensorName(), speed.wheelRevolutionsCount, speed.wheelRevolutionsTime); - } - public boolean hasData() { return crankRevolutionsCount != null && crankRevolutionsTime != null; } diff --git a/src/main/java/de/dennisguse/opentracks/sensors/sensorData/SensorDataCyclingPower.java b/src/main/java/de/dennisguse/opentracks/sensors/sensorData/SensorDataCyclingPower.java index dae106132..5c962b3a5 100644 --- a/src/main/java/de/dennisguse/opentracks/sensors/sensorData/SensorDataCyclingPower.java +++ b/src/main/java/de/dennisguse/opentracks/sensors/sensorData/SensorDataCyclingPower.java @@ -1,6 +1,7 @@ package de.dennisguse.opentracks.sensors.sensorData; import androidx.annotation.NonNull; +import androidx.annotation.Nullable; import de.dennisguse.opentracks.data.models.Power; @@ -17,13 +18,40 @@ public class SensorDataCyclingPower extends SensorData { @NonNull @Override - public String toString() { - return super.toString() + " power=" + value; + protected Power getNoneValue() { + return Power.of(0f); } @NonNull @Override - protected Power getNoneValue() { - return Power.of(0f); + public String toString() { + return super.toString() + " data=" + value; + } + + public static class Data { + private final Power power; + private final SensorDataCycling.CyclingCadence cadence; + + public Data(Power power, @Nullable SensorDataCycling.CyclingCadence cadence) { + this.power = power; + this.cadence = cadence; + } + + public Power getPower() { + return power; + } + + public SensorDataCycling.CyclingCadence getCadence() { + return cadence; + } + + @NonNull + @Override + public String toString() { + return "Data{" + + "power=" + power + + ", cadence=" + cadence + + '}'; + } } } From b6b81b5d3dde94b8b5ff4c000fd9a1f5dd2731ab Mon Sep 17 00:00:00 2001 From: Dennis Guse Date: Sat, 20 Aug 2022 09:48:33 +0200 Subject: [PATCH 4/8] BLE: extract sensor managers into separate files. Part of #1352. --- .../sensors/BluetoothConnectionManager.java | 93 ------------------- .../sensors/BluetoothRemoteSensorManager.java | 10 +- ...oothRemoteSensorManagerCyclingCadence.java | 32 +++++++ ...moteSensorManagerCyclingDistanceSpeed.java | 31 +++++++ ...etoothRemoteSensorManagerCyclingPower.java | 27 ++++++ ...BluetoothRemoteSensorManagerHeartRate.java | 27 ++++++ ...teSensorManagerRunningSpeedAndCadence.java | 24 +++++ 7 files changed, 146 insertions(+), 98 deletions(-) create mode 100644 src/main/java/de/dennisguse/opentracks/sensors/BluetoothRemoteSensorManagerCyclingCadence.java create mode 100644 src/main/java/de/dennisguse/opentracks/sensors/BluetoothRemoteSensorManagerCyclingDistanceSpeed.java create mode 100644 src/main/java/de/dennisguse/opentracks/sensors/BluetoothRemoteSensorManagerCyclingPower.java create mode 100644 src/main/java/de/dennisguse/opentracks/sensors/BluetoothRemoteSensorManagerHeartRate.java create mode 100644 src/main/java/de/dennisguse/opentracks/sensors/BluetoothRemoteSensorManagerRunningSpeedAndCadence.java diff --git a/src/main/java/de/dennisguse/opentracks/sensors/BluetoothConnectionManager.java b/src/main/java/de/dennisguse/opentracks/sensors/BluetoothConnectionManager.java index de245d601..61ffd7fae 100644 --- a/src/main/java/de/dennisguse/opentracks/sensors/BluetoothConnectionManager.java +++ b/src/main/java/de/dennisguse/opentracks/sensors/BluetoothConnectionManager.java @@ -30,14 +30,9 @@ import android.util.Log; import androidx.annotation.NonNull; -import de.dennisguse.opentracks.data.models.Cadence; import de.dennisguse.opentracks.data.models.HeartRate; -import de.dennisguse.opentracks.data.models.Power; import de.dennisguse.opentracks.sensors.sensorData.SensorData; -import de.dennisguse.opentracks.sensors.sensorData.SensorDataCycling; -import de.dennisguse.opentracks.sensors.sensorData.SensorDataCyclingPower; import de.dennisguse.opentracks.sensors.sensorData.SensorDataHeartRate; -import de.dennisguse.opentracks.sensors.sensorData.SensorDataRunning; /** * Manages connection to a Bluetooth LE sensor and subscribes for onChange-notifications. @@ -189,94 +184,6 @@ public abstract class BluetoothConnectionManager { } } - public static class CyclingCadence extends BluetoothConnectionManager { - - CyclingCadence(SensorDataObserver observer) { - super(BluetoothUtils.CYCLING_SPEED_CADENCE, observer); - } - - @Override - protected SensorDataCycling.CyclingCadence createEmptySensorData(String address) { - return new SensorDataCycling.CyclingCadence(address); - } - - @Override - protected SensorDataCycling.CyclingCadence parsePayload(String sensorName, String address, BluetoothGattCharacteristic characteristic) { - SensorDataCycling.CadenceAndSpeed cadenceAndSpeed = BluetoothUtils.parseCyclingCrankAndWheel(address, sensorName, characteristic); - if (cadenceAndSpeed == null) { - return null; - } - - if (cadenceAndSpeed.getCadence() != null) { - return cadenceAndSpeed.getCadence(); - } - - return null; - } - } - - public static class CyclingDistanceSpeed extends BluetoothConnectionManager { - - CyclingDistanceSpeed(SensorDataObserver observer) { - super(BluetoothUtils.CYCLING_SPEED_CADENCE, observer); - } - - @Override - protected SensorDataCycling.DistanceSpeed createEmptySensorData(String address) { - return new SensorDataCycling.DistanceSpeed(address); - } - - @Override - protected SensorDataCycling.DistanceSpeed parsePayload(String sensorName, String address, BluetoothGattCharacteristic characteristic) { - SensorDataCycling.CadenceAndSpeed cadenceAndSpeed = BluetoothUtils.parseCyclingCrankAndWheel(address, sensorName, characteristic); - if (cadenceAndSpeed == null) { - return null; - } - - if (cadenceAndSpeed.getDistanceSpeed() != null) { - return cadenceAndSpeed.getDistanceSpeed(); - } - - return null; - } - } - - public static class CyclingPower extends BluetoothConnectionManager { - - CyclingPower(@NonNull SensorDataObserver observer) { - super(BluetoothUtils.CYCLING_POWER, observer); - } - - @Override - protected SensorDataCyclingPower createEmptySensorData(String address) { - return new SensorDataCyclingPower(address); - } - - @Override - protected SensorDataCyclingPower parsePayload(String sensorName, String address, BluetoothGattCharacteristic characteristic) { - SensorDataCyclingPower.Data cyclingPower = BluetoothUtils.parseCyclingPower(characteristic); - - return cyclingPower != null ? new SensorDataCyclingPower(address, sensorName, cyclingPower.getPower()) : null; - } - } - - public static class RunningSpeedAndCadence extends BluetoothConnectionManager { - - RunningSpeedAndCadence(@NonNull SensorDataObserver observer) { - super(BluetoothUtils.RUNNING_SPEED_CADENCE, observer); - } - - @Override - protected SensorDataRunning createEmptySensorData(String address) { - return new SensorDataRunning(address); - } - - @Override - protected SensorDataRunning parsePayload(String sensorName, String address, BluetoothGattCharacteristic characteristic) { - return BluetoothUtils.parseRunningSpeedAndCadence(address, sensorName, characteristic); - } - } - interface SensorDataObserver { void onChanged(SensorData sensorData); diff --git a/src/main/java/de/dennisguse/opentracks/sensors/BluetoothRemoteSensorManager.java b/src/main/java/de/dennisguse/opentracks/sensors/BluetoothRemoteSensorManager.java index a5c5aa388..567d9d26b 100644 --- a/src/main/java/de/dennisguse/opentracks/sensors/BluetoothRemoteSensorManager.java +++ b/src/main/java/de/dennisguse/opentracks/sensors/BluetoothRemoteSensorManager.java @@ -64,11 +64,11 @@ public class BluetoothRemoteSensorManager implements BluetoothConnectionManager. private Distance preferenceWheelCircumference; - private final BluetoothConnectionManager.HeartRateConnectionManager heartRate = new BluetoothConnectionManager.HeartRateConnectionManager(this); - private final BluetoothConnectionManager.CyclingCadence cyclingCadence = new BluetoothConnectionManager.CyclingCadence(this); - private final BluetoothConnectionManager.CyclingDistanceSpeed cyclingSpeed = new BluetoothConnectionManager.CyclingDistanceSpeed(this); - private final BluetoothConnectionManager.CyclingPower cyclingPower = new BluetoothConnectionManager.CyclingPower(this); - private final BluetoothConnectionManager.RunningSpeedAndCadence runningSpeedAndCadence = new BluetoothConnectionManager.RunningSpeedAndCadence(this); + private final BluetoothRemoteSensorManagerHeartRate heartRate = new BluetoothRemoteSensorManagerHeartRate(this); + private final BluetoothRemoteSensorManagerCyclingCadence cyclingCadence = new BluetoothRemoteSensorManagerCyclingCadence(this); + private final BluetoothRemoteSensorManagerCyclingDistanceSpeed cyclingSpeed = new BluetoothRemoteSensorManagerCyclingDistanceSpeed(this); + private final BluetoothRemoteSensorManagerCyclingPower cyclingPower = new BluetoothRemoteSensorManagerCyclingPower(this); + private final BluetoothRemoteSensorManagerRunningSpeedAndCadence runningSpeedAndCadence = new BluetoothRemoteSensorManagerRunningSpeedAndCadence(this); private final SensorDataSet sensorDataSet = new SensorDataSet(); diff --git a/src/main/java/de/dennisguse/opentracks/sensors/BluetoothRemoteSensorManagerCyclingCadence.java b/src/main/java/de/dennisguse/opentracks/sensors/BluetoothRemoteSensorManagerCyclingCadence.java new file mode 100644 index 000000000..d473cfca1 --- /dev/null +++ b/src/main/java/de/dennisguse/opentracks/sensors/BluetoothRemoteSensorManagerCyclingCadence.java @@ -0,0 +1,32 @@ +package de.dennisguse.opentracks.sensors; + +import android.bluetooth.BluetoothGattCharacteristic; + +import de.dennisguse.opentracks.data.models.Cadence; +import de.dennisguse.opentracks.sensors.sensorData.SensorDataCycling; + +public class BluetoothRemoteSensorManagerCyclingCadence extends BluetoothConnectionManager { + + BluetoothRemoteSensorManagerCyclingCadence(SensorDataObserver observer) { + super(BluetoothUtils.CYCLING_SPEED_CADENCE, observer); + } + + @Override + protected SensorDataCycling.CyclingCadence createEmptySensorData(String address) { + return new SensorDataCycling.CyclingCadence(address); + } + + @Override + protected SensorDataCycling.CyclingCadence parsePayload(String sensorName, String address, BluetoothGattCharacteristic characteristic) { + SensorDataCycling.CadenceAndSpeed cadenceAndSpeed = BluetoothUtils.parseCyclingCrankAndWheel(address, sensorName, characteristic); + if (cadenceAndSpeed == null) { + return null; + } + + if (cadenceAndSpeed.getCadence() != null) { + return cadenceAndSpeed.getCadence(); + } + + return null; + } +} diff --git a/src/main/java/de/dennisguse/opentracks/sensors/BluetoothRemoteSensorManagerCyclingDistanceSpeed.java b/src/main/java/de/dennisguse/opentracks/sensors/BluetoothRemoteSensorManagerCyclingDistanceSpeed.java new file mode 100644 index 000000000..9c2c23601 --- /dev/null +++ b/src/main/java/de/dennisguse/opentracks/sensors/BluetoothRemoteSensorManagerCyclingDistanceSpeed.java @@ -0,0 +1,31 @@ +package de.dennisguse.opentracks.sensors; + +import android.bluetooth.BluetoothGattCharacteristic; + +import de.dennisguse.opentracks.sensors.sensorData.SensorDataCycling; + +public class BluetoothRemoteSensorManagerCyclingDistanceSpeed extends BluetoothConnectionManager { + + BluetoothRemoteSensorManagerCyclingDistanceSpeed(SensorDataObserver observer) { + super(BluetoothUtils.CYCLING_SPEED_CADENCE, observer); + } + + @Override + protected SensorDataCycling.DistanceSpeed createEmptySensorData(String address) { + return new SensorDataCycling.DistanceSpeed(address); + } + + @Override + protected SensorDataCycling.DistanceSpeed parsePayload(String sensorName, String address, BluetoothGattCharacteristic characteristic) { + SensorDataCycling.CadenceAndSpeed cadenceAndSpeed = BluetoothUtils.parseCyclingCrankAndWheel(address, sensorName, characteristic); + if (cadenceAndSpeed == null) { + return null; + } + + if (cadenceAndSpeed.getDistanceSpeed() != null) { + return cadenceAndSpeed.getDistanceSpeed(); + } + + return null; + } +} diff --git a/src/main/java/de/dennisguse/opentracks/sensors/BluetoothRemoteSensorManagerCyclingPower.java b/src/main/java/de/dennisguse/opentracks/sensors/BluetoothRemoteSensorManagerCyclingPower.java new file mode 100644 index 000000000..6efda9ade --- /dev/null +++ b/src/main/java/de/dennisguse/opentracks/sensors/BluetoothRemoteSensorManagerCyclingPower.java @@ -0,0 +1,27 @@ +package de.dennisguse.opentracks.sensors; + +import android.bluetooth.BluetoothGattCharacteristic; + +import androidx.annotation.NonNull; + +import de.dennisguse.opentracks.data.models.Power; +import de.dennisguse.opentracks.sensors.sensorData.SensorDataCyclingPower; + +public class BluetoothRemoteSensorManagerCyclingPower extends BluetoothConnectionManager { + + BluetoothRemoteSensorManagerCyclingPower(@NonNull SensorDataObserver observer) { + super(BluetoothUtils.CYCLING_POWER, observer); + } + + @Override + protected SensorDataCyclingPower createEmptySensorData(String address) { + return new SensorDataCyclingPower(address); + } + + @Override + protected SensorDataCyclingPower parsePayload(String sensorName, String address, BluetoothGattCharacteristic characteristic) { + SensorDataCyclingPower.Data cyclingPower = BluetoothUtils.parseCyclingPower(characteristic); + + return cyclingPower != null ? new SensorDataCyclingPower(address, sensorName, cyclingPower.getPower()) : null; + } +} diff --git a/src/main/java/de/dennisguse/opentracks/sensors/BluetoothRemoteSensorManagerHeartRate.java b/src/main/java/de/dennisguse/opentracks/sensors/BluetoothRemoteSensorManagerHeartRate.java new file mode 100644 index 000000000..2aef346d9 --- /dev/null +++ b/src/main/java/de/dennisguse/opentracks/sensors/BluetoothRemoteSensorManagerHeartRate.java @@ -0,0 +1,27 @@ +package de.dennisguse.opentracks.sensors; + +import android.bluetooth.BluetoothGattCharacteristic; + +import androidx.annotation.NonNull; + +import de.dennisguse.opentracks.data.models.HeartRate; +import de.dennisguse.opentracks.sensors.sensorData.SensorDataHeartRate; + +public class BluetoothRemoteSensorManagerHeartRate extends BluetoothConnectionManager { + + BluetoothRemoteSensorManagerHeartRate(@NonNull SensorDataObserver observer) { + super(BluetoothUtils.HEARTRATE, 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; + } +} diff --git a/src/main/java/de/dennisguse/opentracks/sensors/BluetoothRemoteSensorManagerRunningSpeedAndCadence.java b/src/main/java/de/dennisguse/opentracks/sensors/BluetoothRemoteSensorManagerRunningSpeedAndCadence.java new file mode 100644 index 000000000..20d5e063d --- /dev/null +++ b/src/main/java/de/dennisguse/opentracks/sensors/BluetoothRemoteSensorManagerRunningSpeedAndCadence.java @@ -0,0 +1,24 @@ +package de.dennisguse.opentracks.sensors; + +import android.bluetooth.BluetoothGattCharacteristic; + +import androidx.annotation.NonNull; + +import de.dennisguse.opentracks.sensors.sensorData.SensorDataRunning; + +public class BluetoothRemoteSensorManagerRunningSpeedAndCadence extends BluetoothConnectionManager { + + BluetoothRemoteSensorManagerRunningSpeedAndCadence(@NonNull SensorDataObserver observer) { + super(BluetoothUtils.RUNNING_SPEED_CADENCE, observer); + } + + @Override + protected SensorDataRunning createEmptySensorData(String address) { + return new SensorDataRunning(address); + } + + @Override + protected SensorDataRunning parsePayload(String sensorName, String address, BluetoothGattCharacteristic characteristic) { + return BluetoothUtils.parseRunningSpeedAndCadence(address, sensorName, characteristic); + } +} From 67b174feac19db0d6c2a4a7bef8a9fd4d8bce5c5 Mon Sep 17 00:00:00 2001 From: Dennis Guse Date: Sat, 20 Aug 2022 09:50:34 +0200 Subject: [PATCH 5/8] BLE: do not try to connect twice. Might had some interesting side effects. Part of #1352. --- .../opentracks/sensors/BluetoothConnectionManager.java | 1 + 1 file changed, 1 insertion(+) diff --git a/src/main/java/de/dennisguse/opentracks/sensors/BluetoothConnectionManager.java b/src/main/java/de/dennisguse/opentracks/sensors/BluetoothConnectionManager.java index 61ffd7fae..823d24c66 100644 --- a/src/main/java/de/dennisguse/opentracks/sensors/BluetoothConnectionManager.java +++ b/src/main/java/de/dennisguse/opentracks/sensors/BluetoothConnectionManager.java @@ -122,6 +122,7 @@ public abstract class BluetoothConnectionManager { synchronized void connect(Context context, @NonNull BluetoothDevice device) { if (bluetoothGatt != null) { Log.w(TAG, "Already connected; ignoring."); + return; } Log.d(TAG, "Connecting to: " + device); From dc7afbd07f842cb1c24c2f01d390b991f400fb00 Mon Sep 17 00:00:00 2001 From: Dennis Guse Date: Mon, 22 Aug 2022 19:30:56 +0200 Subject: [PATCH 6/8] BLE: extract SensorDataCycling. Part of #1352. --- .../io/file/importer/ExportImportTest.java | 9 +- .../sensors/BluetoothUtilsTest.java | 8 +- .../sensorData/SensorDataCyclingTest.java | 48 ++-- .../sensors/BluetoothRemoteSensorManager.java | 15 +- ...oothRemoteSensorManagerCyclingCadence.java | 11 +- ...moteSensorManagerCyclingDistanceSpeed.java | 13 +- .../opentracks/sensors/BluetoothUtils.java | 20 +- .../sensors/sensorData/SensorDataCycling.java | 248 ------------------ .../sensorData/SensorDataCyclingCadence.java | 88 +++++++ ...sorDataCyclingCadenceAndDistanceSpeed.java | 27 ++ .../SensorDataCyclingDistanceSpeed.java | 139 ++++++++++ .../sensorData/SensorDataCyclingPower.java | 6 +- .../sensors/sensorData/SensorDataSet.java | 16 +- 13 files changed, 330 insertions(+), 318 deletions(-) delete mode 100644 src/main/java/de/dennisguse/opentracks/sensors/sensorData/SensorDataCycling.java create mode 100644 src/main/java/de/dennisguse/opentracks/sensors/sensorData/SensorDataCyclingCadence.java create mode 100644 src/main/java/de/dennisguse/opentracks/sensors/sensorData/SensorDataCyclingCadenceAndDistanceSpeed.java create mode 100644 src/main/java/de/dennisguse/opentracks/sensors/sensorData/SensorDataCyclingDistanceSpeed.java diff --git a/src/androidTest/java/de/dennisguse/opentracks/io/file/importer/ExportImportTest.java b/src/androidTest/java/de/dennisguse/opentracks/io/file/importer/ExportImportTest.java index 5b1ae1da7..9eed1ff1b 100644 --- a/src/androidTest/java/de/dennisguse/opentracks/io/file/importer/ExportImportTest.java +++ b/src/androidTest/java/de/dennisguse/opentracks/io/file/importer/ExportImportTest.java @@ -61,7 +61,8 @@ import de.dennisguse.opentracks.io.file.TrackFileFormat; import de.dennisguse.opentracks.io.file.exporter.TrackExporter; import de.dennisguse.opentracks.sensors.AltitudeSumManager; import de.dennisguse.opentracks.sensors.BluetoothRemoteSensorManager; -import de.dennisguse.opentracks.sensors.sensorData.SensorDataCycling; +import de.dennisguse.opentracks.sensors.sensorData.SensorDataCyclingCadence; +import de.dennisguse.opentracks.sensors.sensorData.SensorDataCyclingDistanceSpeed; import de.dennisguse.opentracks.sensors.sensorData.SensorDataCyclingPower; import de.dennisguse.opentracks.sensors.sensorData.SensorDataHeartRate; import de.dennisguse.opentracks.sensors.sensorData.SensorDataSet; @@ -504,16 +505,16 @@ public class ExportImportTest { sensorDataSet.set(new SensorDataCyclingPower("power", "power", Power.of(power))); sensorDataSet.set(new SensorDataHeartRate("heartRate", "heartRate", HeartRate.of(heartRate))); - SensorDataCycling.CyclingCadence cyclingCadence = Mockito.mock(SensorDataCycling.CyclingCadence.class); + SensorDataCyclingCadence cyclingCadence = Mockito.mock(SensorDataCyclingCadence.class); Mockito.when(cyclingCadence.hasValue()).thenReturn(true); Mockito.when(cyclingCadence.getValue()).thenReturn(Cadence.of(cadence)); sensorDataSet.set(cyclingCadence); if (distance != null && speed != null) { - SensorDataCycling.DistanceSpeed.Data distanceSpeedData = Mockito.mock(SensorDataCycling.DistanceSpeed.Data.class); + SensorDataCyclingDistanceSpeed.Data distanceSpeedData = Mockito.mock(SensorDataCyclingDistanceSpeed.Data.class); Mockito.when(distanceSpeedData.getDistanceOverall()).thenReturn(distance); Mockito.when(distanceSpeedData.getSpeed()).thenReturn(Speed.of(speed)); - SensorDataCycling.DistanceSpeed distanceSpeed = Mockito.mock(SensorDataCycling.DistanceSpeed.class); + SensorDataCyclingDistanceSpeed distanceSpeed = Mockito.mock(SensorDataCyclingDistanceSpeed.class); Mockito.when(distanceSpeed.hasValue()).thenReturn(true); Mockito.when(distanceSpeed.getValue()).thenReturn(distanceSpeedData); sensorDataSet.set(distanceSpeed); diff --git a/src/androidTest/java/de/dennisguse/opentracks/sensors/BluetoothUtilsTest.java b/src/androidTest/java/de/dennisguse/opentracks/sensors/BluetoothUtilsTest.java index 682af3880..4cad4a8d9 100644 --- a/src/androidTest/java/de/dennisguse/opentracks/sensors/BluetoothUtilsTest.java +++ b/src/androidTest/java/de/dennisguse/opentracks/sensors/BluetoothUtilsTest.java @@ -10,7 +10,7 @@ import org.junit.Test; import de.dennisguse.opentracks.data.models.Cadence; import de.dennisguse.opentracks.data.models.Distance; import de.dennisguse.opentracks.data.models.Speed; -import de.dennisguse.opentracks.sensors.sensorData.SensorDataCycling; +import de.dennisguse.opentracks.sensors.sensorData.SensorDataCyclingCadenceAndDistanceSpeed; import de.dennisguse.opentracks.sensors.sensorData.SensorDataCyclingPower; import de.dennisguse.opentracks.sensors.sensorData.SensorDataRunning; @@ -48,7 +48,7 @@ public class BluetoothUtilsTest { characteristic.setValue(new byte[]{0x02, (byte) 0xC8, 0x00, 0x00, 0x00, 0x06, (byte) 0x99}); // when - SensorDataCycling.CadenceAndSpeed sensor = BluetoothUtils.parseCyclingCrankAndWheel("address", "sensorName", characteristic); + SensorDataCyclingCadenceAndDistanceSpeed sensor = BluetoothUtils.parseCyclingCrankAndWheel("address", "sensorName", characteristic); // then assertNull(sensor.getDistanceSpeed()); @@ -61,7 +61,7 @@ public class BluetoothUtilsTest { characteristic.setValue(new byte[]{0x01, (byte) 0xFF, (byte) 0xFF, 0, 1, 0x45, (byte) 0x99}); // when - SensorDataCycling.CadenceAndSpeed sensor = BluetoothUtils.parseCyclingCrankAndWheel("address", "sensorName", characteristic); + SensorDataCyclingCadenceAndDistanceSpeed sensor = BluetoothUtils.parseCyclingCrankAndWheel("address", "sensorName", characteristic); // then assertEquals(65535 + 16777216, sensor.getDistanceSpeed().getWheelRevolutionsCount()); @@ -74,7 +74,7 @@ public class BluetoothUtilsTest { characteristic.setValue(new byte[]{0x03, (byte) 0xC8, 0x00, 0x00, 0x01, 0x06, (byte) 0x99, (byte) 0xE1, 0x00, 0x45, (byte) 0x99}); // when - SensorDataCycling.CadenceAndSpeed sensor = BluetoothUtils.parseCyclingCrankAndWheel("address", "sensorName", characteristic); + SensorDataCyclingCadenceAndDistanceSpeed sensor = BluetoothUtils.parseCyclingCrankAndWheel("address", "sensorName", characteristic); // then assertEquals(200 + 16777216, sensor.getDistanceSpeed().getWheelRevolutionsCount()); diff --git a/src/androidTest/java/de/dennisguse/opentracks/sensors/sensorData/SensorDataCyclingTest.java b/src/androidTest/java/de/dennisguse/opentracks/sensors/sensorData/SensorDataCyclingTest.java index 90352ff1b..4dd5ca5ba 100644 --- a/src/androidTest/java/de/dennisguse/opentracks/sensors/sensorData/SensorDataCyclingTest.java +++ b/src/androidTest/java/de/dennisguse/opentracks/sensors/sensorData/SensorDataCyclingTest.java @@ -21,8 +21,8 @@ public class SensorDataCyclingTest { @Test public void compute_cadence_1() { // given - SensorDataCycling.CyclingCadence previous = new SensorDataCycling.CyclingCadence("sensorAddress", "sensorName", 1, 1024); // 1s - SensorDataCycling.CyclingCadence current = new SensorDataCycling.CyclingCadence("sensorAddress", "sensorName", 2, 2048); // 2s + SensorDataCyclingCadence previous = new SensorDataCyclingCadence("sensorAddress", "sensorName", 1, 1024); // 1s + SensorDataCyclingCadence current = new SensorDataCyclingCadence("sensorAddress", "sensorName", 2, 2048); // 2s // when current.compute(previous); @@ -34,8 +34,8 @@ public class SensorDataCyclingTest { @Test public void compute_cadence_2() { // given - SensorDataCycling.CyclingCadence previous = new SensorDataCycling.CyclingCadence("sensorAddress", "sensorName", 1, 6184); - SensorDataCycling.CyclingCadence current = new SensorDataCycling.CyclingCadence("sensorAddress", "sensorName", 2, 8016); + SensorDataCyclingCadence previous = new SensorDataCyclingCadence("sensorAddress", "sensorName", 1, 6184); + SensorDataCyclingCadence current = new SensorDataCyclingCadence("sensorAddress", "sensorName", 2, 8016); // when current.compute(previous); @@ -47,8 +47,8 @@ public class SensorDataCyclingTest { @Test public void compute_cadence_sameCount() { // given - SensorDataCycling.CyclingCadence previous = new SensorDataCycling.CyclingCadence("sensorAddress", "sensorName", 1, 1024); - SensorDataCycling.CyclingCadence current = new SensorDataCycling.CyclingCadence("sensorAddress", "sensorName", 1, 2048); + SensorDataCyclingCadence previous = new SensorDataCyclingCadence("sensorAddress", "sensorName", 1, 1024); + SensorDataCyclingCadence current = new SensorDataCyclingCadence("sensorAddress", "sensorName", 1, 2048); // when current.compute(previous); @@ -61,8 +61,8 @@ public class SensorDataCyclingTest { @Test public void compute_cadence_sameTime() { // given - SensorDataCycling.CyclingCadence previous = new SensorDataCycling.CyclingCadence("sensorAddress", "sensorName", 1, 1024); - SensorDataCycling.CyclingCadence current = new SensorDataCycling.CyclingCadence("sensorAddress", "sensorName", 2, 1024); + SensorDataCyclingCadence previous = new SensorDataCyclingCadence("sensorAddress", "sensorName", 1, 1024); + SensorDataCyclingCadence current = new SensorDataCyclingCadence("sensorAddress", "sensorName", 2, 1024); // when current.compute(previous); @@ -74,8 +74,8 @@ public class SensorDataCyclingTest { @Test public void compute_cadence_rollOverTime() { // given - SensorDataCycling.CyclingCadence previous = new SensorDataCycling.CyclingCadence("sensorAddress", "sensorName", 1, UintUtils.UINT16_MAX - 1024); - SensorDataCycling.CyclingCadence current = new SensorDataCycling.CyclingCadence("sensorAddress", "sensorName", 2, 0); + SensorDataCyclingCadence previous = new SensorDataCyclingCadence("sensorAddress", "sensorName", 1, UintUtils.UINT16_MAX - 1024); + SensorDataCyclingCadence current = new SensorDataCyclingCadence("sensorAddress", "sensorName", 2, 0); // when current.compute(previous); @@ -89,8 +89,8 @@ public class SensorDataCyclingTest { @Deprecated public void compute_cadence_rollOverCount() { // given - SensorDataCycling.CyclingCadence previous = new SensorDataCycling.CyclingCadence("sensorAddress", "sensorName", UintUtils.UINT32_MAX - 1, 1024); - SensorDataCycling.CyclingCadence current = new SensorDataCycling.CyclingCadence("sensorAddress", "sensorName", 0, 2048); + SensorDataCyclingCadence previous = new SensorDataCyclingCadence("sensorAddress", "sensorName", UintUtils.UINT32_MAX - 1, 1024); + SensorDataCyclingCadence current = new SensorDataCyclingCadence("sensorAddress", "sensorName", 0, 2048); // when current.compute(previous); @@ -102,8 +102,8 @@ public class SensorDataCyclingTest { @Test public void compute_cadence_overflow() { // given - SensorDataCycling.CyclingCadence previous = new SensorDataCycling.CyclingCadence("sensorAddress", "sensorName", UintUtils.UINT32_MAX - 1, 1024); - SensorDataCycling.CyclingCadence current = new SensorDataCycling.CyclingCadence("sensorAddress", "sensorName", 0, 2048); + SensorDataCyclingCadence previous = new SensorDataCyclingCadence("sensorAddress", "sensorName", UintUtils.UINT32_MAX - 1, 1024); + SensorDataCyclingCadence current = new SensorDataCyclingCadence("sensorAddress", "sensorName", 0, 2048); // when current.compute(previous); @@ -115,8 +115,8 @@ public class SensorDataCyclingTest { @Test public void compute_speed() { // given - SensorDataCycling.DistanceSpeed previous = new SensorDataCycling.DistanceSpeed("sensorAddress", "sensorName", 1, 6184); - SensorDataCycling.DistanceSpeed current = new SensorDataCycling.DistanceSpeed("sensorAddress", "sensorName", 2, 8016); + SensorDataCyclingDistanceSpeed previous = new SensorDataCyclingDistanceSpeed("sensorAddress", "sensorName", 1, 6184); + SensorDataCyclingDistanceSpeed current = new SensorDataCyclingDistanceSpeed("sensorAddress", "sensorName", 2, 8016); // when current.compute(previous, Distance.ofMM(2150)); @@ -131,8 +131,8 @@ public class SensorDataCyclingTest { @Deprecated public void compute_speed_rollOverCount() { // given - SensorDataCycling.DistanceSpeed previous = new SensorDataCycling.DistanceSpeed("sensorAddress", "sensorName", UintUtils.UINT32_MAX - 1, 1024); - SensorDataCycling.DistanceSpeed current = new SensorDataCycling.DistanceSpeed("sensorAddress", "sensorName", 0, 2048); + SensorDataCyclingDistanceSpeed previous = new SensorDataCyclingDistanceSpeed("sensorAddress", "sensorName", UintUtils.UINT32_MAX - 1, 1024); + SensorDataCyclingDistanceSpeed current = new SensorDataCyclingDistanceSpeed("sensorAddress", "sensorName", 0, 2048); // when current.compute(previous, Distance.ofMM(2000)); @@ -145,8 +145,8 @@ public class SensorDataCyclingTest { @Test public void compute_speed_overflow() { // given - SensorDataCycling.DistanceSpeed previous = new SensorDataCycling.DistanceSpeed("sensorAddress", "sensorName", UintUtils.UINT32_MAX - 1, 1024); - SensorDataCycling.DistanceSpeed current = new SensorDataCycling.DistanceSpeed("sensorAddress", "sensorName", 0, 2048); + SensorDataCyclingDistanceSpeed previous = new SensorDataCyclingDistanceSpeed("sensorAddress", "sensorName", UintUtils.UINT32_MAX - 1, 1024); + SensorDataCyclingDistanceSpeed current = new SensorDataCyclingDistanceSpeed("sensorAddress", "sensorName", 0, 2048); // when current.compute(previous, Distance.ofMM(2000)); @@ -158,8 +158,8 @@ public class SensorDataCyclingTest { @Test public void equals_speed_with_no_data() { // given - SensorDataCycling.DistanceSpeed previous = new SensorDataCycling.DistanceSpeed("sensorAddress"); - SensorDataCycling.DistanceSpeed current = new SensorDataCycling.DistanceSpeed("sensorAddress", "sensorName", 0, 2048); + SensorDataCyclingDistanceSpeed previous = new SensorDataCyclingDistanceSpeed("sensorAddress"); + SensorDataCyclingDistanceSpeed current = new SensorDataCyclingDistanceSpeed("sensorAddress", "sensorName", 0, 2048); // when previous.toString(); @@ -172,8 +172,8 @@ public class SensorDataCyclingTest { @Test public void equals_cadence_with_no_data() { // given - SensorDataCycling.CyclingCadence previous = new SensorDataCycling.CyclingCadence("sensorAddress"); - SensorDataCycling.CyclingCadence current = new SensorDataCycling.CyclingCadence("sensorAddress", "sensorName", 0, 2048); + SensorDataCyclingCadence previous = new SensorDataCyclingCadence("sensorAddress"); + SensorDataCyclingCadence current = new SensorDataCyclingCadence("sensorAddress", "sensorName", 0, 2048); // when previous.toString(); diff --git a/src/main/java/de/dennisguse/opentracks/sensors/BluetoothRemoteSensorManager.java b/src/main/java/de/dennisguse/opentracks/sensors/BluetoothRemoteSensorManager.java index 567d9d26b..9e16afa2a 100644 --- a/src/main/java/de/dennisguse/opentracks/sensors/BluetoothRemoteSensorManager.java +++ b/src/main/java/de/dennisguse/opentracks/sensors/BluetoothRemoteSensorManager.java @@ -31,7 +31,8 @@ import de.dennisguse.opentracks.R; import de.dennisguse.opentracks.data.models.Distance; import de.dennisguse.opentracks.data.models.TrackPoint; import de.dennisguse.opentracks.sensors.sensorData.SensorData; -import de.dennisguse.opentracks.sensors.sensorData.SensorDataCycling; +import de.dennisguse.opentracks.sensors.sensorData.SensorDataCyclingCadence; +import de.dennisguse.opentracks.sensors.sensorData.SensorDataCyclingDistanceSpeed; import de.dennisguse.opentracks.sensors.sensorData.SensorDataRunning; import de.dennisguse.opentracks.sensors.sensorData.SensorDataSet; import de.dennisguse.opentracks.settings.PreferencesUtils; @@ -186,24 +187,24 @@ public class BluetoothRemoteSensorManager implements BluetoothConnectionManager. @Override public synchronized void onChanged(SensorData sensorData) { - if (sensorData instanceof SensorDataCycling.CyclingCadence) { - SensorDataCycling.CyclingCadence previous = sensorDataSet.getCyclingCadence(); + if (sensorData instanceof SensorDataCyclingCadence) { + SensorDataCyclingCadence previous = sensorDataSet.getCyclingCadence(); Log.d(TAG, "Previous: " + previous + "; current: " + sensorData); if (sensorData.equals(previous)) { Log.d(TAG, "onChanged: cadence data repeated."); return; } - ((SensorDataCycling.CyclingCadence) sensorData).compute(previous); + ((SensorDataCyclingCadence) sensorData).compute(previous); } - if (sensorData instanceof SensorDataCycling.DistanceSpeed) { - SensorDataCycling.DistanceSpeed previous = sensorDataSet.getCyclingDistanceSpeed(); + if (sensorData instanceof SensorDataCyclingDistanceSpeed) { + SensorDataCyclingDistanceSpeed previous = sensorDataSet.getCyclingDistanceSpeed(); Log.d(TAG, "Previous: " + previous + "; Current" + sensorData); if (sensorData.equals(previous)) { Log.d(TAG, "onChanged: cycling speed data repeated."); return; } - ((SensorDataCycling.DistanceSpeed) sensorData).compute(previous, preferenceWheelCircumference); + ((SensorDataCyclingDistanceSpeed) sensorData).compute(previous, preferenceWheelCircumference); } if (sensorData instanceof SensorDataRunning) { SensorDataRunning previous = sensorDataSet.getRunningDistanceSpeedCadence(); diff --git a/src/main/java/de/dennisguse/opentracks/sensors/BluetoothRemoteSensorManagerCyclingCadence.java b/src/main/java/de/dennisguse/opentracks/sensors/BluetoothRemoteSensorManagerCyclingCadence.java index d473cfca1..baf29226c 100644 --- a/src/main/java/de/dennisguse/opentracks/sensors/BluetoothRemoteSensorManagerCyclingCadence.java +++ b/src/main/java/de/dennisguse/opentracks/sensors/BluetoothRemoteSensorManagerCyclingCadence.java @@ -3,7 +3,8 @@ package de.dennisguse.opentracks.sensors; import android.bluetooth.BluetoothGattCharacteristic; import de.dennisguse.opentracks.data.models.Cadence; -import de.dennisguse.opentracks.sensors.sensorData.SensorDataCycling; +import de.dennisguse.opentracks.sensors.sensorData.SensorDataCyclingCadence; +import de.dennisguse.opentracks.sensors.sensorData.SensorDataCyclingCadenceAndDistanceSpeed; public class BluetoothRemoteSensorManagerCyclingCadence extends BluetoothConnectionManager { @@ -12,13 +13,13 @@ public class BluetoothRemoteSensorManagerCyclingCadence extends BluetoothConnect } @Override - protected SensorDataCycling.CyclingCadence createEmptySensorData(String address) { - return new SensorDataCycling.CyclingCadence(address); + protected SensorDataCyclingCadence createEmptySensorData(String address) { + return new SensorDataCyclingCadence(address); } @Override - protected SensorDataCycling.CyclingCadence parsePayload(String sensorName, String address, BluetoothGattCharacteristic characteristic) { - SensorDataCycling.CadenceAndSpeed cadenceAndSpeed = BluetoothUtils.parseCyclingCrankAndWheel(address, sensorName, characteristic); + protected SensorDataCyclingCadence parsePayload(String sensorName, String address, BluetoothGattCharacteristic characteristic) { + SensorDataCyclingCadenceAndDistanceSpeed cadenceAndSpeed = BluetoothUtils.parseCyclingCrankAndWheel(address, sensorName, characteristic); if (cadenceAndSpeed == null) { return null; } diff --git a/src/main/java/de/dennisguse/opentracks/sensors/BluetoothRemoteSensorManagerCyclingDistanceSpeed.java b/src/main/java/de/dennisguse/opentracks/sensors/BluetoothRemoteSensorManagerCyclingDistanceSpeed.java index 9c2c23601..3abd42bee 100644 --- a/src/main/java/de/dennisguse/opentracks/sensors/BluetoothRemoteSensorManagerCyclingDistanceSpeed.java +++ b/src/main/java/de/dennisguse/opentracks/sensors/BluetoothRemoteSensorManagerCyclingDistanceSpeed.java @@ -2,22 +2,23 @@ package de.dennisguse.opentracks.sensors; import android.bluetooth.BluetoothGattCharacteristic; -import de.dennisguse.opentracks.sensors.sensorData.SensorDataCycling; +import de.dennisguse.opentracks.sensors.sensorData.SensorDataCyclingCadenceAndDistanceSpeed; +import de.dennisguse.opentracks.sensors.sensorData.SensorDataCyclingDistanceSpeed; -public class BluetoothRemoteSensorManagerCyclingDistanceSpeed extends BluetoothConnectionManager { +public class BluetoothRemoteSensorManagerCyclingDistanceSpeed extends BluetoothConnectionManager { BluetoothRemoteSensorManagerCyclingDistanceSpeed(SensorDataObserver observer) { super(BluetoothUtils.CYCLING_SPEED_CADENCE, observer); } @Override - protected SensorDataCycling.DistanceSpeed createEmptySensorData(String address) { - return new SensorDataCycling.DistanceSpeed(address); + protected SensorDataCyclingDistanceSpeed createEmptySensorData(String address) { + return new SensorDataCyclingDistanceSpeed(address); } @Override - protected SensorDataCycling.DistanceSpeed parsePayload(String sensorName, String address, BluetoothGattCharacteristic characteristic) { - SensorDataCycling.CadenceAndSpeed cadenceAndSpeed = BluetoothUtils.parseCyclingCrankAndWheel(address, sensorName, characteristic); + protected SensorDataCyclingDistanceSpeed parsePayload(String sensorName, String address, BluetoothGattCharacteristic characteristic) { + SensorDataCyclingCadenceAndDistanceSpeed cadenceAndSpeed = BluetoothUtils.parseCyclingCrankAndWheel(address, sensorName, characteristic); if (cadenceAndSpeed == null) { return null; } diff --git a/src/main/java/de/dennisguse/opentracks/sensors/BluetoothUtils.java b/src/main/java/de/dennisguse/opentracks/sensors/BluetoothUtils.java index 722aafd13..1450b4842 100644 --- a/src/main/java/de/dennisguse/opentracks/sensors/BluetoothUtils.java +++ b/src/main/java/de/dennisguse/opentracks/sensors/BluetoothUtils.java @@ -32,7 +32,9 @@ import de.dennisguse.opentracks.data.models.Cadence; import de.dennisguse.opentracks.data.models.Distance; import de.dennisguse.opentracks.data.models.Power; import de.dennisguse.opentracks.data.models.Speed; -import de.dennisguse.opentracks.sensors.sensorData.SensorDataCycling; +import de.dennisguse.opentracks.sensors.sensorData.SensorDataCyclingCadence; +import de.dennisguse.opentracks.sensors.sensorData.SensorDataCyclingCadenceAndDistanceSpeed; +import de.dennisguse.opentracks.sensors.sensorData.SensorDataCyclingDistanceSpeed; import de.dennisguse.opentracks.sensors.sensorData.SensorDataCyclingPower; import de.dennisguse.opentracks.sensors.sensorData.SensorDataRunning; @@ -139,20 +141,20 @@ public class BluetoothUtils { index += 2 + 2; } - SensorDataCycling.CyclingCadence cadence = null; + SensorDataCyclingCadence cadence = null; if (hasCrank && valueLength - index >= 4) { long crankCount = characteristic.getIntValue(BluetoothGattCharacteristic.FORMAT_UINT16, index); index += 2; int crankTime = characteristic.getIntValue(BluetoothGattCharacteristic.FORMAT_UINT16, index); // 1/1024s - cadence = new SensorDataCycling.CyclingCadence("", "", crankCount, crankTime); + cadence = new SensorDataCyclingCadence("", "", crankCount, crankTime); } return new SensorDataCyclingPower.Data(Power.of(instantaneousPower), cadence); } - public static SensorDataCycling.CadenceAndSpeed parseCyclingCrankAndWheel(String address, String sensorName, @NonNull BluetoothGattCharacteristic characteristic) { + public static SensorDataCyclingCadenceAndDistanceSpeed parseCyclingCrankAndWheel(String address, String sensorName, @NonNull BluetoothGattCharacteristic characteristic) { // DOCUMENTATION https://www.bluetooth.com/wp-content/uploads/Sitecore-Media-Library/Gatt/Xml/Characteristics/org.bluetooth.characteristic.csc_measurement.xml int valueLength = characteristic.getValue().length; if (valueLength == 0) { @@ -164,25 +166,25 @@ public class BluetoothUtils { boolean hasCrank = (flags & 0x02) > 0; int index = 1; - SensorDataCycling.DistanceSpeed speed = null; + SensorDataCyclingDistanceSpeed speed = null; if (hasWheel && valueLength - index >= 6) { int wheelTotalRevolutionCount = characteristic.getIntValue(BluetoothGattCharacteristic.FORMAT_UINT32, index); index += 4; int wheelTime = characteristic.getIntValue(BluetoothGattCharacteristic.FORMAT_UINT16, index); // 1/1024s - speed = new SensorDataCycling.DistanceSpeed(address, sensorName, wheelTotalRevolutionCount, wheelTime); + speed = new SensorDataCyclingDistanceSpeed(address, sensorName, wheelTotalRevolutionCount, wheelTime); index += 2; } - SensorDataCycling.CyclingCadence cadence = null; + SensorDataCyclingCadence cadence = null; if (hasCrank && valueLength - index >= 4) { long crankCount = characteristic.getIntValue(BluetoothGattCharacteristic.FORMAT_UINT16, index); index += 2; int crankTime = characteristic.getIntValue(BluetoothGattCharacteristic.FORMAT_UINT16, index); // 1/1024s - cadence = new SensorDataCycling.CyclingCadence(address, sensorName, crankCount, crankTime); + cadence = new SensorDataCyclingCadence(address, sensorName, crankCount, crankTime); } - return new SensorDataCycling.CadenceAndSpeed(address, sensorName, cadence, speed); + return new SensorDataCyclingCadenceAndDistanceSpeed(address, sensorName, cadence, speed); } public static SensorDataRunning parseRunningSpeedAndCadence(String address, String sensorName, @NonNull BluetoothGattCharacteristic characteristic) { diff --git a/src/main/java/de/dennisguse/opentracks/sensors/sensorData/SensorDataCycling.java b/src/main/java/de/dennisguse/opentracks/sensors/sensorData/SensorDataCycling.java deleted file mode 100644 index 7827127ca..000000000 --- a/src/main/java/de/dennisguse/opentracks/sensors/sensorData/SensorDataCycling.java +++ /dev/null @@ -1,248 +0,0 @@ -package de.dennisguse.opentracks.sensors.sensorData; - -import android.util.Log; - -import androidx.annotation.NonNull; -import androidx.annotation.Nullable; -import androidx.core.util.Pair; - -import java.time.Duration; - -import de.dennisguse.opentracks.data.models.Cadence; -import de.dennisguse.opentracks.data.models.Distance; -import de.dennisguse.opentracks.data.models.Speed; -import de.dennisguse.opentracks.sensors.UintUtils; - -/** - * Provides cadence in rpm and speed in milliseconds from Bluetooth LE Cycling Cadence and Speed sensors. - */ -public final class SensorDataCycling { - - private static final String TAG = SensorDataCycling.class.getSimpleName(); - - private SensorDataCycling() { - } - - public static class CyclingCadence extends SensorData { - - private final Long crankRevolutionsCount; // UINT32 - private final Integer crankRevolutionsTime; // UINT16; 1/1024s - - public CyclingCadence(String sensorAddress) { - super(sensorAddress); - this.crankRevolutionsCount = null; - this.crankRevolutionsTime = null; - } - - public CyclingCadence(String sensorAddress, String sensorName, long crankRevolutionsCount, int crankRevolutionsTime) { - super(sensorAddress, sensorName); - this.crankRevolutionsCount = crankRevolutionsCount; - this.crankRevolutionsTime = crankRevolutionsTime; - } - - public boolean hasData() { - return crankRevolutionsCount != null && crankRevolutionsTime != null; - } - - public long getCrankRevolutionsCount() { - return crankRevolutionsCount; - } - - public int getCrankRevolutionsTime() { - return crankRevolutionsTime; - } - - @NonNull - @Override - protected Cadence getNoneValue() { - return Cadence.of(0); - } - - public void compute(CyclingCadence previous) { - if (hasData() && previous != null && previous.hasData()) { - float timeDiff_ms = UintUtils.diff(crankRevolutionsTime, previous.crankRevolutionsTime, UintUtils.UINT16_MAX) / 1024f * 1000; - Duration timeDiff = Duration.ofMillis((long) timeDiff_ms); - if (timeDiff.isZero() || timeDiff.isNegative()) { - Log.e(TAG, "Timestamps difference is invalid: cannot compute cadence."); - value = null; - return; - } - - // TODO We have to treat with overflow according to the documentation: read https://github.com/OpenTracksApp/OpenTracks/pull/953#discussion_r711625268 - if (crankRevolutionsCount < previous.crankRevolutionsCount) { - Log.e(TAG, "Crank revolutions count difference is invalid: cannot compute cadence."); - return; - } - - long crankDiff = UintUtils.diff(crankRevolutionsCount, previous.crankRevolutionsCount, UintUtils.UINT32_MAX); - value = Cadence.of(crankDiff, timeDiff); - } - } - - @NonNull - @Override - public String toString() { - return super.toString() + " cadence=" + value + " time=" + crankRevolutionsTime + " count=" + crankRevolutionsCount; - } - - @Override - public boolean equals(@Nullable Object obj) { - if (!(obj instanceof CyclingCadence)) return false; - - CyclingCadence comp = (CyclingCadence) obj; - if (hasData() && comp.hasData() == hasData()) { - return getCrankRevolutionsCount() == comp.getCrankRevolutionsCount() && getCrankRevolutionsTime() == comp.getCrankRevolutionsTime(); - } else { - return false; - } - } - } - - public static class DistanceSpeed extends SensorData { - - private final Long wheelRevolutionsCount; // UINT32 - private final Integer wheelRevolutionsTime; // UINT16; 1/1024s - - public DistanceSpeed(String sensorAddress) { - super(sensorAddress); - this.wheelRevolutionsCount = null; - this.wheelRevolutionsTime = null; - } - - public DistanceSpeed(String sensorAddress, String sensorName, long wheelRevolutionsCount, int wheelRevolutionsTime) { - super(sensorAddress, sensorName); - this.wheelRevolutionsCount = wheelRevolutionsCount; - this.wheelRevolutionsTime = wheelRevolutionsTime; - } - - public boolean hasData() { - return wheelRevolutionsCount != null && wheelRevolutionsTime != null; - } - - public long getWheelRevolutionsCount() { - return wheelRevolutionsCount; - } - - public int getWheelRevolutionsTime() { - return wheelRevolutionsTime; - } - - @NonNull - @Override - protected Data getNoneValue() { - if (value != null) { - return new Data(value.distance, value.distanceOverall, Speed.zero()); - } else { - return new Data(Distance.of(0), Distance.of(0), Speed.zero()); - } - } - - public void compute(DistanceSpeed previous, Distance wheelCircumference) { - if (hasData() && previous != null && previous.hasData()) { - float timeDiff_ms = UintUtils.diff(wheelRevolutionsTime, previous.wheelRevolutionsTime, UintUtils.UINT16_MAX) / 1024f * 1000; - Duration timeDiff = Duration.ofMillis((long) timeDiff_ms); - if (timeDiff.isZero() || timeDiff.isNegative()) { - Log.e(TAG, "Timestamps difference is invalid: cannot compute speed."); - value = null; - return; - } - - if (wheelRevolutionsCount < previous.wheelRevolutionsCount) { - Log.e(TAG, "Wheel revolutions count difference is invalid: cannot compute speed."); - return; - } - long wheelDiff = UintUtils.diff(wheelRevolutionsCount, previous.wheelRevolutionsCount, UintUtils.UINT32_MAX); - - Distance distance = wheelCircumference.multipliedBy(wheelDiff); - Distance distanceOverall = distance; - if (previous.hasValue()) { - distanceOverall = distance.plus(previous.getValue().distanceOverall); - } - Speed speed_mps = Speed.of(distance, timeDiff); - value = new Data(distance, distanceOverall, speed_mps); - } - } - - @Override - public void reset() { - if (value != null) { - value = new Data(value.distance, Distance.of(0), value.speed); - } - } - - @NonNull - @Override - public String toString() { - return super.toString() + " data=" + value + " time=" + wheelRevolutionsTime + " count=" + wheelRevolutionsCount; - } - - @Override - public boolean equals(@Nullable Object obj) { - if (!(obj instanceof DistanceSpeed)) return false; - - DistanceSpeed comp = (DistanceSpeed) obj; - if (!(hasData() && comp.hasData())) { - return false; - } - - return getWheelRevolutionsCount() == comp.getWheelRevolutionsCount() && getWheelRevolutionsTime() == comp.getWheelRevolutionsTime(); - } - - public static class Data { - private final Distance distance; - private final Distance distanceOverall; - private final Speed speed; - - private Data(Distance distance, Distance distanceOverall, Speed speed) { - this.distance = distance; - this.distanceOverall = distanceOverall; - this.speed = speed; - } - - public Distance getDistance() { - return distance; - } - - public Distance getDistanceOverall() { - return distanceOverall; - } - - public Speed getSpeed() { - return speed; - } - - @NonNull - @Override - public String toString() { - return "Data{" + - "distance=" + getDistance() + - ", distance_overall=" + getDistanceOverall() + - ", speed=" + getSpeed() + - '}'; - } - } - } - - public static class CadenceAndSpeed extends SensorData> { - - public CadenceAndSpeed(String sensorAddress, String sensorName, @Nullable CyclingCadence cadence, @Nullable DistanceSpeed distanceSpeed) { - super(sensorAddress, sensorName); - this.value = new Pair<>(cadence, distanceSpeed); - } - - public CyclingCadence getCadence() { - return this.value != null ? this.value.first : null; - } - - public DistanceSpeed getDistanceSpeed() { - return this.value != null ? this.value.second : null; - } - - @NonNull - @Override - protected Pair getNoneValue() { - return new Pair<>(null, null); - } - } -} - diff --git a/src/main/java/de/dennisguse/opentracks/sensors/sensorData/SensorDataCyclingCadence.java b/src/main/java/de/dennisguse/opentracks/sensors/sensorData/SensorDataCyclingCadence.java new file mode 100644 index 000000000..efb95d070 --- /dev/null +++ b/src/main/java/de/dennisguse/opentracks/sensors/sensorData/SensorDataCyclingCadence.java @@ -0,0 +1,88 @@ +package de.dennisguse.opentracks.sensors.sensorData; + +import android.util.Log; + +import androidx.annotation.NonNull; +import androidx.annotation.Nullable; + +import java.time.Duration; + +import de.dennisguse.opentracks.data.models.Cadence; +import de.dennisguse.opentracks.sensors.UintUtils; + +public class SensorDataCyclingCadence extends SensorData { + + private final String TAG = SensorDataCyclingCadence.class.getSimpleName(); + + private final Long crankRevolutionsCount; // UINT32 + private final Integer crankRevolutionsTime; // UINT16; 1/1024s + + public SensorDataCyclingCadence(String sensorAddress) { + super(sensorAddress); + this.crankRevolutionsCount = null; + this.crankRevolutionsTime = null; + } + + public SensorDataCyclingCadence(String sensorAddress, String sensorName, long crankRevolutionsCount, int crankRevolutionsTime) { + super(sensorAddress, sensorName); + this.crankRevolutionsCount = crankRevolutionsCount; + this.crankRevolutionsTime = crankRevolutionsTime; + } + + public boolean hasData() { + return crankRevolutionsCount != null && crankRevolutionsTime != null; + } + + public long getCrankRevolutionsCount() { + return crankRevolutionsCount; + } + + public int getCrankRevolutionsTime() { + return crankRevolutionsTime; + } + + @NonNull + @Override + protected Cadence getNoneValue() { + return Cadence.of(0); + } + + public void compute(SensorDataCyclingCadence previous) { + if (hasData() && previous != null && previous.hasData()) { + float timeDiff_ms = UintUtils.diff(crankRevolutionsTime, previous.crankRevolutionsTime, UintUtils.UINT16_MAX) / 1024f * 1000; + Duration timeDiff = Duration.ofMillis((long) timeDiff_ms); + if (timeDiff.isZero() || timeDiff.isNegative()) { + Log.e(TAG, "Timestamps difference is invalid: cannot compute cadence."); + value = null; + return; + } + + // TODO We have to treat with overflow according to the documentation: read https://github.com/OpenTracksApp/OpenTracks/pull/953#discussion_r711625268 + if (crankRevolutionsCount < previous.crankRevolutionsCount) { + Log.e(TAG, "Crank revolutions count difference is invalid: cannot compute cadence."); + return; + } + + long crankDiff = UintUtils.diff(crankRevolutionsCount, previous.crankRevolutionsCount, UintUtils.UINT32_MAX); + value = Cadence.of(crankDiff, timeDiff); + } + } + + @NonNull + @Override + public String toString() { + return super.toString() + " cadence=" + value + " time=" + crankRevolutionsTime + " count=" + crankRevolutionsCount; + } + + @Override + public boolean equals(@Nullable Object obj) { + if (!(obj instanceof SensorDataCyclingCadence)) return false; + + SensorDataCyclingCadence comp = (SensorDataCyclingCadence) obj; + if (hasData() && comp.hasData() == hasData()) { + return getCrankRevolutionsCount() == comp.getCrankRevolutionsCount() && getCrankRevolutionsTime() == comp.getCrankRevolutionsTime(); + } else { + return false; + } + } +} diff --git a/src/main/java/de/dennisguse/opentracks/sensors/sensorData/SensorDataCyclingCadenceAndDistanceSpeed.java b/src/main/java/de/dennisguse/opentracks/sensors/sensorData/SensorDataCyclingCadenceAndDistanceSpeed.java new file mode 100644 index 000000000..26d4cd9c2 --- /dev/null +++ b/src/main/java/de/dennisguse/opentracks/sensors/sensorData/SensorDataCyclingCadenceAndDistanceSpeed.java @@ -0,0 +1,27 @@ +package de.dennisguse.opentracks.sensors.sensorData; + +import androidx.annotation.NonNull; +import androidx.annotation.Nullable; +import androidx.core.util.Pair; + +public class SensorDataCyclingCadenceAndDistanceSpeed extends SensorData> { + + public SensorDataCyclingCadenceAndDistanceSpeed(String sensorAddress, String sensorName, @Nullable SensorDataCyclingCadence cadence, @Nullable SensorDataCyclingDistanceSpeed distanceSpeed) { + super(sensorAddress, sensorName); + this.value = new Pair<>(cadence, distanceSpeed); + } + + public SensorDataCyclingCadence getCadence() { + return this.value != null ? this.value.first : null; + } + + public SensorDataCyclingDistanceSpeed getDistanceSpeed() { + return this.value != null ? this.value.second : null; + } + + @NonNull + @Override + protected Pair getNoneValue() { + return new Pair<>(null, null); + } +} diff --git a/src/main/java/de/dennisguse/opentracks/sensors/sensorData/SensorDataCyclingDistanceSpeed.java b/src/main/java/de/dennisguse/opentracks/sensors/sensorData/SensorDataCyclingDistanceSpeed.java new file mode 100644 index 000000000..6a5528e24 --- /dev/null +++ b/src/main/java/de/dennisguse/opentracks/sensors/sensorData/SensorDataCyclingDistanceSpeed.java @@ -0,0 +1,139 @@ +package de.dennisguse.opentracks.sensors.sensorData; + +import android.util.Log; + +import androidx.annotation.NonNull; +import androidx.annotation.Nullable; + +import java.time.Duration; + +import de.dennisguse.opentracks.data.models.Distance; +import de.dennisguse.opentracks.data.models.Speed; +import de.dennisguse.opentracks.sensors.UintUtils; + +public class SensorDataCyclingDistanceSpeed extends SensorData { + + private final String TAG = SensorDataCyclingDistanceSpeed.class.getSimpleName(); + + private final Long wheelRevolutionsCount; // UINT32 + private final Integer wheelRevolutionsTime; // UINT16; 1/1024s + + public SensorDataCyclingDistanceSpeed(String sensorAddress) { + super(sensorAddress); + this.wheelRevolutionsCount = null; + this.wheelRevolutionsTime = null; + } + + public SensorDataCyclingDistanceSpeed(String sensorAddress, String sensorName, long wheelRevolutionsCount, int wheelRevolutionsTime) { + super(sensorAddress, sensorName); + this.wheelRevolutionsCount = wheelRevolutionsCount; + this.wheelRevolutionsTime = wheelRevolutionsTime; + } + + public boolean hasData() { + return wheelRevolutionsCount != null && wheelRevolutionsTime != null; + } + + public long getWheelRevolutionsCount() { + return wheelRevolutionsCount; + } + + public int getWheelRevolutionsTime() { + return wheelRevolutionsTime; + } + + @NonNull + @Override + protected Data getNoneValue() { + if (value != null) { + return new Data(value.distance, value.distanceOverall, Speed.zero()); + } else { + return new Data(Distance.of(0), Distance.of(0), Speed.zero()); + } + } + + public void compute(SensorDataCyclingDistanceSpeed previous, Distance wheelCircumference) { + if (hasData() && previous != null && previous.hasData()) { + float timeDiff_ms = UintUtils.diff(wheelRevolutionsTime, previous.wheelRevolutionsTime, UintUtils.UINT16_MAX) / 1024f * 1000; + Duration timeDiff = Duration.ofMillis((long) timeDiff_ms); + if (timeDiff.isZero() || timeDiff.isNegative()) { + Log.e(TAG, "Timestamps difference is invalid: cannot compute speed."); + value = null; + return; + } + + if (wheelRevolutionsCount < previous.wheelRevolutionsCount) { + Log.e(TAG, "Wheel revolutions count difference is invalid: cannot compute speed."); + return; + } + long wheelDiff = UintUtils.diff(wheelRevolutionsCount, previous.wheelRevolutionsCount, UintUtils.UINT32_MAX); + + Distance distance = wheelCircumference.multipliedBy(wheelDiff); + Distance distanceOverall = distance; + if (previous.hasValue()) { + distanceOverall = distance.plus(previous.getValue().distanceOverall); + } + Speed speed_mps = Speed.of(distance, timeDiff); + value = new Data(distance, distanceOverall, speed_mps); + } + } + + @Override + public void reset() { + if (value != null) { + value = new Data(value.distance, Distance.of(0), value.speed); + } + } + + @NonNull + @Override + public String toString() { + return super.toString() + " data=" + value + " time=" + wheelRevolutionsTime + " count=" + wheelRevolutionsCount; + } + + @Override + public boolean equals(@Nullable Object obj) { + if (!(obj instanceof SensorDataCyclingDistanceSpeed)) return false; + + SensorDataCyclingDistanceSpeed comp = (SensorDataCyclingDistanceSpeed) obj; + if (!(hasData() && comp.hasData())) { + return false; + } + + return getWheelRevolutionsCount() == comp.getWheelRevolutionsCount() && getWheelRevolutionsTime() == comp.getWheelRevolutionsTime(); + } + + public static class Data { + private final Distance distance; + private final Distance distanceOverall; + private final Speed speed; + + private Data(Distance distance, Distance distanceOverall, Speed speed) { + this.distance = distance; + this.distanceOverall = distanceOverall; + this.speed = speed; + } + + public Distance getDistance() { + return distance; + } + + public Distance getDistanceOverall() { + return distanceOverall; + } + + public Speed getSpeed() { + return speed; + } + + @NonNull + @Override + public String toString() { + return "Data{" + + "distance=" + getDistance() + + ", distance_overall=" + getDistanceOverall() + + ", speed=" + getSpeed() + + '}'; + } + } +} diff --git a/src/main/java/de/dennisguse/opentracks/sensors/sensorData/SensorDataCyclingPower.java b/src/main/java/de/dennisguse/opentracks/sensors/sensorData/SensorDataCyclingPower.java index 5c962b3a5..ba81d789d 100644 --- a/src/main/java/de/dennisguse/opentracks/sensors/sensorData/SensorDataCyclingPower.java +++ b/src/main/java/de/dennisguse/opentracks/sensors/sensorData/SensorDataCyclingPower.java @@ -30,9 +30,9 @@ public class SensorDataCyclingPower extends SensorData { public static class Data { private final Power power; - private final SensorDataCycling.CyclingCadence cadence; + private final SensorDataCyclingCadence cadence; - public Data(Power power, @Nullable SensorDataCycling.CyclingCadence cadence) { + public Data(Power power, @Nullable SensorDataCyclingCadence cadence) { this.power = power; this.cadence = cadence; } @@ -41,7 +41,7 @@ public class SensorDataCyclingPower extends SensorData { return power; } - public SensorDataCycling.CyclingCadence getCadence() { + public SensorDataCyclingCadence getCadence() { return cadence; } diff --git a/src/main/java/de/dennisguse/opentracks/sensors/sensorData/SensorDataSet.java b/src/main/java/de/dennisguse/opentracks/sensors/sensorData/SensorDataSet.java index e054f2c2e..07cb2535f 100644 --- a/src/main/java/de/dennisguse/opentracks/sensors/sensorData/SensorDataSet.java +++ b/src/main/java/de/dennisguse/opentracks/sensors/sensorData/SensorDataSet.java @@ -13,9 +13,9 @@ public final class SensorDataSet { private SensorDataHeartRate heartRate; - private SensorDataCycling.CyclingCadence cyclingCadence; + private SensorDataCyclingCadence cyclingCadence; - private SensorDataCycling.DistanceSpeed cyclingDistanceSpeed; + private SensorDataCyclingDistanceSpeed cyclingDistanceSpeed; private SensorDataCyclingPower cyclingPower; @@ -64,11 +64,11 @@ public final class SensorDataSet { return null; } - public SensorDataCycling.CyclingCadence getCyclingCadence() { + public SensorDataCyclingCadence getCyclingCadence() { return cyclingCadence; } - public SensorDataCycling.DistanceSpeed getCyclingDistanceSpeed() { + public SensorDataCyclingDistanceSpeed getCyclingDistanceSpeed() { return cyclingDistanceSpeed; } @@ -146,12 +146,12 @@ public final class SensorDataSet { return; } - if (type instanceof SensorDataCycling.CyclingCadence) { - this.cyclingCadence = (SensorDataCycling.CyclingCadence) data; + if (type instanceof SensorDataCyclingCadence) { + this.cyclingCadence = (SensorDataCyclingCadence) data; return; } - if (type instanceof SensorDataCycling.DistanceSpeed) { - this.cyclingDistanceSpeed = (SensorDataCycling.DistanceSpeed) data; + if (type instanceof SensorDataCyclingDistanceSpeed) { + this.cyclingDistanceSpeed = (SensorDataCyclingDistanceSpeed) data; return; } From da29766fd0d7b720a9e7553eb18ce672d7588b48 Mon Sep 17 00:00:00 2001 From: Dennis Guse Date: Sun, 4 Sep 2022 19:11:31 +0200 Subject: [PATCH 7/8] BLE: BluetoothConnectionManager supports multiple BLE services per sensor type (e.g., cadence). Part of #1352. --- .../sensors/BluetoothConnectionManager.java | 32 ++++++++++++++----- .../opentracks/sensors/BluetoothUtils.java | 6 ++++ 2 files changed, 30 insertions(+), 8 deletions(-) diff --git a/src/main/java/de/dennisguse/opentracks/sensors/BluetoothConnectionManager.java b/src/main/java/de/dennisguse/opentracks/sensors/BluetoothConnectionManager.java index 823d24c66..20138971c 100644 --- a/src/main/java/de/dennisguse/opentracks/sensors/BluetoothConnectionManager.java +++ b/src/main/java/de/dennisguse/opentracks/sensors/BluetoothConnectionManager.java @@ -30,6 +30,8 @@ import android.util.Log; import androidx.annotation.NonNull; +import java.util.List; + import de.dennisguse.opentracks.data.models.HeartRate; import de.dennisguse.opentracks.sensors.sensorData.SensorData; import de.dennisguse.opentracks.sensors.sensorData.SensorDataHeartRate; @@ -44,7 +46,7 @@ public abstract class BluetoothConnectionManager { private final SensorDataObserver observer; - private final ServiceMeasurementUUID sensor; + private final List serviceMeasurementUUIDs; private BluetoothGatt bluetoothGatt; private final BluetoothGattCallback connectCallback = new BluetoothGattCallback() { @@ -73,15 +75,24 @@ public abstract class BluetoothConnectionManager { @Override public void onServicesDiscovered(@NonNull BluetoothGatt gatt, int status) { - BluetoothGattService service = gatt.getService(sensor.getServiceUUID()); - if (service == null) { - Log.e(TAG, "Could not get service for address=" + gatt.getDevice().getAddress() + " serviceUUID=" + sensor); + BluetoothGattService gattService = null; + ServiceMeasurementUUID serviceMeasurement = null; + for (ServiceMeasurementUUID s : serviceMeasurementUUIDs) { + gattService = gatt.getService(s.getServiceUUID()); + if (gattService != null) { + serviceMeasurement = s; + break; + } + } + + if (gattService == null) { + Log.e(TAG, "Could not get gattService for address=" + gatt.getDevice().getAddress() + " serviceUUID=" + serviceMeasurement); return; } - BluetoothGattCharacteristic characteristic = service.getCharacteristic(sensor.getMeasurementUUID()); + BluetoothGattCharacteristic characteristic = gattService.getCharacteristic(serviceMeasurement.getMeasurementUUID()); if (characteristic == null) { - Log.e(TAG, "Could not get BluetoothCharacteristic for address=" + gatt.getDevice().getAddress() + " serviceUUID=" + sensor + " characteristicUUID=" + sensor.getMeasurementUUID()); + Log.e(TAG, "Could not get BluetoothCharacteristic for address=" + gatt.getDevice().getAddress() + " serviceUUID=" + serviceMeasurement.getServiceUUID() + " characteristicUUID=" + serviceMeasurement.getMeasurementUUID()); return; } gatt.setCharacteristicNotification(characteristic, true); @@ -115,7 +126,12 @@ public abstract class BluetoothConnectionManager { }; BluetoothConnectionManager(ServiceMeasurementUUID serviceUUUID, SensorDataObserver observer) { - this.sensor = serviceUUUID; + this.serviceMeasurementUUIDs = List.of(serviceUUUID); + this.observer = observer; + } + + BluetoothConnectionManager(List serviceUUUID, SensorDataObserver observer) { + this.serviceMeasurementUUIDs = serviceUUUID; this.observer = observer; } @@ -169,7 +185,7 @@ public abstract class BluetoothConnectionManager { public static class HeartRateConnectionManager extends BluetoothConnectionManager { HeartRateConnectionManager(@NonNull SensorDataObserver observer) { - super(BluetoothUtils.HEARTRATE, observer); + super(BluetoothUtils.HEART_RATE_SUPPORTING_DEVICES, observer); } @Override diff --git a/src/main/java/de/dennisguse/opentracks/sensors/BluetoothUtils.java b/src/main/java/de/dennisguse/opentracks/sensors/BluetoothUtils.java index 1450b4842..d94fd0cc4 100644 --- a/src/main/java/de/dennisguse/opentracks/sensors/BluetoothUtils.java +++ b/src/main/java/de/dennisguse/opentracks/sensors/BluetoothUtils.java @@ -52,6 +52,7 @@ public class BluetoothUtils { new UUID(0x2A3700001000L, 0x800000805f9b34fbL) ); + // Used for device discovery in preferences public static final List HEART_RATE_SUPPORTING_DEVICES = Collections.unmodifiableList(Arrays.asList( HEARTRATE, //Devices that support HEART_RATE_SERVICE_UUID, but do not announce HEART_RATE_SERVICE_UUID in there BLE announcement messages (during device discovery). @@ -71,6 +72,11 @@ public class BluetoothUtils { new UUID(0x2A5B00001000L, 0x800000805f9b34fbL) ); + public static final List CYCLING_CADENCE = List.of( + CYCLING_POWER, + CYCLING_SPEED_CADENCE + ); + public static final ServiceMeasurementUUID RUNNING_SPEED_CADENCE = new ServiceMeasurementUUID( new UUID(0x181400001000L, 0x800000805f9b34fbL), new UUID(0x2A5300001000L, 0x800000805f9b34fbL) From a93424629d64e6f6e0fa8f2f891a3de514290571 Mon Sep 17 00:00:00 2001 From: Dennis Guse Date: Sun, 4 Sep 2022 19:53:08 +0200 Subject: [PATCH 8/8] BLE: process cycling cadence from powermeter. Fixes #1352. --- .../sensors/BluetoothUtilsTest.java | 8 ++-- .../sensors/BluetoothConnectionManager.java | 37 +++++++------------ ...oothRemoteSensorManagerCyclingCadence.java | 32 +++++++++++----- ...moteSensorManagerCyclingDistanceSpeed.java | 2 +- ...etoothRemoteSensorManagerCyclingPower.java | 6 +-- ...BluetoothRemoteSensorManagerHeartRate.java | 2 +- ...teSensorManagerRunningSpeedAndCadence.java | 2 +- .../opentracks/sensors/BluetoothUtils.java | 7 ++-- .../sensorData/SensorDataCyclingPower.java | 6 +-- .../BluetoothLeCyclingCadencePreference.java | 32 ++++++++++++++++ ...=> BluetoothLeCyclingSpeedPreference.java} | 10 ++--- src/main/res/xml/settings_sensors.xml | 4 +- 12 files changed, 92 insertions(+), 56 deletions(-) create mode 100644 src/main/java/de/dennisguse/opentracks/settings/bluetooth/BluetoothLeCyclingCadencePreference.java rename src/main/java/de/dennisguse/opentracks/settings/bluetooth/{BluetoothLeCyclingCadenceAndSpeedPreference.java => BluetoothLeCyclingSpeedPreference.java} (56%) diff --git a/src/androidTest/java/de/dennisguse/opentracks/sensors/BluetoothUtilsTest.java b/src/androidTest/java/de/dennisguse/opentracks/sensors/BluetoothUtilsTest.java index 4cad4a8d9..fd0f00014 100644 --- a/src/androidTest/java/de/dennisguse/opentracks/sensors/BluetoothUtilsTest.java +++ b/src/androidTest/java/de/dennisguse/opentracks/sensors/BluetoothUtilsTest.java @@ -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()); diff --git a/src/main/java/de/dennisguse/opentracks/sensors/BluetoothConnectionManager.java b/src/main/java/de/dennisguse/opentracks/sensors/BluetoothConnectionManager.java index 20138971c..dc1d55541 100644 --- a/src/main/java/de/dennisguse/opentracks/sensors/BluetoothConnectionManager.java +++ b/src/main/java/de/dennisguse/opentracks/sensors/BluetoothConnectionManager.java @@ -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 { @Override public void onCharacteristicChanged(BluetoothGatt gatt, @NonNull BluetoothGattCharacteristic characteristic) { - Log.d(TAG, "Received data from " + gatt.getDevice().getAddress()); - SensorData 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 = serviceMeasurementUUIDs.stream() + .filter(s -> s.getServiceUUID().equals(characteristic.getService().getUuid())).findFirst(); + if (serviceMeasurementUUID.isEmpty()) { + Log.e(TAG, "Unknown service UUID; not supported?"); + return; + } + + SensorData 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 { /** * @return null if data could not be parsed. */ - protected abstract SensorData parsePayload(String sensorName, String address, BluetoothGattCharacteristic characteristic); - - public static class HeartRateConnectionManager extends BluetoothConnectionManager { - - 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 parsePayload(@NonNull ServiceMeasurementUUID serviceMeasurementUUID, String sensorName, String address, @NonNull BluetoothGattCharacteristic characteristic); interface SensorDataObserver { diff --git a/src/main/java/de/dennisguse/opentracks/sensors/BluetoothRemoteSensorManagerCyclingCadence.java b/src/main/java/de/dennisguse/opentracks/sensors/BluetoothRemoteSensorManagerCyclingCadence.java index baf29226c..b6a2cb9af 100644 --- a/src/main/java/de/dennisguse/opentracks/sensors/BluetoothRemoteSensorManagerCyclingCadence.java +++ b/src/main/java/de/dennisguse/opentracks/sensors/BluetoothRemoteSensorManagerCyclingCadence.java @@ -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 { + 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; } } diff --git a/src/main/java/de/dennisguse/opentracks/sensors/BluetoothRemoteSensorManagerCyclingDistanceSpeed.java b/src/main/java/de/dennisguse/opentracks/sensors/BluetoothRemoteSensorManagerCyclingDistanceSpeed.java index 3abd42bee..e6bfba003 100644 --- a/src/main/java/de/dennisguse/opentracks/sensors/BluetoothRemoteSensorManagerCyclingDistanceSpeed.java +++ b/src/main/java/de/dennisguse/opentracks/sensors/BluetoothRemoteSensorManagerCyclingDistanceSpeed.java @@ -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; diff --git a/src/main/java/de/dennisguse/opentracks/sensors/BluetoothRemoteSensorManagerCyclingPower.java b/src/main/java/de/dennisguse/opentracks/sensors/BluetoothRemoteSensorManagerCyclingPower.java index 6efda9ade..f94b8bf76 100644 --- a/src/main/java/de/dennisguse/opentracks/sensors/BluetoothRemoteSensorManagerCyclingPower.java +++ b/src/main/java/de/dennisguse/opentracks/sensors/BluetoothRemoteSensorManagerCyclingPower.java @@ -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; } } diff --git a/src/main/java/de/dennisguse/opentracks/sensors/BluetoothRemoteSensorManagerHeartRate.java b/src/main/java/de/dennisguse/opentracks/sensors/BluetoothRemoteSensorManagerHeartRate.java index 2aef346d9..54b2b317c 100644 --- a/src/main/java/de/dennisguse/opentracks/sensors/BluetoothRemoteSensorManagerHeartRate.java +++ b/src/main/java/de/dennisguse/opentracks/sensors/BluetoothRemoteSensorManagerHeartRate.java @@ -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; diff --git a/src/main/java/de/dennisguse/opentracks/sensors/BluetoothRemoteSensorManagerRunningSpeedAndCadence.java b/src/main/java/de/dennisguse/opentracks/sensors/BluetoothRemoteSensorManagerRunningSpeedAndCadence.java index 20d5e063d..73b11b38c 100644 --- a/src/main/java/de/dennisguse/opentracks/sensors/BluetoothRemoteSensorManagerRunningSpeedAndCadence.java +++ b/src/main/java/de/dennisguse/opentracks/sensors/BluetoothRemoteSensorManagerRunningSpeedAndCadence.java @@ -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); } } diff --git a/src/main/java/de/dennisguse/opentracks/sensors/BluetoothUtils.java b/src/main/java/de/dennisguse/opentracks/sensors/BluetoothUtils.java index d94fd0cc4..41c99b2af 100644 --- a/src/main/java/de/dennisguse/opentracks/sensors/BluetoothUtils.java +++ b/src/main/java/de/dennisguse/opentracks/sensors/BluetoothUtils.java @@ -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) { diff --git a/src/main/java/de/dennisguse/opentracks/sensors/sensorData/SensorDataCyclingPower.java b/src/main/java/de/dennisguse/opentracks/sensors/sensorData/SensorDataCyclingPower.java index ba81d789d..7f0a512f0 100644 --- a/src/main/java/de/dennisguse/opentracks/sensors/sensorData/SensorDataCyclingPower.java +++ b/src/main/java/de/dennisguse/opentracks/sensors/sensorData/SensorDataCyclingPower.java @@ -29,15 +29,15 @@ public class SensorDataCyclingPower extends SensorData { } 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; } diff --git a/src/main/java/de/dennisguse/opentracks/settings/bluetooth/BluetoothLeCyclingCadencePreference.java b/src/main/java/de/dennisguse/opentracks/settings/bluetooth/BluetoothLeCyclingCadencePreference.java new file mode 100644 index 000000000..bf9577621 --- /dev/null +++ b/src/main/java/de/dennisguse/opentracks/settings/bluetooth/BluetoothLeCyclingCadencePreference.java @@ -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); + } +} diff --git a/src/main/java/de/dennisguse/opentracks/settings/bluetooth/BluetoothLeCyclingCadenceAndSpeedPreference.java b/src/main/java/de/dennisguse/opentracks/settings/bluetooth/BluetoothLeCyclingSpeedPreference.java similarity index 56% rename from src/main/java/de/dennisguse/opentracks/settings/bluetooth/BluetoothLeCyclingCadenceAndSpeedPreference.java rename to src/main/java/de/dennisguse/opentracks/settings/bluetooth/BluetoothLeCyclingSpeedPreference.java index a4e234ead..db918d97d 100644 --- a/src/main/java/de/dennisguse/opentracks/settings/bluetooth/BluetoothLeCyclingCadenceAndSpeedPreference.java +++ b/src/main/java/de/dennisguse/opentracks/settings/bluetooth/BluetoothLeCyclingSpeedPreference.java @@ -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); } diff --git a/src/main/res/xml/settings_sensors.xml b/src/main/res/xml/settings_sensors.xml index 4d40b595c..23a4be516 100644 --- a/src/main/res/xml/settings_sensors.xml +++ b/src/main/res/xml/settings_sensors.xml @@ -12,7 +12,7 @@ - @@ -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" /> -