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 01a5a8828..fd0f00014 100644 --- a/src/androidTest/java/de/dennisguse/opentracks/sensors/BluetoothUtilsTest.java +++ b/src/androidTest/java/de/dennisguse/opentracks/sensors/BluetoothUtilsTest.java @@ -10,7 +10,8 @@ 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; public class BluetoothUtilsTest { @@ -18,7 +19,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 +32,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,11 +44,11 @@ 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 - SensorDataCycling.CadenceAndSpeed sensor = BluetoothUtils.parseCyclingCrankAndWheel("address", "sensorName", characteristic); + SensorDataCyclingCadenceAndDistanceSpeed sensor = BluetoothUtils.parseCyclingCrankAndWheel("address", "sensorName", characteristic); // then assertNull(sensor.getDistanceSpeed()); @@ -56,11 +57,11 @@ 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 - SensorDataCycling.CadenceAndSpeed sensor = BluetoothUtils.parseCyclingCrankAndWheel("address", "sensorName", characteristic); + SensorDataCyclingCadenceAndDistanceSpeed sensor = BluetoothUtils.parseCyclingCrankAndWheel("address", "sensorName", characteristic); // then assertEquals(65535 + 16777216, sensor.getDistanceSpeed().getWheelRevolutionsCount()); @@ -69,11 +70,11 @@ 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 - SensorDataCycling.CadenceAndSpeed sensor = BluetoothUtils.parseCyclingCrankAndWheel("address", "sensorName", characteristic); + SensorDataCyclingCadenceAndDistanceSpeed sensor = BluetoothUtils.parseCyclingCrankAndWheel("address", "sensorName", characteristic); // then assertEquals(200 + 16777216, sensor.getDistanceSpeed().getWheelRevolutionsCount()); @@ -82,19 +83,34 @@ 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 - int power_w = BluetoothUtils.parseCyclingPower(characteristic); + SensorDataCyclingPower.Data powerCadence = BluetoothUtils.parseCyclingPower("", "", characteristic); // then - assertEquals(40, power_w); + assertEquals(40, powerCadence.getPower().getValue().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().getValue().getW(), 0.01); + + assertEquals(12, powerCadence.getCadence().getCrankRevolutionsCount()); + assertEquals(17125, powerCadence.getCadence().getCrankRevolutionsTime()); } @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/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/BluetoothConnectionManager.java b/src/main/java/de/dennisguse/opentracks/sensors/BluetoothConnectionManager.java index e4987c19b..dc1d55541 100644 --- a/src/main/java/de/dennisguse/opentracks/sensors/BluetoothConnectionManager.java +++ b/src/main/java/de/dennisguse/opentracks/sensors/BluetoothConnectionManager.java @@ -30,16 +30,11 @@ 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.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. @@ -51,8 +46,7 @@ public abstract class BluetoothConnectionManager { private final SensorDataObserver observer; - private final UUID serviceUUUID; - private final UUID measurementUUID; + private final List serviceMeasurementUUIDs; private BluetoothGatt bluetoothGatt; private final BluetoothGattCallback connectCallback = new BluetoothGattCallback() { @@ -81,15 +75,24 @@ public abstract class BluetoothConnectionManager { @Override public void onServicesDiscovered(@NonNull BluetoothGatt gatt, int status) { - BluetoothGattService service = gatt.getService(serviceUUUID); - if (service == null) { - Log.e(TAG, "Could not get service for address=" + gatt.getDevice().getAddress() + " serviceUUID=" + serviceUUUID); + 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(measurementUUID); + BluetoothGattCharacteristic characteristic = gattService.getCharacteristic(serviceMeasurement.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=" + serviceMeasurement.getServiceUUID() + " characteristicUUID=" + serviceMeasurement.getMeasurementUUID()); return; } gatt.setCharacteristicNotification(characteristic, true); @@ -108,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) { @@ -122,15 +133,20 @@ public abstract class BluetoothConnectionManager { } }; - BluetoothConnectionManager(UUID serviceUUUID, UUID measurementUUID, SensorDataObserver observer) { - this.serviceUUUID = serviceUUUID; - this.measurementUUID = measurementUUID; + BluetoothConnectionManager(ServiceMeasurementUUID serviceUUUID, SensorDataObserver observer) { + this.serviceMeasurementUUIDs = List.of(serviceUUUID); + this.observer = observer; + } + + BluetoothConnectionManager(List serviceUUUID, SensorDataObserver observer) { + this.serviceMeasurementUUIDs = serviceUUUID; this.observer = observer; } synchronized void connect(Context context, @NonNull BluetoothDevice device) { if (bluetoothGatt != null) { Log.w(TAG, "Already connected; ignoring."); + return; } Log.d(TAG, "Connecting to: " + device); @@ -172,114 +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_SERVICE_UUID, BluetoothUtils.HEART_RATE_MEASUREMENT_CHAR_UUID, 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; - } - } - - public static class CyclingCadence extends BluetoothConnectionManager { - - CyclingCadence(SensorDataObserver observer) { - super(BluetoothUtils.CYCLING_SPEED_CADENCE_SERVICE_UUID, BluetoothUtils.CYCLING_SPEED_CADENCE_MEASUREMENT_CHAR_UUID, 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_SERVICE_UUID, BluetoothUtils.CYCLING_SPEED_CADENCE_MEASUREMENT_CHAR_UUID, 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_UUID, BluetoothUtils.CYCLING_POWER_MEASUREMENT_CHAR_UUID, observer); - } - - @Override - protected SensorDataCyclingPower createEmptySensorData(String address) { - return new SensorDataCyclingPower(address); - } - - @Override - protected SensorDataCyclingPower parsePayload(String sensorName, String address, BluetoothGattCharacteristic characteristic) { - Integer cyclingPower = BluetoothUtils.parseCyclingPower(characteristic); - - return cyclingPower != null ? new SensorDataCyclingPower(address, sensorName, Power.of(cyclingPower)) : null; - } - } - - 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); - } - - @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); - } - } + 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/BluetoothRemoteSensorManager.java b/src/main/java/de/dennisguse/opentracks/sensors/BluetoothRemoteSensorManager.java index a5c5aa388..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; @@ -64,11 +65,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(); @@ -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 new file mode 100644 index 000000000..b6a2cb9af --- /dev/null +++ b/src/main/java/de/dennisguse/opentracks/sensors/BluetoothRemoteSensorManagerCyclingCadence.java @@ -0,0 +1,47 @@ +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_CADENCE, observer); + } + + @Override + protected SensorDataCyclingCadence createEmptySensorData(String address) { + return new SensorDataCyclingCadence(address); + } + + @Override + 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 new file mode 100644 index 000000000..e6bfba003 --- /dev/null +++ b/src/main/java/de/dennisguse/opentracks/sensors/BluetoothRemoteSensorManagerCyclingDistanceSpeed.java @@ -0,0 +1,32 @@ +package de.dennisguse.opentracks.sensors; + +import android.bluetooth.BluetoothGattCharacteristic; + +import de.dennisguse.opentracks.sensors.sensorData.SensorDataCyclingCadenceAndDistanceSpeed; +import de.dennisguse.opentracks.sensors.sensorData.SensorDataCyclingDistanceSpeed; + +public class BluetoothRemoteSensorManagerCyclingDistanceSpeed extends BluetoothConnectionManager { + + BluetoothRemoteSensorManagerCyclingDistanceSpeed(SensorDataObserver observer) { + super(BluetoothUtils.CYCLING_SPEED_CADENCE, observer); + } + + @Override + protected SensorDataCyclingDistanceSpeed createEmptySensorData(String address) { + return new SensorDataCyclingDistanceSpeed(address); + } + + @Override + protected SensorDataCyclingDistanceSpeed parsePayload(ServiceMeasurementUUID serviceMeasurementUUID, String sensorName, String address, BluetoothGattCharacteristic characteristic) { + SensorDataCyclingCadenceAndDistanceSpeed 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..f94b8bf76 --- /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(@NonNull ServiceMeasurementUUID serviceMeasurementUUID, String sensorName, String address, BluetoothGattCharacteristic characteristic) { + SensorDataCyclingPower.Data cyclingPower = BluetoothUtils.parseCyclingPower(address, sensorName, characteristic); + + 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 new file mode 100644 index 000000000..54b2b317c --- /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(@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 new file mode 100644 index 000000000..73b11b38c --- /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(@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 df4c687e1..41c99b2af 100644 --- a/src/main/java/de/dennisguse/opentracks/sensors/BluetoothUtils.java +++ b/src/main/java/de/dennisguse/opentracks/sensors/BluetoothUtils.java @@ -30,8 +30,12 @@ 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.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; /** @@ -43,23 +47,40 @@ 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, + // 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). - 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 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) + ); private static final String TAG = BluetoothUtils.class.getSimpleName(); @@ -98,17 +119,49 @@ public class BluetoothUtils { return null; } - public static Integer 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 < 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; + } + + 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 SensorDataCyclingCadence(address, sensorName, crankCount, crankTime); + } + + + return new SensorDataCyclingPower.Data(new SensorDataCyclingPower(sensorName, address, 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) { @@ -120,25 +173,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/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/sensors/sensorData/SensorDataCycling.java b/src/main/java/de/dennisguse/opentracks/sensors/sensorData/SensorDataCycling.java deleted file mode 100644 index 1d3ad400b..000000000 --- a/src/main/java/de/dennisguse/opentracks/sensors/sensorData/SensorDataCycling.java +++ /dev/null @@ -1,255 +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; - } - - /** - * 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; - } - - 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 dae106132..7f0a512f0 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 SensorDataCyclingPower power; + private final SensorDataCyclingCadence cadence; + + public Data(SensorDataCyclingPower power, @Nullable SensorDataCyclingCadence cadence) { + this.power = power; + this.cadence = cadence; + } + + public SensorDataCyclingPower getPower() { + return power; + } + + public SensorDataCyclingCadence getCadence() { + return cadence; + } + + @NonNull + @Override + public String toString() { + return "Data{" + + "power=" + power + + ", cadence=" + 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; } 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 deleted file mode 100644 index aba91ba79..000000000 --- a/src/main/java/de/dennisguse/opentracks/settings/bluetooth/BluetoothLeCyclingCadenceAndSpeedPreference.java +++ /dev/null @@ -1,23 +0,0 @@ -package de.dennisguse.opentracks.settings.bluetooth; - -import android.content.Context; -import android.util.AttributeSet; - -public class BluetoothLeCyclingCadenceAndSpeedPreference extends BluetoothLeSensorPreference { - - public BluetoothLeCyclingCadenceAndSpeedPreference(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) { - super(context, attrs, defStyleAttr, defStyleRes); - } - - public BluetoothLeCyclingCadenceAndSpeedPreference(Context context, AttributeSet attrs, int defStyleAttr) { - super(context, attrs, defStyleAttr); - } - - public BluetoothLeCyclingCadenceAndSpeedPreference(Context context, AttributeSet attrs) { - super(context, attrs); - } - - public BluetoothLeCyclingCadenceAndSpeedPreference(Context context) { - super(context); - } -} 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/BluetoothLeCyclingPowerPreference.java b/src/main/java/de/dennisguse/opentracks/settings/bluetooth/BluetoothLeCyclingPowerPreference.java index 486136bea..af020ce07 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); + } } \ No newline at end of file diff --git a/src/main/java/de/dennisguse/opentracks/settings/bluetooth/BluetoothLeCyclingSpeedPreference.java b/src/main/java/de/dennisguse/opentracks/settings/bluetooth/BluetoothLeCyclingSpeedPreference.java new file mode 100644 index 000000000..db918d97d --- /dev/null +++ b/src/main/java/de/dennisguse/opentracks/settings/bluetooth/BluetoothLeCyclingSpeedPreference.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 BluetoothLeCyclingSpeedPreference extends BluetoothLeSensorPreference { + + public BluetoothLeCyclingSpeedPreference(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) { + super(context, attrs, defStyleAttr, defStyleRes); + } + + public BluetoothLeCyclingSpeedPreference(Context context, AttributeSet attrs, int defStyleAttr) { + super(context, attrs, defStyleAttr); + } + + public BluetoothLeCyclingSpeedPreference(Context context, AttributeSet attrs) { + super(context, attrs); + } + + public BluetoothLeCyclingSpeedPreference(Context context) { + super(context); + } + + @Override + public PreferenceDialogFragmentCompat createInstance() { + return BluetoothLeSensorPreference.BluetoothLeSensorPreferenceDialog.newInstance(getKey(), BluetoothUtils.CYCLING_SPEED_CADENCE); + } +} 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..856c79b92 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_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 c3d759eb1..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; @@ -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; @@ -123,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; } 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" /> -