diff --git a/build.gradle b/build.gradle
index 89c375594..0b84b40c8 100644
--- a/build.gradle
+++ b/build.gradle
@@ -69,7 +69,7 @@ android {
buildConfigField "String", "VERSION_NAME_FULL", "\"${getVersionName()}\""
- minSdk 24
+ minSdk 26
targetSdk 34
testInstrumentationRunner "de.dennisguse.opentracks.TestRunner"
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 7065be17d..97b61dda0 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
@@ -48,6 +48,7 @@ import de.dennisguse.opentracks.TimezoneRule;
import de.dennisguse.opentracks.content.data.TestDataUtil;
import de.dennisguse.opentracks.data.ContentProviderUtils;
import de.dennisguse.opentracks.data.models.ActivityType;
+import de.dennisguse.opentracks.data.models.AltitudeGainLoss;
import de.dennisguse.opentracks.data.models.Cadence;
import de.dennisguse.opentracks.data.models.Distance;
import de.dennisguse.opentracks.data.models.HeartRate;
@@ -58,11 +59,12 @@ import de.dennisguse.opentracks.data.models.Track;
import de.dennisguse.opentracks.data.models.TrackPoint;
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.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.Aggregator;
+import de.dennisguse.opentracks.sensors.sensorData.AggregatorBarometer;
+import de.dennisguse.opentracks.sensors.sensorData.AggregatorCyclingCadence;
+import de.dennisguse.opentracks.sensors.sensorData.AggregatorCyclingDistanceSpeed;
+import de.dennisguse.opentracks.sensors.sensorData.AggregatorCyclingPower;
+import de.dennisguse.opentracks.sensors.sensorData.AggregatorHeartRate;
import de.dennisguse.opentracks.sensors.sensorData.SensorDataSet;
import de.dennisguse.opentracks.services.TrackRecordingService;
import de.dennisguse.opentracks.services.handlers.TrackPointCreator;
@@ -71,14 +73,12 @@ import de.dennisguse.opentracks.stats.TrackStatistics;
/**
* Export a track to {@link TrackFileFormat} and verify that the import is identical.
*
- * Note: those tests are affected by {@link de.dennisguse.opentracks.sensors.sensorData.SensorData}.isRecent().
+ * Note: those tests are affected by {@link Aggregator}.isRecent().
* If the test device is too slow (like in a CI) these are likely to fail as the sensor data will be omitted from actual.
*/
@RunWith(AndroidJUnit4.class)
public class ExportImportTest {
- private static final String TAG = ExportImportTest.class.getSimpleName();
-
@Rule
public final ServiceTestRule mServiceRule = ServiceTestRule.withTimeout(5, TimeUnit.SECONDS);
@@ -139,23 +139,22 @@ public class ExportImportTest {
Distance sensorDistance = Distance.of(10); // recording distance interval
- sendLocation(trackPointCreator, "2020-02-02T02:02:03Z", 3, 14, 10, 13, 15, 10, 1);
+ sendLocation(trackPointCreator, "2020-02-02T02:02:03Z", 3, 14, 10, 13, 15, 10, 1f);
service.insertMarker("Marker 1", "Marker 1 category", "Marker 1 desc", null);
// A sensor-only TrackPoint
trackPointCreator.setClock("2020-02-02T02:02:04Z");
- mockAltitudeChange(trackPointCreator, 1);
- mockBLESensorData(trackPointCreator, 15f, sensorDistance, 66f, 3f, 50f);
+ mockSensorData(trackPointCreator, 15f, sensorDistance, 66f, 3f, 50f, 1f);
trackPointCreator.setClock("2020-02-02T02:02:14Z");
- mockBLESensorData(trackPointCreator, 15f, null, 67f, 3f, 50f);
+ mockSensorData(trackPointCreator, 15f, null, 67f, 3f, 50f, null);
trackPointCreator.setClock("2020-02-02T02:02:15Z");
- mockBLESensorData(trackPointCreator, null, null, 68f, 3f, 50f);
+ mockSensorData(trackPointCreator, null, null, 68f, 3f, 50f, null);
trackPointCreator.setClock("2020-02-02T02:02:16Z");
- mockBLESensorData(trackPointCreator, 5f, Distance.of(2), 69f, 3f, 50f); // Distance will be added to next TrackPoint
+ mockSensorData(trackPointCreator, 5f, Distance.of(2), 69f, 3f, 50f, null); // Distance will be added to next TrackPoint
- sendLocation(trackPointCreator, "2020-02-02T02:02:17Z", 3, 14.001, 10, 13, 15, 10, 0);
+ sendLocation(trackPointCreator, "2020-02-02T02:02:17Z", 3, 14.001, 10, 13, 15, 10, 0f);
service.insertMarker("Marker 2", "Marker 2 category", "Marker 2 desc", null);
trackPointCreator.setClock("2020-02-02T02:02:18Z");
@@ -165,15 +164,16 @@ public class ExportImportTest {
trackPointCreator.setClock("2020-02-02T02:03:20Z");
service.resumeTrack(trackId);
- sendLocation(trackPointCreator, "2020-02-02T02:03:21Z", 3, 14.002, 10, 13, 15, 10, 0);
+ sendLocation(trackPointCreator, "2020-02-02T02:03:21Z", 3, 14.002, 10, 13, 15, 10, 0f);
- sendLocation(trackPointCreator, "2020-02-02T02:03:22Z", 3, 16, 10, 13, 15, 10, 0);
+ sendLocation(trackPointCreator, "2020-02-02T02:03:22Z", 3, 16, 10, 13, 15, 10, 0f);
trackPointCreator.setClock("2020-02-02T02:03:30Z");
service.getTrackRecordingManager().onIdle();
- sendLocation(trackPointCreator, "2020-02-02T02:03:50Z", 3, 16.001, 10, 27, 15, 10, 0);
+ sendLocation(trackPointCreator, "2020-02-02T02:03:50Z", 3, 16.001, 10, 27, 15, 10, 0f);
+ trackPointCreator.getSensorManager().sensorDataSet = new SensorDataSet();
trackPointCreator.setClock("2020-02-02T02:04:00Z");
service.endCurrentTrack();
@@ -271,7 +271,9 @@ public class ExportImportTest {
.setAltitudeLoss(0f)
.setAltitudeGain(0f)
.setHorizontalAccuracy(Distance.of(10)),
- new TrackPoint(TrackPoint.Type.IDLE, Instant.parse("2020-02-02T02:03:30Z")),
+ new TrackPoint(TrackPoint.Type.IDLE, Instant.parse("2020-02-02T02:03:30Z"))
+ .setAltitudeLoss(0f)
+ .setAltitudeGain(0f),
new TrackPoint(TrackPoint.Type.TRACKPOINT, Instant.parse("2020-02-02T02:03:50Z"))
.setLatitude(3)
.setLongitude(16.001)
@@ -281,7 +283,6 @@ public class ExportImportTest {
.setAltitudeGain(0f)
.setHorizontalAccuracy(Distance.of(10)),
new TrackPoint(TrackPoint.Type.SEGMENT_END_MANUAL, Instant.parse("2020-02-02T02:04:00Z"))
-
), actual);
}
@@ -545,40 +546,51 @@ public class ExportImportTest {
}
}
- private void mockBLESensorData(TrackPointCreator trackPointCreator, Float speed, Distance distance, float heartRate, float cadence, Float power) {
+ private void mockSensorData(TrackPointCreator trackPointCreator, Float speed, Distance distance, float heartRate, float cadence, Float power, Float altitudeGain) {
+ SensorDataSet sensorDataSet = trackPointCreator.getSensorManager().sensorDataSet;
- SensorDataSet sensorDataSet = new SensorDataSet();
- sensorDataSet.set(new SensorDataCyclingPower("power", "power", Power.of(power)));
- sensorDataSet.set(new SensorDataHeartRate("heartRate", "heartRate", HeartRate.of(heartRate)));
+ AggregatorCyclingPower cyclingPower = Mockito.mock(AggregatorCyclingPower.class);
+ Mockito.when(cyclingPower.hasValue()).thenReturn(true);
+ Mockito.when(cyclingPower.getValue()).thenReturn(Power.of(power));
+ sensorDataSet.add(cyclingPower);
- SensorDataCyclingCadence cyclingCadence = Mockito.mock(SensorDataCyclingCadence.class);
+ AggregatorHeartRate agheartRate = Mockito.mock(AggregatorHeartRate.class);
+ Mockito.when(agheartRate.getValue()).thenReturn(HeartRate.of(heartRate));
+ sensorDataSet.add(agheartRate);
+
+ AggregatorCyclingCadence cyclingCadence = Mockito.mock(AggregatorCyclingCadence.class);
Mockito.when(cyclingCadence.hasValue()).thenReturn(true);
Mockito.when(cyclingCadence.getValue()).thenReturn(Cadence.of(cadence));
- sensorDataSet.set(cyclingCadence);
+ sensorDataSet.add(cyclingCadence);
if (distance != null && speed != null) {
- SensorDataCyclingDistanceSpeed.Data distanceSpeedData = Mockito.mock(SensorDataCyclingDistanceSpeed.Data.class);
- Mockito.when(distanceSpeedData.getDistanceOverall()).thenReturn(distance);
- Mockito.when(distanceSpeedData.getSpeed()).thenReturn(Speed.of(speed));
- SensorDataCyclingDistanceSpeed distanceSpeed = Mockito.mock(SensorDataCyclingDistanceSpeed.class);
+ AggregatorCyclingDistanceSpeed distanceSpeed = Mockito.mock(AggregatorCyclingDistanceSpeed.class);
Mockito.when(distanceSpeed.hasValue()).thenReturn(true);
- Mockito.when(distanceSpeed.getValue()).thenReturn(distanceSpeedData);
- sensorDataSet.set(distanceSpeed);
+ Mockito.when(distanceSpeed.getValue()).thenReturn(new AggregatorCyclingDistanceSpeed.Data(null, distance, Speed.of(speed)));
+ sensorDataSet.add(distanceSpeed);
+ } else {
+ sensorDataSet.add(new AggregatorCyclingDistanceSpeed("", ""));
}
- trackPointCreator.getSensorManager().sensorDataSet = sensorDataSet;
+ mockAltitudeChange(trackPointCreator, altitudeGain);
+
trackPointCreator.onChange(sensorDataSet);
-
- //TODO Might require: getSensorManager().sensorDataSet = new SensorDataSet()
}
- private void mockAltitudeChange(TrackPointCreator trackPointCreator, float altitudeGain) {
- AltitudeSumManager altitudeSumManager = trackPointCreator.getSensorManager().getAltitudeSumManager();
- altitudeSumManager.setAltitudeGain_m(altitudeGain);
- altitudeSumManager.setAltitudeLoss_m(altitudeGain);
+ private void mockAltitudeChange(TrackPointCreator trackPointCreator, Float altitudeGain) {
+ SensorDataSet sensorDataSet = trackPointCreator.getSensorManager().sensorDataSet;
+
+ if (altitudeGain != null) {
+ AggregatorBarometer barometer = Mockito.mock(AggregatorBarometer.class);
+ Mockito.when(barometer.hasValue()).thenReturn(true);
+ Mockito.when(barometer.getValue()).thenReturn(new AltitudeGainLoss(altitudeGain, altitudeGain));
+ sensorDataSet.add(barometer);
+ } else {
+ sensorDataSet.add(new AggregatorBarometer(""));
+ }
}
- private void sendLocation(TrackPointCreator trackPointCreator, String time, double latitude, double longitude, float accuracy, float verticalAccuracy, float speed, float altitude, float altitudeGain) {
+ private void sendLocation(TrackPointCreator trackPointCreator, String time, double latitude, double longitude, float accuracy, float verticalAccuracy, float speed, float altitude, Float altitudeGain) {
Location location = new Location("mock");
location.setLatitude(latitude);
location.setLongitude(longitude);
diff --git a/src/androidTest/java/de/dennisguse/opentracks/sensors/AltitudeSumManagerTest.java b/src/androidTest/java/de/dennisguse/opentracks/sensors/AltitudeSumManagerTest.java
deleted file mode 100644
index 557d3ff71..000000000
--- a/src/androidTest/java/de/dennisguse/opentracks/sensors/AltitudeSumManagerTest.java
+++ /dev/null
@@ -1,58 +0,0 @@
-package de.dennisguse.opentracks.sensors;
-
-import static org.mockito.Mockito.when;
-
-import org.junit.Assert;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-import org.mockito.Mock;
-import org.mockito.junit.MockitoJUnitRunner;
-
-import de.dennisguse.opentracks.data.models.AtmosphericPressure;
-import de.dennisguse.opentracks.sensors.driver.BarometerInternal;
-
-/**
- * NOTE: Test data is completely artificial.
- */
-@RunWith(MockitoJUnitRunner.class)
-public class AltitudeSumManagerTest {
-
- private static void addSensorValue(AltitudeSumManager altitudeSumManager, float[] values) {
- for (float f : values) {
- altitudeSumManager.onSensorValueChanged(AtmosphericPressure.ofHPA(f));
- }
- }
-
- @Mock
- BarometerInternal driver;
-
- @Test
- public void getAltitudeGainLoss_downhill() {
- // given
- AltitudeSumManager subject = new AltitudeSumManager(driver);
-
- when(driver.isConnected()).thenReturn(true);
-
- // then
- addSensorValue(subject, new float[]{1015f, 1015.01f, 1015.02f, 1015.03f, 1015.04f, 1015.05f, 1015.06f, 1015.07f, 1015.08f, 1015.09f, 1015.10f, 1015.11f, 1015.12f, 1015.13f, 1018f, 1018.1f, 1018.1f, 1018.1f, 1018.1f});
-
- // then
- Assert.assertEquals(0f, subject.getAltitudeGain_m(), 0.01);
- Assert.assertEquals(15f, subject.getAltitudeLoss_m(), 0.01);
- }
-
- @Test
- public void sensorUnavailable() {
- // given
- AltitudeSumManager subject = new AltitudeSumManager(driver);
-
- when(driver.isConnected()).thenReturn(true);
-
- // then
- subject.onSensorValueChanged(AtmosphericPressure.ofHPA(999f));
-
- // then
- Assert.assertNull(subject.getAltitudeGain_m());
- Assert.assertNull(subject.getAltitudeLoss_m());
- }
-}
\ No newline at end of file
diff --git a/src/androidTest/java/de/dennisguse/opentracks/sensors/BluetoothHandlerBarometricPressureTest.java b/src/androidTest/java/de/dennisguse/opentracks/sensors/BluetoothHandlerBarometricPressureTest.java
new file mode 100644
index 000000000..03f513ff6
--- /dev/null
+++ b/src/androidTest/java/de/dennisguse/opentracks/sensors/BluetoothHandlerBarometricPressureTest.java
@@ -0,0 +1,25 @@
+package de.dennisguse.opentracks.sensors;
+
+import static org.junit.Assert.assertEquals;
+
+import android.bluetooth.BluetoothGattCharacteristic;
+
+import org.junit.Test;
+
+import de.dennisguse.opentracks.data.models.AtmosphericPressure;
+
+public class BluetoothHandlerBarometricPressureTest {
+
+ @Test
+ public void parseEnvironmentalSensing_Pa() {
+ // given
+ BluetoothGattCharacteristic characteristic = new BluetoothGattCharacteristic(BluetoothHandlerBarometricPressure.BAROMETRIC_PRESSURE.serviceUUID(), 0, 0);
+ characteristic.setValue(new byte[]{(byte) 0xB2, (byte) 0x48, (byte) 0x0F, (byte) 0x00});
+
+ // when
+ AtmosphericPressure pressure = BluetoothHandlerBarometricPressure.parseEnvironmentalSensing(characteristic);
+
+ // then
+ assertEquals(AtmosphericPressure.ofPA(100165), pressure);
+ }
+}
\ No newline at end of file
diff --git a/src/androidTest/java/de/dennisguse/opentracks/sensors/BluetoothHandlerCyclingDistanceSpeedTest.java b/src/androidTest/java/de/dennisguse/opentracks/sensors/BluetoothHandlerCyclingDistanceSpeedTest.java
new file mode 100644
index 000000000..58bbbfacd
--- /dev/null
+++ b/src/androidTest/java/de/dennisguse/opentracks/sensors/BluetoothHandlerCyclingDistanceSpeedTest.java
@@ -0,0 +1,51 @@
+package de.dennisguse.opentracks.sensors;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNull;
+
+import android.bluetooth.BluetoothGattCharacteristic;
+import android.util.Pair;
+
+import org.junit.Test;
+
+public class BluetoothHandlerCyclingDistanceSpeedTest {
+ @Test
+ public void parseCyclingSpeedCadence_crankOnly() {
+ BluetoothGattCharacteristic characteristic = new BluetoothGattCharacteristic(BluetoothHandlerCyclingDistanceSpeed.CYCLING_SPEED_CADENCE.serviceUUID(), 0, 0);
+ characteristic.setValue(new byte[]{0x02, (byte) 0xC8, 0x00, 0x00, 0x00, 0x06, (byte) 0x99});
+
+ // when
+ Pair sensor = BluetoothHandlerCyclingDistanceSpeed.parseCyclingCrankAndWheel("address", "sensorName", characteristic);
+
+ // then
+ assertNull(sensor.first);
+ assertEquals(200, sensor.second.crankRevolutionsCount());
+ }
+
+ @Test
+ public void parseCyclingSpeedCadence_wheelOnly() {
+ BluetoothGattCharacteristic characteristic = new BluetoothGattCharacteristic(BluetoothHandlerCyclingDistanceSpeed.CYCLING_SPEED_CADENCE.serviceUUID(), 0, 0);
+ characteristic.setValue(new byte[]{0x01, (byte) 0xFF, (byte) 0xFF, 0, 1, 0x45, (byte) 0x99});
+
+ // when
+ Pair sensor = BluetoothHandlerCyclingDistanceSpeed.parseCyclingCrankAndWheel("address", "sensorName", characteristic);
+
+ // then
+ assertEquals(65535 + 16777216, sensor.first.wheelRevolutionsCount());
+ assertNull(sensor.second);
+ }
+
+ @Test
+ public void parseCyclingSpeedCadence_crankWheel() {
+ BluetoothGattCharacteristic characteristic = new BluetoothGattCharacteristic(BluetoothHandlerCyclingDistanceSpeed.CYCLING_SPEED_CADENCE.serviceUUID(), 0, 0);
+ characteristic.setValue(new byte[]{0x03, (byte) 0xC8, 0x00, 0x00, 0x01, 0x06, (byte) 0x99, (byte) 0xE1, 0x00, 0x45, (byte) 0x99});
+
+ // when
+ Pair sensor = BluetoothHandlerCyclingDistanceSpeed.parseCyclingCrankAndWheel("address", "sensorName", characteristic);
+
+ // then
+ assertEquals(200 + 16777216, sensor.first.wheelRevolutionsCount());
+ assertEquals(225, sensor.second.crankRevolutionsCount());
+ }
+
+}
\ No newline at end of file
diff --git a/src/androidTest/java/de/dennisguse/opentracks/sensors/BluetoothHandlerCyclingPowerTest.java b/src/androidTest/java/de/dennisguse/opentracks/sensors/BluetoothHandlerCyclingPowerTest.java
new file mode 100644
index 000000000..2f4d56e14
--- /dev/null
+++ b/src/androidTest/java/de/dennisguse/opentracks/sensors/BluetoothHandlerCyclingPowerTest.java
@@ -0,0 +1,38 @@
+package de.dennisguse.opentracks.sensors;
+
+import static org.junit.Assert.assertEquals;
+
+import android.bluetooth.BluetoothGattCharacteristic;
+
+import org.junit.Test;
+
+public class BluetoothHandlerCyclingPowerTest {
+
+ @Test
+ public void parseCyclingPower_power() {
+ BluetoothGattCharacteristic characteristic = new BluetoothGattCharacteristic(BluetoothHandlerManagerCyclingPower.CYCLING_POWER.serviceUUID(), 0, 0);
+ characteristic.setValue(new byte[]{0, 0, 40, 0});
+
+ // when
+ BluetoothHandlerManagerCyclingPower.Data powerCadence = BluetoothHandlerManagerCyclingPower.parseCyclingPower(characteristic);
+
+ // then
+ assertEquals(40, powerCadence.power().getW(), 0.01);
+ }
+
+ @Test
+ public void parseCyclingPower_power_with_cadence() {
+ BluetoothGattCharacteristic characteristic = new BluetoothGattCharacteristic(BluetoothHandlerManagerCyclingPower.CYCLING_POWER.serviceUUID(), 0, 0);
+ characteristic.setValue(new byte[]{0x2C, 0x00, 0x00, 0x00, (byte) 0x9F, 0x00, 0x0C, 0x00, (byte) 0xE5, 0x42});
+
+ // when
+ BluetoothHandlerManagerCyclingPower.Data powerCadence = BluetoothHandlerManagerCyclingPower.parseCyclingPower(characteristic);
+
+ // then
+ assertEquals(0, powerCadence.power().getW(), 0.01);
+
+ assertEquals(12, powerCadence.crank().crankRevolutionsCount());
+ assertEquals(17125, powerCadence.crank().crankRevolutionsTime());
+ }
+
+}
\ No newline at end of file
diff --git a/src/androidTest/java/de/dennisguse/opentracks/sensors/BluetoothHandlerManagerHeartRateTest.java b/src/androidTest/java/de/dennisguse/opentracks/sensors/BluetoothHandlerManagerHeartRateTest.java
new file mode 100644
index 000000000..55fecb1d5
--- /dev/null
+++ b/src/androidTest/java/de/dennisguse/opentracks/sensors/BluetoothHandlerManagerHeartRateTest.java
@@ -0,0 +1,38 @@
+package de.dennisguse.opentracks.sensors;
+
+import static org.junit.Assert.assertEquals;
+
+import android.bluetooth.BluetoothGattCharacteristic;
+
+import org.junit.Test;
+
+import de.dennisguse.opentracks.data.models.HeartRate;
+
+public class BluetoothHandlerManagerHeartRateTest {
+
+ @Test
+ public void parseHeartRate_uint8() {
+ // given
+ BluetoothGattCharacteristic characteristic = new BluetoothGattCharacteristic(BluetoothHandlerManagerHeartRate.HEARTRATE.serviceUUID(), 0, 0);
+ characteristic.setValue(new byte[]{0x02, 0x3C});
+
+ // when
+ HeartRate heartRate = BluetoothHandlerManagerHeartRate.parseHeartRate(characteristic);
+
+ // then
+ assertEquals(HeartRate.of(60), heartRate);
+ }
+
+ @Test
+ public void parseHeartRate_uint16() {
+ // given
+ BluetoothGattCharacteristic characteristic = new BluetoothGattCharacteristic(BluetoothHandlerManagerHeartRate.HEARTRATE.serviceUUID(), 0, 0);
+ characteristic.setValue(new byte[]{0x01, 0x01, 0x01});
+
+ // when
+ HeartRate heartRate = BluetoothHandlerManagerHeartRate.parseHeartRate(characteristic);
+
+ // then
+ assertEquals(HeartRate.of(257), heartRate);
+ }
+}
\ No newline at end of file
diff --git a/src/androidTest/java/de/dennisguse/opentracks/sensors/BluetoothHandlerRunningSpeedAndCadenceTest.java b/src/androidTest/java/de/dennisguse/opentracks/sensors/BluetoothHandlerRunningSpeedAndCadenceTest.java
new file mode 100644
index 000000000..7ca2507e3
--- /dev/null
+++ b/src/androidTest/java/de/dennisguse/opentracks/sensors/BluetoothHandlerRunningSpeedAndCadenceTest.java
@@ -0,0 +1,28 @@
+package de.dennisguse.opentracks.sensors;
+
+import static org.junit.Assert.assertEquals;
+
+import android.bluetooth.BluetoothGattCharacteristic;
+
+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;
+
+public class BluetoothHandlerRunningSpeedAndCadenceTest {
+
+ @Test
+ public void parseRunningSpeedAndCadence_with_distance() {
+ BluetoothGattCharacteristic characteristic = new BluetoothGattCharacteristic(BluetoothHandlerRunningSpeedAndCadence.RUNNING_SPEED_CADENCE.serviceUUID(), 0, 0);
+ characteristic.setValue(new byte[]{2, 0, 5, 80, (byte) 0xFF, (byte) 0xFF, 0, 1});
+
+ // when
+ BluetoothHandlerRunningSpeedAndCadence.Data sensor = BluetoothHandlerRunningSpeedAndCadence.parseRunningSpeedAndCadence("sensorName", characteristic);
+
+ // then
+ assertEquals(Speed.of(5), sensor.speed());
+ assertEquals(Cadence.of(80), sensor.cadence());
+ assertEquals(Distance.of(6553.5 + 1677721.6), sensor.totalDistance());
+ }
+}
\ No newline at end of file
diff --git a/src/androidTest/java/de/dennisguse/opentracks/sensors/BluetoothUtilsTest.java b/src/androidTest/java/de/dennisguse/opentracks/sensors/BluetoothUtilsTest.java
deleted file mode 100644
index 92dc59f21..000000000
--- a/src/androidTest/java/de/dennisguse/opentracks/sensors/BluetoothUtilsTest.java
+++ /dev/null
@@ -1,139 +0,0 @@
-package de.dennisguse.opentracks.sensors;
-
-import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.assertNull;
-
-import android.bluetooth.BluetoothGattCharacteristic;
-
-import org.junit.Test;
-
-import de.dennisguse.opentracks.data.models.AtmosphericPressure;
-import de.dennisguse.opentracks.data.models.Cadence;
-import de.dennisguse.opentracks.data.models.Distance;
-import de.dennisguse.opentracks.data.models.HeartRate;
-import de.dennisguse.opentracks.data.models.Speed;
-import de.dennisguse.opentracks.sensors.sensorData.SensorDataCyclingCadenceAndDistanceSpeed;
-import de.dennisguse.opentracks.sensors.sensorData.SensorDataCyclingPower;
-import de.dennisguse.opentracks.sensors.sensorData.SensorDataRunning;
-
-public class BluetoothUtilsTest {
-
- @Test
- public void parseHeartRate_uint8() {
- // given
- BluetoothGattCharacteristic characteristic = new BluetoothGattCharacteristic(BluetoothUtils.HEARTRATE.serviceUUID(), 0, 0);
- characteristic.setValue(new byte[]{0x02, 0x3C});
-
- // when
- HeartRate heartRate = BluetoothUtils.parseHeartRate(characteristic);
-
- // then
- assertEquals(HeartRate.of(60), heartRate);
- }
-
- @Test
- public void parseHeartRate_uint16() {
- // given
- BluetoothGattCharacteristic characteristic = new BluetoothGattCharacteristic(BluetoothUtils.HEARTRATE.serviceUUID(), 0, 0);
- characteristic.setValue(new byte[]{0x01, 0x01, 0x01});
-
- // when
- HeartRate heartRate = BluetoothUtils.parseHeartRate(characteristic);
-
- // then
- assertEquals(HeartRate.of(257), heartRate);
- }
-
- @Test
- public void parseEnvironmentalSensing_Pa() {
- // given
- BluetoothGattCharacteristic characteristic = new BluetoothGattCharacteristic(BluetoothUtils.BAROMETRIC_PRESSURE.serviceUUID(), 0, 0);
- characteristic.setValue(new byte[]{(byte) 0xB2, (byte) 0x48, (byte) 0x0F, (byte) 0x00});
-
- // when
- AtmosphericPressure pressure = BluetoothUtils.parseEnvironmentalSensing(characteristic);
-
- // then
- assertEquals(AtmosphericPressure.ofPA(100165), pressure);
- }
-
- @Test
- public void parseCyclingSpeedCadence_crankOnly() {
- BluetoothGattCharacteristic characteristic = new BluetoothGattCharacteristic(BluetoothUtils.CYCLING_SPEED_CADENCE.serviceUUID(), 0, 0);
- characteristic.setValue(new byte[]{0x02, (byte) 0xC8, 0x00, 0x00, 0x00, 0x06, (byte) 0x99});
-
- // when
- SensorDataCyclingCadenceAndDistanceSpeed sensor = BluetoothUtils.parseCyclingCrankAndWheel("address", "sensorName", characteristic);
-
- // then
- assertNull(sensor.getDistanceSpeed());
- assertEquals(200, sensor.getCadence().getCrankRevolutionsCount());
- }
-
- @Test
- public void parseCyclingSpeedCadence_wheelOnly() {
- BluetoothGattCharacteristic characteristic = new BluetoothGattCharacteristic(BluetoothUtils.CYCLING_SPEED_CADENCE.serviceUUID(), 0, 0);
- characteristic.setValue(new byte[]{0x01, (byte) 0xFF, (byte) 0xFF, 0, 1, 0x45, (byte) 0x99});
-
- // when
- SensorDataCyclingCadenceAndDistanceSpeed sensor = BluetoothUtils.parseCyclingCrankAndWheel("address", "sensorName", characteristic);
-
- // then
- assertEquals(65535 + 16777216, sensor.getDistanceSpeed().getWheelRevolutionsCount());
- assertNull(sensor.getCadence());
- }
-
- @Test
- public void parseCyclingSpeedCadence_crankWheel() {
- BluetoothGattCharacteristic characteristic = new BluetoothGattCharacteristic(BluetoothUtils.CYCLING_SPEED_CADENCE.serviceUUID(), 0, 0);
- characteristic.setValue(new byte[]{0x03, (byte) 0xC8, 0x00, 0x00, 0x01, 0x06, (byte) 0x99, (byte) 0xE1, 0x00, 0x45, (byte) 0x99});
-
- // when
- SensorDataCyclingCadenceAndDistanceSpeed sensor = BluetoothUtils.parseCyclingCrankAndWheel("address", "sensorName", characteristic);
-
- // then
- assertEquals(200 + 16777216, sensor.getDistanceSpeed().getWheelRevolutionsCount());
- assertEquals(225, sensor.getCadence().getCrankRevolutionsCount());
- }
-
- @Test
- public void parseCyclingPower_power() {
- BluetoothGattCharacteristic characteristic = new BluetoothGattCharacteristic(BluetoothUtils.CYCLING_POWER.serviceUUID(), 0, 0);
- characteristic.setValue(new byte[]{0, 0, 40, 0});
-
- // when
- SensorDataCyclingPower.Data powerCadence = BluetoothUtils.parseCyclingPower("", "", characteristic);
-
- // then
- assertEquals(40, powerCadence.power().getValue().getW(), 0.01);
- }
-
- @Test
- public void parseCyclingPower_power_with_cadence() {
- BluetoothGattCharacteristic characteristic = new BluetoothGattCharacteristic(BluetoothUtils.CYCLING_POWER.serviceUUID(), 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.power().getValue().getW(), 0.01);
-
- assertEquals(12, powerCadence.cadence().getCrankRevolutionsCount());
- assertEquals(17125, powerCadence.cadence().getCrankRevolutionsTime());
- }
-
- @Test
- public void parseRunningSpeedAndCadence_with_distance() {
- BluetoothGattCharacteristic characteristic = new BluetoothGattCharacteristic(BluetoothUtils.RUNNING_SPEED_CADENCE.serviceUUID(), 0, 0);
- characteristic.setValue(new byte[]{2, 0, 5, 80, (byte) 0xFF, (byte) 0xFF, 0, 1});
-
- // when
- SensorDataRunning sensor = BluetoothUtils.parseRunningSpeedAndCadence("address", "sensorName", characteristic);
-
- // then
- assertEquals(Speed.of(5), sensor.getSpeed());
- assertEquals(Cadence.of(80), sensor.getCadence());
- assertEquals(Distance.of(6553.5 + 1677721.6), sensor.getTotalDistance());
- }
-}
\ No newline at end of file
diff --git a/src/androidTest/java/de/dennisguse/opentracks/sensors/sensorData/AggregatorBarometerTest.java b/src/androidTest/java/de/dennisguse/opentracks/sensors/sensorData/AggregatorBarometerTest.java
new file mode 100644
index 000000000..5ef2b51c6
--- /dev/null
+++ b/src/androidTest/java/de/dennisguse/opentracks/sensors/sensorData/AggregatorBarometerTest.java
@@ -0,0 +1,28 @@
+package de.dennisguse.opentracks.sensors.sensorData;
+
+import org.junit.Assert;
+import org.junit.Test;
+
+import de.dennisguse.opentracks.data.models.AtmosphericPressure;
+
+public class AggregatorBarometerTest {
+
+ private static void addSensorValue(AggregatorBarometer aggregatorBarometer, float[] values) {
+ for (float f : values) {
+ aggregatorBarometer.add(new Raw<>(AtmosphericPressure.ofHPA(f)));
+ }
+ }
+
+ @Test
+ public void getAltitudeGainLoss_downhill() {
+ // given
+ AggregatorBarometer subject = new AggregatorBarometer("");
+
+ // then
+ addSensorValue(subject, new float[]{1015f, 1015.01f, 1015.02f, 1015.03f, 1015.04f, 1015.05f, 1015.06f, 1015.07f, 1015.08f, 1015.09f, 1015.10f, 1015.11f, 1015.12f, 1015.13f, 1018f, 1018.1f, 1018.1f, 1018.1f, 1018.1f});
+
+ // then
+ Assert.assertEquals(0f, subject.value.gain_m(), 0.01);
+ Assert.assertEquals(15f, subject.value.loss_m(), 0.01);
+ }
+}
\ No newline at end of file
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 4dd5ca5ba..ca3e18949 100644
--- a/src/androidTest/java/de/dennisguse/opentracks/sensors/sensorData/SensorDataCyclingTest.java
+++ b/src/androidTest/java/de/dennisguse/opentracks/sensors/sensorData/SensorDataCyclingTest.java
@@ -2,17 +2,17 @@ package de.dennisguse.opentracks.sensors.sensorData;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
-import static org.junit.Assert.assertNotEquals;
import static org.junit.Assert.assertNull;
import androidx.test.ext.junit.runners.AndroidJUnit4;
-import org.junit.Ignore;
import org.junit.Test;
import org.junit.runner.RunWith;
import de.dennisguse.opentracks.data.models.Cadence;
import de.dennisguse.opentracks.data.models.Distance;
+import de.dennisguse.opentracks.sensors.BluetoothHandlerCyclingCadence;
+import de.dennisguse.opentracks.sensors.BluetoothHandlerCyclingDistanceSpeed;
import de.dennisguse.opentracks.sensors.UintUtils;
@RunWith(AndroidJUnit4.class)
@@ -20,12 +20,11 @@ public class SensorDataCyclingTest {
@Test
public void compute_cadence_1() {
- // given
- SensorDataCyclingCadence previous = new SensorDataCyclingCadence("sensorAddress", "sensorName", 1, 1024); // 1s
- SensorDataCyclingCadence current = new SensorDataCyclingCadence("sensorAddress", "sensorName", 2, 2048); // 2s
+ AggregatorCyclingCadence current = new AggregatorCyclingCadence("", "");
// when
- current.compute(previous);
+ current.add(new Raw<>(new BluetoothHandlerCyclingCadence.CrankData(1, 1024)));
+ current.add(new Raw<>(new BluetoothHandlerCyclingCadence.CrankData(2, 2048)));
// then
assertEquals(60, current.getValue().getRPM(), 0.01);
@@ -33,12 +32,11 @@ public class SensorDataCyclingTest {
@Test
public void compute_cadence_2() {
- // given
- SensorDataCyclingCadence previous = new SensorDataCyclingCadence("sensorAddress", "sensorName", 1, 6184);
- SensorDataCyclingCadence current = new SensorDataCyclingCadence("sensorAddress", "sensorName", 2, 8016);
+ AggregatorCyclingCadence current = new AggregatorCyclingCadence("", "");
// when
- current.compute(previous);
+ current.add(new Raw<>(new BluetoothHandlerCyclingCadence.CrankData(1, 6184)));
+ current.add(new Raw<>(new BluetoothHandlerCyclingCadence.CrankData(2, 8016)));
// then
assertEquals(33.53, current.getValue().getRPM(), 0.01);
@@ -46,12 +44,11 @@ public class SensorDataCyclingTest {
@Test
public void compute_cadence_sameCount() {
- // given
- SensorDataCyclingCadence previous = new SensorDataCyclingCadence("sensorAddress", "sensorName", 1, 1024);
- SensorDataCyclingCadence current = new SensorDataCyclingCadence("sensorAddress", "sensorName", 1, 2048);
+ AggregatorCyclingCadence current = new AggregatorCyclingCadence("", "");
// when
- current.compute(previous);
+ current.add(new Raw<>(new BluetoothHandlerCyclingCadence.CrankData(1, 1024)));
+ current.add(new Raw<>(new BluetoothHandlerCyclingCadence.CrankData(1, 2048)));
// then
assertEquals(Cadence.of(0), current.getValue());
@@ -60,126 +57,72 @@ public class SensorDataCyclingTest {
@Test
public void compute_cadence_sameTime() {
- // given
- SensorDataCyclingCadence previous = new SensorDataCyclingCadence("sensorAddress", "sensorName", 1, 1024);
- SensorDataCyclingCadence current = new SensorDataCyclingCadence("sensorAddress", "sensorName", 2, 1024);
+ AggregatorCyclingCadence current = new AggregatorCyclingCadence("", "");
// when
- current.compute(previous);
+ current.add(new Raw<>(new BluetoothHandlerCyclingCadence.CrankData(1, 1024)));
+ current.add(new Raw<>(new BluetoothHandlerCyclingCadence.CrankData(2, 1024)));
// then
- assertFalse(current.hasValue());
+ assertFalse(current.hasValue()); //TODO Cadence should be 0?
}
@Test
public void compute_cadence_rollOverTime() {
- // given
- SensorDataCyclingCadence previous = new SensorDataCyclingCadence("sensorAddress", "sensorName", 1, UintUtils.UINT16_MAX - 1024);
- SensorDataCyclingCadence current = new SensorDataCyclingCadence("sensorAddress", "sensorName", 2, 0);
+ AggregatorCyclingCadence current = new AggregatorCyclingCadence("", "");
// when
- current.compute(previous);
+ current.add(new Raw<>(new BluetoothHandlerCyclingCadence.CrankData(1, UintUtils.UINT16_MAX - 1024)));
+ current.add(new Raw<>(new BluetoothHandlerCyclingCadence.CrankData(2, 0)));
// then
assertEquals(60, current.getValue().getRPM(), 0.01);
}
- @Ignore("Disabled from #953")
@Test
@Deprecated
public void compute_cadence_rollOverCount() {
- // given
- SensorDataCyclingCadence previous = new SensorDataCyclingCadence("sensorAddress", "sensorName", UintUtils.UINT32_MAX - 1, 1024);
- SensorDataCyclingCadence current = new SensorDataCyclingCadence("sensorAddress", "sensorName", 0, 2048);
+ AggregatorCyclingCadence current = new AggregatorCyclingCadence("", "");
// when
- current.compute(previous);
-
- // then
- assertEquals(60, current.getValue().getRPM(), 0.01);
- }
-
- @Test
- public void compute_cadence_overflow() {
- // given
- SensorDataCyclingCadence previous = new SensorDataCyclingCadence("sensorAddress", "sensorName", UintUtils.UINT32_MAX - 1, 1024);
- SensorDataCyclingCadence current = new SensorDataCyclingCadence("sensorAddress", "sensorName", 0, 2048);
-
- // when
- current.compute(previous);
+ current.add(new Raw<>(new BluetoothHandlerCyclingCadence.CrankData(UintUtils.UINT32_MAX - 1, 1024)));
+ current.add(new Raw<>(new BluetoothHandlerCyclingCadence.CrankData(0, 2048)));
// then
+ // TODO See #953
+// assertEquals(60, current.getValue().getRPM(), 0.01);
assertNull(current.getValue());
}
@Test
public void compute_speed() {
- // given
- SensorDataCyclingDistanceSpeed previous = new SensorDataCyclingDistanceSpeed("sensorAddress", "sensorName", 1, 6184);
- SensorDataCyclingDistanceSpeed current = new SensorDataCyclingDistanceSpeed("sensorAddress", "sensorName", 2, 8016);
+ AggregatorCyclingDistanceSpeed current = new AggregatorCyclingDistanceSpeed("", "");
+ current.setWheelCircumference(Distance.ofMM(2150));
// when
- current.compute(previous, Distance.ofMM(2150));
+ current.add(new Raw<>(new BluetoothHandlerCyclingDistanceSpeed.WheelData(1, 6184)));
+ current.add(new Raw<>(new BluetoothHandlerCyclingDistanceSpeed.WheelData(2, 8016)));
// then
- assertEquals(2.15, current.getValue().getDistance().toM(), 0.01);
- assertEquals(1.20, current.getValue().getSpeed().toMPS(), 0.01);
+ assertEquals(2.15, current.getValue().distance().toM(), 0.01);
+ assertEquals(1.20, current.getValue().speed().toMPS(), 0.01);
}
- @Ignore("Disabled from #953")
@Test
@Deprecated
public void compute_speed_rollOverCount() {
- // given
- SensorDataCyclingDistanceSpeed previous = new SensorDataCyclingDistanceSpeed("sensorAddress", "sensorName", UintUtils.UINT32_MAX - 1, 1024);
- SensorDataCyclingDistanceSpeed current = new SensorDataCyclingDistanceSpeed("sensorAddress", "sensorName", 0, 2048);
+ AggregatorCyclingDistanceSpeed current = new AggregatorCyclingDistanceSpeed("", "");
+ current.setWheelCircumference(Distance.ofMM(2000));
// when
- current.compute(previous, Distance.ofMM(2000));
-
- // then
- assertEquals(2, current.getValue().getDistance().toM(), 0.01);
- assertEquals(2, current.getValue().getSpeed().toMPS(), 0.01);
- }
-
- @Test
- public void compute_speed_overflow() {
- // given
- 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));
+ current.add(new Raw<>(new BluetoothHandlerCyclingDistanceSpeed.WheelData(UintUtils.UINT32_MAX - 1, 1024)));
+ current.add(new Raw<>(new BluetoothHandlerCyclingDistanceSpeed.WheelData(0, 2048)));
+
// then
+ // TODO See #953
+// assertEquals(2, current.getValue().getDistance().toM(), 0.01);
+// assertEquals(2, current.getValue().getSpeed().toMPS(), 0.01);
assertNull(current.getValue());
}
-
- @Test
- public void equals_speed_with_no_data() {
- // given
- SensorDataCyclingDistanceSpeed previous = new SensorDataCyclingDistanceSpeed("sensorAddress");
- SensorDataCyclingDistanceSpeed current = new SensorDataCyclingDistanceSpeed("sensorAddress", "sensorName", 0, 2048);
-
- // when
- previous.toString();
-
- // then
- assertNotEquals(previous, current);
- assertNotEquals(previous, previous);
- }
-
- @Test
- public void equals_cadence_with_no_data() {
- // given
- SensorDataCyclingCadence previous = new SensorDataCyclingCadence("sensorAddress");
- SensorDataCyclingCadence current = new SensorDataCyclingCadence("sensorAddress", "sensorName", 0, 2048);
-
- // when
- previous.toString();
-
- // then
- assertNotEquals(previous, current);
- assertNotEquals(previous, previous);
- }
}
\ No newline at end of file
diff --git a/src/androidTest/java/de/dennisguse/opentracks/services/TrackRecordingServiceRecordingTest.java b/src/androidTest/java/de/dennisguse/opentracks/services/TrackRecordingServiceRecordingTest.java
index 95e35f88e..82c75ce8e 100644
--- a/src/androidTest/java/de/dennisguse/opentracks/services/TrackRecordingServiceRecordingTest.java
+++ b/src/androidTest/java/de/dennisguse/opentracks/services/TrackRecordingServiceRecordingTest.java
@@ -7,7 +7,6 @@ import android.content.Context;
import android.content.Intent;
import android.os.Looper;
-import androidx.annotation.NonNull;
import androidx.test.core.app.ApplicationProvider;
import androidx.test.ext.junit.runners.AndroidJUnit4;
import androidx.test.filters.MediumTest;
@@ -19,6 +18,7 @@ import org.junit.BeforeClass;
import org.junit.Rule;
import org.junit.Test;
import org.junit.runner.RunWith;
+import org.mockito.Mockito;
import java.time.Instant;
import java.util.List;
@@ -28,24 +28,25 @@ import java.util.concurrent.TimeoutException;
import de.dennisguse.opentracks.R;
import de.dennisguse.opentracks.content.data.TestDataUtil;
import de.dennisguse.opentracks.data.ContentProviderUtils;
+import de.dennisguse.opentracks.data.models.AltitudeGainLoss;
import de.dennisguse.opentracks.data.models.Distance;
import de.dennisguse.opentracks.data.models.HeartRate;
import de.dennisguse.opentracks.data.models.Speed;
import de.dennisguse.opentracks.data.models.Track;
import de.dennisguse.opentracks.data.models.TrackPoint;
import de.dennisguse.opentracks.io.file.importer.TrackPointAssert;
-import de.dennisguse.opentracks.sensors.AltitudeSumManager;
-import de.dennisguse.opentracks.sensors.BluetoothRemoteSensorManager;
-import de.dennisguse.opentracks.sensors.sensorData.SensorDataHeartRate;
-import de.dennisguse.opentracks.sensors.sensorData.SensorDataRunning;
+import de.dennisguse.opentracks.sensors.BluetoothHandlerRunningSpeedAndCadence;
+import de.dennisguse.opentracks.sensors.SensorManager;
+import de.dennisguse.opentracks.sensors.sensorData.AggregatorBarometer;
+import de.dennisguse.opentracks.sensors.sensorData.AggregatorHeartRate;
+import de.dennisguse.opentracks.sensors.sensorData.AggregatorRunning;
+import de.dennisguse.opentracks.sensors.sensorData.Raw;
import de.dennisguse.opentracks.services.handlers.TrackPointCreator;
import de.dennisguse.opentracks.settings.PreferencesUtils;
import de.dennisguse.opentracks.stats.TrackStatistics;
/**
* Tests insert location.
- * Note: on API23, the TrackRecordingService may not be stopped properly before the next test.
- * So, if something fails, subsequent tests may be affected.
*/
@RunWith(AndroidJUnit4.class)
public class TrackRecordingServiceRecordingTest {
@@ -58,14 +59,6 @@ public class TrackRecordingServiceRecordingTest {
private TrackRecordingService service;
- private final AltitudeSumManager altitudeSumManager = new AltitudeSumManager() {
- @Override
- public void fill(@NonNull TrackPoint trackPoint) {
- trackPoint.setAltitudeGain(0f);
- trackPoint.setAltitudeLoss(0f);
- }
- };
-
@BeforeClass
public static void preSetUp() {
// Prepare looper for Android's message queue
@@ -105,7 +98,8 @@ public class TrackRecordingServiceRecordingTest {
String startTime = "2020-02-02T02:02:02Z";
trackPointCreator.setClock(startTime);
Track.Id trackId = service.startNewTrack();
- trackPointCreator.getSensorManager().setAltitudeSumManager(altitudeSumManager);
+ mockAltitudeChange(trackPointCreator, 0);
+
// then
assertEquals(new TrackStatistics(startTime, startTime, 0, 0, 0, 0, null, null)
@@ -142,7 +136,8 @@ public class TrackRecordingServiceRecordingTest {
trackPointCreator.setClock(startTime);
Track.Id trackId = service.startNewTrack();
- trackPointCreator.getSensorManager().setAltitudeSumManager(altitudeSumManager);
+ mockAltitudeChange(trackPointCreator, 0);
+
// when
String pauseTime = "2020-02-02T02:02:03Z";
@@ -188,7 +183,8 @@ public class TrackRecordingServiceRecordingTest {
String starTime = "2020-02-02T02:02:02Z";
trackPointCreator.setClock(starTime);
Track.Id trackId = service.startNewTrack();
- trackPointCreator.getSensorManager().setAltitudeSumManager(altitudeSumManager);
+ mockAltitudeChange(trackPointCreator, 0);
+
String pauseTime = "2020-02-02T02:02:03Z";
trackPointCreator.setClock(pauseTime);
@@ -220,7 +216,8 @@ public class TrackRecordingServiceRecordingTest {
trackPointCreator.setClock(startTime);
Track.Id trackId = service.startNewTrack();
- trackPointCreator.getSensorManager().setAltitudeSumManager(altitudeSumManager);
+ mockAltitudeChange(trackPointCreator, 0);
+
String stopTime = "2020-02-02T02:02:03Z";
trackPointCreator.setClock(stopTime);
@@ -230,7 +227,8 @@ public class TrackRecordingServiceRecordingTest {
String resumeTime = "2020-02-02T02:02:04Z";
trackPointCreator.setClock(resumeTime);
service.resumeTrack(trackId);
- trackPointCreator.getSensorManager().setAltitudeSumManager(altitudeSumManager);
+ mockAltitudeChange(trackPointCreator, 0);
+
// then
new TrackPointAssert().assertEquals(List.of(
@@ -251,18 +249,19 @@ public class TrackRecordingServiceRecordingTest {
trackPointCreator.setClock(startTime);
Track.Id trackId = service.startNewTrack();
- trackPointCreator.getSensorManager().setAltitudeSumManager(altitudeSumManager);
- BluetoothRemoteSensorManager remoteSensorManager = trackPointCreator.getSensorManager().getBluetoothSensorManager();
+ mockAltitudeChange(trackPointCreator, 0);
+ SensorManager sensorManager = trackPointCreator.getSensorManager();
+ sensorManager.sensorDataSet.add(new AggregatorHeartRate("", ""));
// when
String sensor1 = "2020-02-02T02:02:03Z";
trackPointCreator.setClock(sensor1);
- remoteSensorManager.onChanged(new SensorDataHeartRate("", "", HeartRate.of(5))); //Should be ignored
+ sensorManager.onChanged(new Raw<>(HeartRate.of(5))); //Should be ignored
String sensor3 = "2020-02-02T02:02:13Z";
trackPointCreator.setClock(sensor3);
- remoteSensorManager.onChanged(new SensorDataHeartRate("", "", HeartRate.of(7)));
+ sensorManager.onChanged(new Raw<>(HeartRate.of(7)));
String stopTime = "2020-02-02T02:02:15Z";
trackPointCreator.setClock(stopTime);
@@ -282,7 +281,6 @@ public class TrackRecordingServiceRecordingTest {
), TestDataUtil.getTrackPoints(contentProviderUtils, trackId));
}
-
@MediumTest
@Test
public void testRecording_gpsOnly_recordingDistance_above() {
@@ -291,7 +289,7 @@ public class TrackRecordingServiceRecordingTest {
TrackPointCreator trackPointCreator = service.getTrackPointCreator();
trackPointCreator.setClock(startTime);
Track.Id trackId = service.startNewTrack();
- trackPointCreator.getSensorManager().setAltitudeSumManager(altitudeSumManager);
+ mockAltitudeChange(trackPointCreator, 0);
// when
@@ -365,7 +363,8 @@ public class TrackRecordingServiceRecordingTest {
TrackPointCreator trackPointCreator = service.getTrackPointCreator();
trackPointCreator.setClock(startTime);
Track.Id trackId = service.startNewTrack();
- trackPointCreator.getSensorManager().setAltitudeSumManager(altitudeSumManager);
+ mockAltitudeChange(trackPointCreator, 0);
+
// when
String gps1 = "2020-02-02T02:02:03Z";
@@ -429,7 +428,8 @@ public class TrackRecordingServiceRecordingTest {
TrackPointCreator trackPointCreator = service.getTrackPointCreator();
trackPointCreator.setClock(startTime);
Track.Id trackId = service.startNewTrack();
- trackPointCreator.getSensorManager().setAltitudeSumManager(altitudeSumManager);
+ mockAltitudeChange(trackPointCreator, 0);
+
// when
String gps1 = "2020-02-02T02:02:03Z";
@@ -479,7 +479,8 @@ public class TrackRecordingServiceRecordingTest {
TrackPointCreator trackPointCreator = service.getTrackPointCreator();
trackPointCreator.setClock(startTime);
Track.Id trackId = service.startNewTrack();
- trackPointCreator.getSensorManager().setAltitudeSumManager(altitudeSumManager);
+ mockAltitudeChange(trackPointCreator, 0);
+
// when
String gps1 = "2020-02-02T02:02:03Z";
@@ -525,7 +526,7 @@ public class TrackRecordingServiceRecordingTest {
TrackPointCreator trackPointCreator = service.getTrackPointCreator();
trackPointCreator.setClock(startTime);
Track.Id trackId = service.startNewTrack();
- trackPointCreator.getSensorManager().setAltitudeSumManager(altitudeSumManager);
+ mockAltitudeChange(trackPointCreator, 0);
// when
String gps1 = "2020-02-02T02:02:03Z";
@@ -581,25 +582,25 @@ public class TrackRecordingServiceRecordingTest {
@Test
public void testRecording_gpsAndSensor_gpsIdleMoving_sensorMoving() {
// TODO Check TrackStatistics
- AltitudeSumManager altitudeSumManager = new AltitudeSumManager();
-
// given
String startTime = "2020-02-02T02:02:02Z";
TrackPointCreator trackPointCreator = service.getTrackPointCreator();
trackPointCreator.setClock(startTime);
Track.Id trackId = service.startNewTrack();
- trackPointCreator.getSensorManager().setAltitudeSumManager(altitudeSumManager);
- BluetoothRemoteSensorManager remoteSensorManager = trackPointCreator.getSensorManager().getBluetoothSensorManager();
+
+ SensorManager sensorManager = trackPointCreator.getSensorManager();
+ sensorManager.sensorDataSet.add(new AggregatorRunning("", ""));
+ sensorManager.sensorDataSet.barometer = null;
// when
String sensor1 = "2020-02-02T02:02:03Z";
trackPointCreator.setClock(sensor1);
- remoteSensorManager.onChanged(new SensorDataRunning("", "", Speed.of(5), null, Distance.of(0))); //Should be ignored
+ sensorManager.onChanged(new Raw<>(new BluetoothHandlerRunningSpeedAndCadence.Data(Speed.of(5), null, Distance.of(0)))); //Should be ignored
// when
String sensor2 = "2020-02-02T02:02:04Z";
trackPointCreator.setClock(sensor2);
- remoteSensorManager.onChanged(new SensorDataRunning("", "", Speed.of(5), null, Distance.of(2)));
+ sensorManager.onChanged(new Raw<>(new BluetoothHandlerRunningSpeedAndCadence.Data(Speed.of(5), null, Distance.of(2))));
// when
String gps1 = "2020-02-02T02:02:05Z";
@@ -608,12 +609,12 @@ public class TrackRecordingServiceRecordingTest {
// when
String sensor3 = "2020-02-02T02:02:06Z";
trackPointCreator.setClock(sensor3);
- remoteSensorManager.onChanged(new SensorDataRunning("", "", Speed.of(5), null, Distance.of(12)));
+ sensorManager.onChanged(new Raw<>(new BluetoothHandlerRunningSpeedAndCadence.Data(Speed.of(5), null, Distance.of(12))));
// when
String sensor4 = "2020-02-02T02:02:07Z";
trackPointCreator.setClock(sensor4);
- remoteSensorManager.onChanged(new SensorDataRunning("", "", Speed.of(5), null, Distance.of(14))); //Should be ignored
+ sensorManager.onChanged(new Raw<>(new BluetoothHandlerRunningSpeedAndCadence.Data(Speed.of(5), null, Distance.of(14)))); //Should be ignored
// when
String gps2 = "2020-02-02T02:02:08Z";
@@ -622,7 +623,7 @@ public class TrackRecordingServiceRecordingTest {
// when
String sensor5 = "2020-02-02T02:02:10Z";
trackPointCreator.setClock(sensor5);
- remoteSensorManager.onChanged(new SensorDataRunning("", "", Speed.of(5), null, Distance.of(16))); //Should be ignored
+ sensorManager.onChanged(new Raw<>(new BluetoothHandlerRunningSpeedAndCadence.Data(Speed.of(5), null, Distance.of(16)))); //Should be ignored
// when
String gps3 = "2020-02-02T02:02:12Z";
@@ -671,4 +672,12 @@ public class TrackRecordingServiceRecordingTest {
.setSensorDistance(Distance.of(0))
), TestDataUtil.getTrackPoints(contentProviderUtils, trackId));
}
+
+ private void mockAltitudeChange(TrackPointCreator trackPointCreator, float altitudeGain) {
+ AggregatorBarometer barometer = Mockito.mock(AggregatorBarometer.class);
+ Mockito.when(barometer.hasValue()).thenReturn(true);
+ Mockito.when(barometer.getValue()).thenReturn(new AltitudeGainLoss(altitudeGain, altitudeGain));
+
+ trackPointCreator.getSensorManager().sensorDataSet.barometer = barometer;
+ }
}
diff --git a/src/androidTest/res/raw/csv_export.csv b/src/androidTest/res/raw/csv_export.csv
index cdefb81a1..f4344cd20 100644
--- a/src/androidTest/res/raw/csv_export.csv
+++ b/src/androidTest/res/raw/csv_export.csv
@@ -8,6 +8,6 @@
"2020-02-02T03:03:20+01:00","SEGMENT_START_MANUAL",,,,,,,,,,,,
"2020-02-02T03:03:21+01:00","TRACKPOINT",3,14.002,10,10,,54,0,0,,,,
"2020-02-02T03:03:22+01:00","SEGMENT_START_AUTOMATIC",3,16,10,10,,54,0,0,,,,
-"2020-02-02T03:03:30+01:00","IDLE",,,,,,,,,,,,
+"2020-02-02T03:03:30+01:00","IDLE",,,,,,,0,0,,,,
"2020-02-02T03:03:50+01:00","TRACKPOINT",3,16.001,10,10,,54,0,0,,,,
"2020-02-02T03:04:00+01:00","SEGMENT_END_MANUAL",,,,,,,,,,,,
\ No newline at end of file
diff --git a/src/main/java/de/dennisguse/opentracks/data/models/AltitudeGainLoss.java b/src/main/java/de/dennisguse/opentracks/data/models/AltitudeGainLoss.java
new file mode 100644
index 000000000..a33047393
--- /dev/null
+++ b/src/main/java/de/dennisguse/opentracks/data/models/AltitudeGainLoss.java
@@ -0,0 +1,3 @@
+package de.dennisguse.opentracks.data.models;
+
+public record AltitudeGainLoss(float gain_m, float loss_m) {}
diff --git a/src/main/java/de/dennisguse/opentracks/sensors/AltitudeSumManager.java b/src/main/java/de/dennisguse/opentracks/sensors/AltitudeSumManager.java
deleted file mode 100644
index 63587e033..000000000
--- a/src/main/java/de/dennisguse/opentracks/sensors/AltitudeSumManager.java
+++ /dev/null
@@ -1,108 +0,0 @@
-package de.dennisguse.opentracks.sensors;
-
-import android.content.Context;
-import android.os.Handler;
-import android.util.Log;
-
-import androidx.annotation.NonNull;
-import androidx.annotation.Nullable;
-import androidx.annotation.VisibleForTesting;
-
-import de.dennisguse.opentracks.data.models.AtmosphericPressure;
-import de.dennisguse.opentracks.data.models.TrackPoint;
-import de.dennisguse.opentracks.sensors.driver.BarometerInternal;
-
-/**
- * Estimates the altitude gain and altitude loss using the device's pressure sensor (i.e., barometer).
- */
-public class AltitudeSumManager implements SensorConnector {
-
- private static final String TAG = AltitudeSumManager.class.getSimpleName();
-
- private final BarometerInternal driver;
-
- private AtmosphericPressure lastAcceptedSensorValue;
-
- private AtmosphericPressure lastSeenSensorValue;
-
- private Float altitudeGain_m;
- private Float altitudeLoss_m;
-
- public AltitudeSumManager() {
- driver = new BarometerInternal();
- }
-
- @VisibleForTesting
- public AltitudeSumManager(BarometerInternal mock) {
- this.driver = mock;
- }
-
- public void start(Context context, Handler handler) {
- driver.connect(context, handler, this);
-
- lastAcceptedSensorValue = null;
- reset();
- }
-
- public void stop(Context context) {
- Log.d(TAG, "Stop");
-
- driver.disconnect(context);
- reset();
- }
-
- public void fill(@NonNull TrackPoint trackPoint) {
- trackPoint.setAltitudeGain(altitudeGain_m);
- trackPoint.setAltitudeLoss(altitudeLoss_m);
- }
-
- @Nullable
- public Float getAltitudeGain_m() {
- return driver.isConnected() ? altitudeGain_m : null;
- }
-
- @VisibleForTesting
- public void setAltitudeGain_m(float altitudeGain_m) {
- this.altitudeGain_m = altitudeGain_m;
- }
-
- @Nullable
- public Float getAltitudeLoss_m() {
- return driver.isConnected() ? altitudeLoss_m : null;
- }
-
- @VisibleForTesting
- public void setAltitudeLoss_m(float altitudeLoss_m) {
- this.altitudeLoss_m = altitudeLoss_m;
- }
-
- public void reset() {
- Log.d(TAG, "Reset");
- altitudeGain_m = null;
- altitudeLoss_m = null;
- }
-
- public void onSensorValueChanged(AtmosphericPressure currentSensorValue) {
- if (lastAcceptedSensorValue == null) {
- lastAcceptedSensorValue = currentSensorValue;
- lastSeenSensorValue = currentSensorValue;
- return;
- }
-
- altitudeGain_m = altitudeGain_m != null ? altitudeGain_m : 0;
- altitudeLoss_m = altitudeLoss_m != null ? altitudeLoss_m : 0;
-
- PressureSensorUtils.AltitudeChange altitudeChange = PressureSensorUtils.computeChangesWithSmoothing_m(lastAcceptedSensorValue, lastSeenSensorValue, currentSensorValue);
- if (altitudeChange != null) {
- altitudeGain_m += altitudeChange.getAltitudeGain_m();
-
- altitudeLoss_m += altitudeChange.getAltitudeLoss_m();
-
- lastAcceptedSensorValue = altitudeChange.currentSensorValue();
- }
-
- lastSeenSensorValue = currentSensorValue;
-
- Log.v(TAG, "altitude gain: " + altitudeGain_m + ", altitude loss: " + altitudeLoss_m);
- }
-}
diff --git a/src/main/java/de/dennisguse/opentracks/sensors/AbstractBluetoothConnectionManager.java b/src/main/java/de/dennisguse/opentracks/sensors/BluetoothConnectionManager.java
similarity index 66%
rename from src/main/java/de/dennisguse/opentracks/sensors/AbstractBluetoothConnectionManager.java
rename to src/main/java/de/dennisguse/opentracks/sensors/BluetoothConnectionManager.java
index 7a7185b68..bbb483d15 100644
--- a/src/main/java/de/dennisguse/opentracks/sensors/AbstractBluetoothConnectionManager.java
+++ b/src/main/java/de/dennisguse/opentracks/sensors/BluetoothConnectionManager.java
@@ -16,6 +16,7 @@
package de.dennisguse.opentracks.sensors;
+import android.annotation.SuppressLint;
import android.bluetooth.BluetoothDevice;
import android.bluetooth.BluetoothGatt;
import android.bluetooth.BluetoothGattCallback;
@@ -24,29 +25,27 @@ import android.bluetooth.BluetoothGattDescriptor;
import android.bluetooth.BluetoothGattService;
import android.bluetooth.BluetoothProfile;
import android.content.Context;
-import android.os.Build;
import android.os.Handler;
import android.util.Log;
import androidx.annotation.NonNull;
-import java.util.List;
import java.util.Optional;
import java.util.UUID;
-import de.dennisguse.opentracks.sensors.sensorData.SensorData;
+import de.dennisguse.opentracks.sensors.sensorData.SensorHandlerInterface;
/**
* Manages connection to a Bluetooth LE sensor and subscribes for onChange-notifications.
- * Also parses the transferred data into {@link SensorDataObserver}.
*/
-public abstract class AbstractBluetoothConnectionManager {
+@SuppressLint("MissingPermission")
+public class BluetoothConnectionManager {
- private static final String TAG = AbstractBluetoothConnectionManager.class.getSimpleName();
+ private static final String TAG = BluetoothConnectionManager.class.getSimpleName();
- private final SensorDataObserver observer;
+ private final SensorManager.SensorDataChangedObserver observer;
- private final List serviceMeasurementUUIDs;
+ private final SensorHandlerInterface sensorHandler;
private BluetoothGatt bluetoothGatt;
private final BluetoothGattCallback connectCallback = new BluetoothGattCallback() {
@@ -76,7 +75,7 @@ public abstract class AbstractBluetoothConnectionManager {
public void onServicesDiscovered(@NonNull BluetoothGatt gatt, int status) {
BluetoothGattService gattService = null;
ServiceMeasurementUUID serviceMeasurement = null;
- for (ServiceMeasurementUUID s : serviceMeasurementUUIDs) {
+ for (ServiceMeasurementUUID s : sensorHandler.getServices()) {
gattService = gatt.getService(s.serviceUUID());
if (gattService != null) {
serviceMeasurement = s;
@@ -112,37 +111,25 @@ public abstract class AbstractBluetoothConnectionManager {
public void onCharacteristicChanged(BluetoothGatt gatt, @NonNull BluetoothGattCharacteristic 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.serviceUUID().equals(characteristic.getService().getUuid())).findFirst();
+ Optional serviceMeasurementUUID = sensorHandler.getServices()
+ .stream()
+ .filter(s -> s.serviceUUID().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) {
- observer.onChanged(sensorData);
- } else {
- //TODO This might lead to NPEs in case of race conditions due to shutdown.
- observer.getHandler().post(() -> observer.onChanged(sensorData));
- }
- }
+ sensorHandler.handlePayload(observer, serviceMeasurementUUID.get(), gatt.getDevice().getName(), gatt.getDevice().getAddress(), characteristic);
}
};
- AbstractBluetoothConnectionManager(ServiceMeasurementUUID serviceUUUID, SensorDataObserver observer) {
- this.serviceMeasurementUUIDs = List.of(serviceUUUID);
+ BluetoothConnectionManager(SensorManager.SensorDataChangedObserver observer, SensorHandlerInterface sensorHandler) {
this.observer = observer;
+ this.sensorHandler = sensorHandler;
}
- AbstractBluetoothConnectionManager(List serviceUUUID, SensorDataObserver observer) {
- this.serviceMeasurementUUIDs = serviceUUUID;
- this.observer = observer;
- }
-
- synchronized void connect(Context context, @NonNull BluetoothDevice device) {
+ synchronized void connect(Context context, Handler handler, @NonNull BluetoothDevice device) {
if (bluetoothGatt != null) {
Log.w(TAG, "Already connected; ignoring.");
return;
@@ -150,20 +137,15 @@ public abstract class AbstractBluetoothConnectionManager {
Log.d(TAG, "Connecting to: " + device);
- if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
- bluetoothGatt = device.connectGatt(context, false, connectCallback, BluetoothDevice.TRANSPORT_AUTO, 0, this.observer.getHandler());
- } else {
- bluetoothGatt = device.connectGatt(context, false, connectCallback);
- }
- SensorData> sensorData = createEmptySensorData(bluetoothGatt.getDevice().getAddress());
- observer.onChanged(sensorData);
+ bluetoothGatt = device.connectGatt(context, false, connectCallback, BluetoothDevice.TRANSPORT_AUTO, 0, handler);
+
+ observer.onConnect(sensorHandler.createEmptySensorData(bluetoothGatt.getDevice().getAddress()));
}
private synchronized void clearData() {
- observer.onDisconnecting(createEmptySensorData(bluetoothGatt.getDevice().getAddress()));
+ observer.onDisconnect(sensorHandler.createEmptySensorData(bluetoothGatt.getDevice().getAddress()));
}
-
synchronized void disconnect() {
if (bluetoothGatt == null) {
Log.w(TAG, "Cannot disconnect if not connected.");
@@ -181,22 +163,4 @@ public abstract class AbstractBluetoothConnectionManager {
return address.equals(bluetoothGatt.getDevice().getAddress());
}
-
- protected abstract SensorData createEmptySensorData(String address);
-
- /**
- * @return null if data could not be parsed.
- */
- protected abstract SensorData parsePayload(@NonNull ServiceMeasurementUUID serviceMeasurementUUID, String sensorName, String address, @NonNull BluetoothGattCharacteristic characteristic);
-
- @Deprecated //TODO REMOVE
- interface SensorDataObserver {
-
- void onChanged(SensorData> sensorData);
-
- void onDisconnecting(SensorData> sensorData);
-
- @NonNull
- Handler getHandler();
- }
}
diff --git a/src/main/java/de/dennisguse/opentracks/sensors/BluetoothConnectionManagerCyclingCadence.java b/src/main/java/de/dennisguse/opentracks/sensors/BluetoothConnectionManagerCyclingCadence.java
deleted file mode 100644
index 58980afa7..000000000
--- a/src/main/java/de/dennisguse/opentracks/sensors/BluetoothConnectionManagerCyclingCadence.java
+++ /dev/null
@@ -1,47 +0,0 @@
-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 BluetoothConnectionManagerCyclingCadence extends AbstractBluetoothConnectionManager {
-
- private static final String TAG = BluetoothConnectionManagerCyclingCadence.class.getSimpleName();
-
- BluetoothConnectionManagerCyclingCadence(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.cadence();
- }
- } 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/BluetoothConnectionManagerCyclingDistanceSpeed.java b/src/main/java/de/dennisguse/opentracks/sensors/BluetoothConnectionManagerCyclingDistanceSpeed.java
deleted file mode 100644
index 538183ea6..000000000
--- a/src/main/java/de/dennisguse/opentracks/sensors/BluetoothConnectionManagerCyclingDistanceSpeed.java
+++ /dev/null
@@ -1,32 +0,0 @@
-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 BluetoothConnectionManagerCyclingDistanceSpeed extends AbstractBluetoothConnectionManager {
-
- BluetoothConnectionManagerCyclingDistanceSpeed(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/BluetoothConnectionManagerCyclingPower.java b/src/main/java/de/dennisguse/opentracks/sensors/BluetoothConnectionManagerCyclingPower.java
deleted file mode 100644
index b5b5f8649..000000000
--- a/src/main/java/de/dennisguse/opentracks/sensors/BluetoothConnectionManagerCyclingPower.java
+++ /dev/null
@@ -1,27 +0,0 @@
-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 BluetoothConnectionManagerCyclingPower extends AbstractBluetoothConnectionManager {
-
- BluetoothConnectionManagerCyclingPower(@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.power() : null;
- }
-}
diff --git a/src/main/java/de/dennisguse/opentracks/sensors/BluetoothConnectionManagerHeartRate.java b/src/main/java/de/dennisguse/opentracks/sensors/BluetoothConnectionManagerHeartRate.java
deleted file mode 100644
index b8271920b..000000000
--- a/src/main/java/de/dennisguse/opentracks/sensors/BluetoothConnectionManagerHeartRate.java
+++ /dev/null
@@ -1,27 +0,0 @@
-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 BluetoothConnectionManagerHeartRate extends AbstractBluetoothConnectionManager {
-
- BluetoothConnectionManagerHeartRate(@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) {
- HeartRate heartRate = BluetoothUtils.parseHeartRate(characteristic);
-
- return heartRate != null ? new SensorDataHeartRate(address, sensorName, heartRate) : null;
- }
-}
diff --git a/src/main/java/de/dennisguse/opentracks/sensors/BluetoothConnectionRunningSpeedAndCadence.java b/src/main/java/de/dennisguse/opentracks/sensors/BluetoothConnectionRunningSpeedAndCadence.java
deleted file mode 100644
index 42c952628..000000000
--- a/src/main/java/de/dennisguse/opentracks/sensors/BluetoothConnectionRunningSpeedAndCadence.java
+++ /dev/null
@@ -1,24 +0,0 @@
-package de.dennisguse.opentracks.sensors;
-
-import android.bluetooth.BluetoothGattCharacteristic;
-
-import androidx.annotation.NonNull;
-
-import de.dennisguse.opentracks.sensors.sensorData.SensorDataRunning;
-
-public class BluetoothConnectionRunningSpeedAndCadence extends AbstractBluetoothConnectionManager {
-
- BluetoothConnectionRunningSpeedAndCadence(@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/BluetoothHandlerBarometricPressure.java b/src/main/java/de/dennisguse/opentracks/sensors/BluetoothHandlerBarometricPressure.java
new file mode 100644
index 000000000..44adebbc9
--- /dev/null
+++ b/src/main/java/de/dennisguse/opentracks/sensors/BluetoothHandlerBarometricPressure.java
@@ -0,0 +1,45 @@
+package de.dennisguse.opentracks.sensors;
+
+import android.bluetooth.BluetoothGattCharacteristic;
+
+import java.util.List;
+import java.util.UUID;
+
+import de.dennisguse.opentracks.data.models.AtmosphericPressure;
+import de.dennisguse.opentracks.sensors.sensorData.Aggregator;
+import de.dennisguse.opentracks.sensors.sensorData.SensorHandlerInterface;
+
+public class BluetoothHandlerBarometricPressure implements SensorHandlerInterface {
+ private static final UUID ENVIRONMENTAL_SENSING_SERVICE = new UUID(0x181A00001000L, 0x800000805f9b34fbL);
+ public static final ServiceMeasurementUUID BAROMETRIC_PRESSURE = new ServiceMeasurementUUID(
+ ENVIRONMENTAL_SENSING_SERVICE,
+ new UUID(0x2A6D00001000L, 0x800000805f9b34fbL)
+ );
+
+ @Override
+ public List getServices() {
+ return List.of(BAROMETRIC_PRESSURE);
+ }
+
+ @Override
+ public Aggregator, ?> createEmptySensorData(String address) {
+ return null; //TODO
+ }
+
+ @Override
+ public void handlePayload(SensorManager.SensorDataChangedObserver observer, ServiceMeasurementUUID serviceMeasurementUUID, String sensorName, String address, BluetoothGattCharacteristic characteristic) {
+ //TODO
+ }
+
+ public static AtmosphericPressure parseEnvironmentalSensing(BluetoothGattCharacteristic characteristic) {
+ byte[] raw = characteristic.getValue();
+
+ if (raw.length < 4) {
+ return null;
+ }
+
+ Integer pressure = characteristic.getIntValue(BluetoothGattCharacteristic.FORMAT_UINT32, 0);
+ return AtmosphericPressure.ofPA(pressure / 10f);
+ }
+
+}
diff --git a/src/main/java/de/dennisguse/opentracks/sensors/BluetoothHandlerCyclingCadence.java b/src/main/java/de/dennisguse/opentracks/sensors/BluetoothHandlerCyclingCadence.java
new file mode 100644
index 000000000..74ad56d33
--- /dev/null
+++ b/src/main/java/de/dennisguse/opentracks/sensors/BluetoothHandlerCyclingCadence.java
@@ -0,0 +1,57 @@
+package de.dennisguse.opentracks.sensors;
+
+import android.bluetooth.BluetoothGattCharacteristic;
+import android.util.Log;
+import android.util.Pair;
+
+import java.util.List;
+
+import de.dennisguse.opentracks.sensors.sensorData.AggregatorCyclingCadence;
+import de.dennisguse.opentracks.sensors.sensorData.Raw;
+import de.dennisguse.opentracks.sensors.sensorData.SensorHandlerInterface;
+
+public class BluetoothHandlerCyclingCadence implements SensorHandlerInterface {
+ private static final String TAG = BluetoothHandlerCyclingCadence.class.getSimpleName();
+
+ public static final List CYCLING_CADENCE = List.of(
+ BluetoothHandlerManagerCyclingPower.CYCLING_POWER,
+ BluetoothHandlerCyclingDistanceSpeed.CYCLING_SPEED_CADENCE
+ );
+
+ @Override
+ public List getServices() {
+ return CYCLING_CADENCE;
+ }
+
+ @Override
+ public AggregatorCyclingCadence createEmptySensorData(String address) {
+ return new AggregatorCyclingCadence(address);
+ }
+
+ @Override
+ public void handlePayload(SensorManager.SensorDataChangedObserver observer, ServiceMeasurementUUID serviceMeasurementUUID, String sensorName, String address, BluetoothGattCharacteristic characteristic) {
+ if (serviceMeasurementUUID.equals(BluetoothHandlerManagerCyclingPower.CYCLING_POWER)) {
+ BluetoothHandlerManagerCyclingPower.Data data = BluetoothHandlerManagerCyclingPower.parseCyclingPower(characteristic);
+ if (data != null && data.crank() != null) {
+ observer.onChange(new Raw<>(data.crank()));
+ }
+ return;
+ }
+
+ if (serviceMeasurementUUID.equals(BluetoothHandlerCyclingDistanceSpeed.CYCLING_SPEED_CADENCE)) {
+ Pair data = BluetoothHandlerCyclingDistanceSpeed.parseCyclingCrankAndWheel(address, sensorName, characteristic);
+
+ if (data != null && data.second != null) {
+ observer.onChange(new Raw<>(data.second));
+ }
+ return;
+ }
+
+ Log.e(TAG, "Don't know how to decode this payload.");
+ }
+
+ public record CrankData(
+ long crankRevolutionsCount, // UINT32
+ int crankRevolutionsTime // UINT16; 1/1024s
+ ) {}
+}
diff --git a/src/main/java/de/dennisguse/opentracks/sensors/BluetoothHandlerCyclingDistanceSpeed.java b/src/main/java/de/dennisguse/opentracks/sensors/BluetoothHandlerCyclingDistanceSpeed.java
new file mode 100644
index 000000000..a0cb87a48
--- /dev/null
+++ b/src/main/java/de/dennisguse/opentracks/sensors/BluetoothHandlerCyclingDistanceSpeed.java
@@ -0,0 +1,82 @@
+package de.dennisguse.opentracks.sensors;
+
+import android.bluetooth.BluetoothGattCharacteristic;
+import android.util.Pair;
+
+import androidx.annotation.NonNull;
+import androidx.annotation.VisibleForTesting;
+
+import java.util.List;
+import java.util.UUID;
+
+import de.dennisguse.opentracks.sensors.sensorData.AggregatorCyclingDistanceSpeed;
+import de.dennisguse.opentracks.sensors.sensorData.Raw;
+import de.dennisguse.opentracks.sensors.sensorData.SensorHandlerInterface;
+
+public class BluetoothHandlerCyclingDistanceSpeed implements SensorHandlerInterface {
+
+ public static final ServiceMeasurementUUID CYCLING_SPEED_CADENCE = new ServiceMeasurementUUID(
+ new UUID(0x181600001000L, 0x800000805f9b34fbL),
+ new UUID(0x2A5B00001000L, 0x800000805f9b34fbL)
+ );
+
+ @Override
+ public List getServices() {
+ return List.of(CYCLING_SPEED_CADENCE);
+ }
+
+ @Override
+ public AggregatorCyclingDistanceSpeed createEmptySensorData(String address) {
+ return new AggregatorCyclingDistanceSpeed(address);
+ }
+
+ @Override
+ public void handlePayload(SensorManager.SensorDataChangedObserver observer, ServiceMeasurementUUID serviceMeasurementUUID, String sensorName, String address, BluetoothGattCharacteristic characteristic) {
+ Pair data = parseCyclingCrankAndWheel(address, sensorName, characteristic);
+ if (data.first != null) {
+ observer.onChange(new Raw<>(data.first));
+ }
+ }
+
+
+ @VisibleForTesting(otherwise = VisibleForTesting.PACKAGE_PRIVATE)
+ public static Pair 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) {
+ return null;
+ }
+
+ int flags = characteristic.getValue()[0];
+ boolean hasWheel = (flags & 0x01) > 0;
+ boolean hasCrank = (flags & 0x02) > 0;
+
+ int index = 1;
+ WheelData wheelData = null;
+ if (hasWheel && valueLength - index >= 6) {
+ long wheelTotalRevolutionCount = characteristic.getIntValue(BluetoothGattCharacteristic.FORMAT_UINT32, index);
+ index += 4;
+ int wheelTime = characteristic.getIntValue(BluetoothGattCharacteristic.FORMAT_UINT16, index); // 1/1024s
+ wheelData = new WheelData(wheelTotalRevolutionCount, wheelTime);
+ index += 2;
+ }
+
+ BluetoothHandlerCyclingCadence.CrankData crankData = 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
+ crankData = new BluetoothHandlerCyclingCadence.CrankData(crankCount, crankTime);
+ }
+
+ return new Pair<>(wheelData, crankData);
+ }
+
+ public record WheelData(
+
+ long wheelRevolutionsCount, // UINT32
+
+ int wheelRevolutionsTime // UINT16; 1/1024s
+ ) {}
+}
diff --git a/src/main/java/de/dennisguse/opentracks/sensors/BluetoothHandlerManagerCyclingPower.java b/src/main/java/de/dennisguse/opentracks/sensors/BluetoothHandlerManagerCyclingPower.java
new file mode 100644
index 000000000..149ec9207
--- /dev/null
+++ b/src/main/java/de/dennisguse/opentracks/sensors/BluetoothHandlerManagerCyclingPower.java
@@ -0,0 +1,87 @@
+package de.dennisguse.opentracks.sensors;
+
+import android.bluetooth.BluetoothGattCharacteristic;
+
+import androidx.annotation.NonNull;
+import androidx.annotation.VisibleForTesting;
+
+import java.util.List;
+import java.util.UUID;
+
+import de.dennisguse.opentracks.data.models.Power;
+import de.dennisguse.opentracks.sensors.sensorData.AggregatorCyclingPower;
+import de.dennisguse.opentracks.sensors.sensorData.Raw;
+import de.dennisguse.opentracks.sensors.sensorData.SensorHandlerInterface;
+
+public class BluetoothHandlerManagerCyclingPower implements SensorHandlerInterface {
+
+ public static final ServiceMeasurementUUID CYCLING_POWER = new ServiceMeasurementUUID(
+ new UUID(0x181800001000L, 0x800000805f9b34fbL),
+ new UUID(0x2A6300001000L, 0x800000805f9b34fbL)
+ );
+
+ @Override
+ public List getServices() {
+ return List.of(CYCLING_POWER);
+ }
+
+ @Override
+ public AggregatorCyclingPower createEmptySensorData(String address) {
+ return new AggregatorCyclingPower(address);
+ }
+
+ @Override
+ public void handlePayload(SensorManager.SensorDataChangedObserver observer, @NonNull ServiceMeasurementUUID serviceMeasurementUUID, String sensorName, String address, BluetoothGattCharacteristic characteristic) {
+ Data cyclingPower = parseCyclingPower(characteristic);
+
+ if (cyclingPower != null) {
+ observer.onChange(new Raw<>(cyclingPower));
+ }
+ }
+
+
+ @VisibleForTesting(otherwise = VisibleForTesting.PACKAGE_PRIVATE)
+ public static 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 == 0) {
+ return null;
+ }
+
+ 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;
+ }
+
+ BluetoothHandlerCyclingCadence.CrankData crankData = 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
+
+ crankData = new BluetoothHandlerCyclingCadence.CrankData(crankCount, crankTime);
+ }
+
+ return new Data(Power.of(instantaneousPower), crankData);
+ }
+
+ public record Data(Power power, BluetoothHandlerCyclingCadence.CrankData crank) {}
+
+}
diff --git a/src/main/java/de/dennisguse/opentracks/sensors/BluetoothHandlerManagerHeartRate.java b/src/main/java/de/dennisguse/opentracks/sensors/BluetoothHandlerManagerHeartRate.java
new file mode 100644
index 000000000..10ace7bc0
--- /dev/null
+++ b/src/main/java/de/dennisguse/opentracks/sensors/BluetoothHandlerManagerHeartRate.java
@@ -0,0 +1,70 @@
+package de.dennisguse.opentracks.sensors;
+
+import android.bluetooth.BluetoothGattCharacteristic;
+
+import androidx.annotation.NonNull;
+import androidx.annotation.VisibleForTesting;
+
+import java.util.List;
+import java.util.UUID;
+
+import de.dennisguse.opentracks.data.models.HeartRate;
+import de.dennisguse.opentracks.sensors.sensorData.AggregatorHeartRate;
+import de.dennisguse.opentracks.sensors.sensorData.Raw;
+import de.dennisguse.opentracks.sensors.sensorData.SensorHandlerInterface;
+
+public class BluetoothHandlerManagerHeartRate implements SensorHandlerInterface {
+
+ public static final ServiceMeasurementUUID HEARTRATE = new ServiceMeasurementUUID(
+ new UUID(0x180D00001000L, 0x800000805f9b34fbL),
+ new UUID(0x2A3700001000L, 0x800000805f9b34fbL)
+ );
+
+ // Used for device discovery in preferences
+ public static final List HEART_RATE_SUPPORTING_DEVICES = List.of(
+ HEARTRATE,
+ //Devices that support HEART_RATE_SERVICE_UUID, but do not announce HEART_RATE_SERVICE_UUID in there BLE announcement messages (during device discovery).
+ new ServiceMeasurementUUID(
+ UUID.fromString("0000fee0-0000-1000-8000-00805f9b34fb"), //Miband3
+ HEARTRATE.measurementUUID()
+ )
+ );
+
+ @Override
+ public List getServices() {
+ return HEART_RATE_SUPPORTING_DEVICES;
+ }
+
+ @Override
+ public AggregatorHeartRate createEmptySensorData(String address) {
+ return new AggregatorHeartRate(address);
+ }
+
+ @Override
+ public void handlePayload(SensorManager.SensorDataChangedObserver observer, @NonNull ServiceMeasurementUUID serviceMeasurementUUID, String sensorName, String address, BluetoothGattCharacteristic characteristic) {
+ HeartRate heartRate = parseHeartRate(characteristic);
+
+ if (heartRate != null) {
+ observer.onChange(new Raw<>(heartRate));
+ }
+ }
+
+ @VisibleForTesting
+ public static HeartRate parseHeartRate(BluetoothGattCharacteristic characteristic) {
+ //DOCUMENTATION https://www.bluetooth.com/wp-content/uploads/Sitecore-Media-Library/Gatt/Xml/Characteristics/org.bluetooth.characteristic.heart_rate_measurement.xml
+ byte[] raw = characteristic.getValue();
+ if (raw.length == 0) {
+ return null;
+ }
+
+ boolean formatUINT16 = ((raw[0] & 0x1) == 1);
+ if (formatUINT16 && raw.length >= 3) {
+ return HeartRate.of(characteristic.getIntValue(BluetoothGattCharacteristic.FORMAT_UINT16, 1));
+ }
+ if (!formatUINT16 && raw.length >= 2) {
+ return HeartRate.of(characteristic.getIntValue(BluetoothGattCharacteristic.FORMAT_UINT8, 1));
+ }
+
+ return null;
+ }
+}
diff --git a/src/main/java/de/dennisguse/opentracks/sensors/BluetoothHandlerRunningSpeedAndCadence.java b/src/main/java/de/dennisguse/opentracks/sensors/BluetoothHandlerRunningSpeedAndCadence.java
new file mode 100644
index 000000000..1a7f8a6f4
--- /dev/null
+++ b/src/main/java/de/dennisguse/opentracks/sensors/BluetoothHandlerRunningSpeedAndCadence.java
@@ -0,0 +1,88 @@
+package de.dennisguse.opentracks.sensors;
+
+import android.bluetooth.BluetoothGattCharacteristic;
+
+import androidx.annotation.NonNull;
+import androidx.annotation.VisibleForTesting;
+
+import java.util.List;
+import java.util.UUID;
+
+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.AggregatorRunning;
+import de.dennisguse.opentracks.sensors.sensorData.Raw;
+import de.dennisguse.opentracks.sensors.sensorData.SensorHandlerInterface;
+
+public class BluetoothHandlerRunningSpeedAndCadence implements SensorHandlerInterface {
+
+
+ public static final ServiceMeasurementUUID RUNNING_SPEED_CADENCE = new ServiceMeasurementUUID(
+ new UUID(0x181400001000L, 0x800000805f9b34fbL),
+ new UUID(0x2A5300001000L, 0x800000805f9b34fbL)
+ );
+
+ @Override
+ public List getServices() {
+ return List.of(RUNNING_SPEED_CADENCE);
+ }
+
+ @Override
+ public AggregatorRunning createEmptySensorData(String address) {
+ return new AggregatorRunning(address);
+ }
+
+ @Override
+ public void handlePayload(SensorManager.SensorDataChangedObserver observer, @NonNull ServiceMeasurementUUID serviceMeasurementUUID, String sensorName, String address, BluetoothGattCharacteristic characteristic) {
+ Data data = parseRunningSpeedAndCadence(sensorName, characteristic);
+ observer.onChange(new Raw<>(data));
+ }
+
+ @VisibleForTesting
+ public static Data parseRunningSpeedAndCadence(String sensorName, @NonNull BluetoothGattCharacteristic characteristic) {
+ // DOCUMENTATION https://www.bluetooth.com/wp-content/uploads/Sitecore-Media-Library/Gatt/Xml/Characteristics/org.bluetooth.characteristic.rsc_measurement.xml
+ int valueLength = characteristic.getValue().length;
+ if (valueLength == 0) {
+ return null;
+ }
+
+ int flags = characteristic.getValue()[0];
+ boolean hasStrideLength = (flags & 0x01) > 0;
+ boolean hasTotalDistance = (flags & 0x02) > 0;
+ boolean hasStatus = (flags & 0x03) > 0; // walking vs running
+
+ Speed speed = null;
+ Cadence cadence = null;
+ Distance totalDistance = null;
+
+ int index = 1;
+ if (valueLength - index >= 2) {
+ speed = Speed.of(characteristic.getIntValue(BluetoothGattCharacteristic.FORMAT_UINT16, index) / 256f);
+ }
+
+ index = 3;
+ if (valueLength - index >= 1) {
+ cadence = Cadence.of(characteristic.getIntValue(BluetoothGattCharacteristic.FORMAT_UINT8, index));
+
+ // Hacky workaround as the Wahoo Tickr X provides cadence in SPM (steps per minute) in violation to the standard.
+ if (sensorName != null && sensorName.startsWith("TICKR X")) {
+ cadence = Cadence.of(cadence.getRPM() / 2);
+ }
+ }
+
+ index = 4;
+ if (hasStrideLength && valueLength - index >= 2) {
+ Distance strideDistance = Distance.ofCM(characteristic.getIntValue(BluetoothGattCharacteristic.FORMAT_UINT16, index));
+ index += 2;
+ }
+
+ if (hasTotalDistance && valueLength - index >= 4) {
+ totalDistance = Distance.ofDM(characteristic.getIntValue(BluetoothGattCharacteristic.FORMAT_UINT32, index));
+ }
+
+ return new Data(speed, cadence, totalDistance);
+ }
+
+ public record Data(Speed speed, Cadence cadence, Distance totalDistance) {}
+}
diff --git a/src/main/java/de/dennisguse/opentracks/sensors/BluetoothRemoteSensorManager.java b/src/main/java/de/dennisguse/opentracks/sensors/BluetoothRemoteSensorManager.java
index 3e6ddc54f..96f62779b 100644
--- a/src/main/java/de/dennisguse/opentracks/sensors/BluetoothRemoteSensorManager.java
+++ b/src/main/java/de/dennisguse/opentracks/sensors/BluetoothRemoteSensorManager.java
@@ -29,7 +29,6 @@ import androidx.annotation.Nullable;
import java.time.Duration;
import de.dennisguse.opentracks.R;
-import de.dennisguse.opentracks.sensors.sensorData.SensorData;
import de.dennisguse.opentracks.settings.PreferencesUtils;
import de.dennisguse.opentracks.util.PermissionRequester;
@@ -47,29 +46,34 @@ import de.dennisguse.opentracks.util.PermissionRequester;
*
* @author Sandor Dornbush
*/
-public class BluetoothRemoteSensorManager implements SensorConnector, AbstractBluetoothConnectionManager.SensorDataObserver, SharedPreferences.OnSharedPreferenceChangeListener {
+public class BluetoothRemoteSensorManager implements SensorConnector, SharedPreferences.OnSharedPreferenceChangeListener {
private static final String TAG = BluetoothRemoteSensorManager.class.getSimpleName();
- public static final Duration MAX_SENSOR_DATE_SET_AGE = Duration.ofSeconds(5);
+ public static final Duration MAX_SENSOR_DATE_SET_AGE = Duration.ofSeconds(50); //TODO
private final BluetoothAdapter bluetoothAdapter;
private final Context context;
private final Handler handler;
- private final SensorManager.SensorDataChangedObserver observer;
private boolean started = false;
- private final BluetoothConnectionManagerHeartRate heartRate = new BluetoothConnectionManagerHeartRate(this);
- private final BluetoothConnectionManagerCyclingCadence cyclingCadence = new BluetoothConnectionManagerCyclingCadence(this);
- private final BluetoothConnectionManagerCyclingDistanceSpeed cyclingSpeed = new BluetoothConnectionManagerCyclingDistanceSpeed(this);
- private final BluetoothConnectionManagerCyclingPower cyclingPower = new BluetoothConnectionManagerCyclingPower(this);
- private final BluetoothConnectionRunningSpeedAndCadence runningSpeedAndCadence = new BluetoothConnectionRunningSpeedAndCadence(this);
+ private final BluetoothConnectionManager heartRate;
+ private final BluetoothConnectionManager cyclingCadence;
+ private final BluetoothConnectionManager cyclingSpeed;
+ private final BluetoothConnectionManager cyclingPower;
+ private final BluetoothConnectionManager runningSpeedAndCadence;
public BluetoothRemoteSensorManager(@NonNull Context context, @NonNull Handler handler, @Nullable SensorManager.SensorDataChangedObserver observer) {
this.context = context;
this.handler = handler;
- this.observer = observer;
bluetoothAdapter = BluetoothUtils.getAdapter(context);
+
+ this.heartRate = new BluetoothConnectionManager(observer, new BluetoothHandlerManagerHeartRate());
+ this.cyclingCadence = new BluetoothConnectionManager(observer, new BluetoothHandlerCyclingDistanceSpeed());
+ this.cyclingSpeed = new BluetoothConnectionManager(observer, new BluetoothHandlerCyclingDistanceSpeed());
+ this.cyclingPower = new BluetoothConnectionManager(observer, new BluetoothHandlerManagerCyclingPower());
+ this.runningSpeedAndCadence = new BluetoothConnectionManager(observer, new BluetoothHandlerRunningSpeedAndCadence());
+
}
@Override
@@ -95,7 +99,7 @@ public class BluetoothRemoteSensorManager implements SensorConnector, AbstractBl
return bluetoothAdapter != null && bluetoothAdapter.isEnabled();
}
- private synchronized void connect(AbstractBluetoothConnectionManager> connectionManager, String address) {
+ private synchronized void connect(BluetoothConnectionManager connectionManager, String address) {
if (!isEnabled()) {
Log.w(TAG, "Bluetooth not enabled.");
return;
@@ -120,28 +124,12 @@ public class BluetoothRemoteSensorManager implements SensorConnector, AbstractBl
Log.i(TAG, "Connecting to bluetooth address: " + address);
try {
BluetoothDevice device = bluetoothAdapter.getRemoteDevice(address);
- connectionManager.connect(context, device);
+ connectionManager.connect(context, handler, device);
} catch (IllegalArgumentException e) {
Log.e(TAG, "Unable to get remote device for: " + address, e);
}
}
- @Override
- public synchronized void onChanged(SensorData> sensorData) {
- observer.onChange(sensorData);
- }
-
- @Override
- public void onDisconnecting(SensorData> sensorData) {
- observer.onDisconnect(sensorData);
- }
-
- @NonNull
- @Override
- public Handler getHandler() {
- return handler;
- }
-
@Override
public void onSharedPreferenceChanged(SharedPreferences unused, @Nullable String key) {
if (!started) return;
diff --git a/src/main/java/de/dennisguse/opentracks/sensors/BluetoothUtils.java b/src/main/java/de/dennisguse/opentracks/sensors/BluetoothUtils.java
index 48e0bb296..c0f2f2d1f 100644
--- a/src/main/java/de/dennisguse/opentracks/sensors/BluetoothUtils.java
+++ b/src/main/java/de/dennisguse/opentracks/sensors/BluetoothUtils.java
@@ -21,23 +21,9 @@ import android.bluetooth.BluetoothManager;
import android.content.Context;
import android.util.Log;
-import androidx.annotation.NonNull;
-
-import java.util.List;
import java.util.UUID;
-import de.dennisguse.opentracks.data.models.AtmosphericPressure;
import de.dennisguse.opentracks.data.models.BatteryLevel;
-import de.dennisguse.opentracks.data.models.Cadence;
-import de.dennisguse.opentracks.data.models.Distance;
-import de.dennisguse.opentracks.data.models.HeartRate;
-import de.dennisguse.opentracks.data.models.Power;
-import de.dennisguse.opentracks.data.models.Speed;
-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;
/**
* Utilities for dealing with bluetooth devices.
@@ -53,52 +39,9 @@ public class BluetoothUtils {
new UUID(0x2A1900001000L, 0x800000805f9b34fbL)
);
- public static final ServiceMeasurementUUID HEARTRATE = new ServiceMeasurementUUID(
- new UUID(0x180D00001000L, 0x800000805f9b34fbL),
- new UUID(0x2A3700001000L, 0x800000805f9b34fbL)
- );
-
- // Used for device discovery in preferences
- public static final List HEART_RATE_SUPPORTING_DEVICES = List.of(
- HEARTRATE,
- //Devices that support HEART_RATE_SERVICE_UUID, but do not announce HEART_RATE_SERVICE_UUID in there BLE announcement messages (during device discovery).
- new ServiceMeasurementUUID(
- UUID.fromString("0000fee0-0000-1000-8000-00805f9b34fb"), //Miband3
- HEARTRATE.measurementUUID()
- )
- );
-
- private static final UUID ENVIRONMENTAL_SENSING_SERVICE = new UUID(0x181A00001000L, 0x800000805f9b34fbL);
- public static final ServiceMeasurementUUID BAROMETRIC_PRESSURE = new ServiceMeasurementUUID(
- ENVIRONMENTAL_SENSING_SERVICE,
- new UUID(0x2A6D00001000L, 0x800000805f9b34fbL)
- );
-
- public static final ServiceMeasurementUUID CYCLING_POWER = new ServiceMeasurementUUID(
- new UUID(0x181800001000L, 0x800000805f9b34fbL),
- new UUID(0x2A6300001000L, 0x800000805f9b34fbL)
- );
-
- public static final ServiceMeasurementUUID CYCLING_SPEED_CADENCE = new ServiceMeasurementUUID(
- new UUID(0x181600001000L, 0x800000805f9b34fbL),
- 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)
- );
private static final String TAG = BluetoothUtils.class.getSimpleName();
- private BluetoothUtils() {
- }
-
public static BluetoothAdapter getAdapter(Context context) {
BluetoothManager bluetoothManager = (BluetoothManager) context.getSystemService(Context.BLUETOOTH_SERVICE);
if (bluetoothManager == null) {
@@ -123,152 +66,4 @@ public class BluetoothUtils {
final int batteryLevel = characteristic.getIntValue(BluetoothGattCharacteristic.FORMAT_UINT8, 0);
return BatteryLevel.of(batteryLevel);
}
-
- public static HeartRate parseHeartRate(BluetoothGattCharacteristic characteristic) {
- //DOCUMENTATION https://www.bluetooth.com/wp-content/uploads/Sitecore-Media-Library/Gatt/Xml/Characteristics/org.bluetooth.characteristic.heart_rate_measurement.xml
- byte[] raw = characteristic.getValue();
- if (raw.length == 0) {
- return null;
- }
-
- boolean formatUINT16 = ((raw[0] & 0x1) == 1);
- if (formatUINT16 && raw.length >= 3) {
- return HeartRate.of(characteristic.getIntValue(BluetoothGattCharacteristic.FORMAT_UINT16, 1));
- }
- if (!formatUINT16 && raw.length >= 2) {
- return HeartRate.of(characteristic.getIntValue(BluetoothGattCharacteristic.FORMAT_UINT8, 1));
- }
-
- return null;
- }
-
- public static AtmosphericPressure parseEnvironmentalSensing(BluetoothGattCharacteristic characteristic) {
- byte[] raw = characteristic.getValue();
-
- if (raw.length < 4) {
- return null;
- }
-
- Integer pressure = characteristic.getIntValue(BluetoothGattCharacteristic.FORMAT_UINT32, 0);
- return AtmosphericPressure.ofPA(pressure / 10f);
- }
-
- 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) {
- return null;
- }
-
- 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 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) {
- return null;
- }
-
- int flags = characteristic.getValue()[0];
- boolean hasWheel = (flags & 0x01) > 0;
- boolean hasCrank = (flags & 0x02) > 0;
-
- int index = 1;
- 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 SensorDataCyclingDistanceSpeed(address, sensorName, wheelTotalRevolutionCount, wheelTime);
- index += 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 SensorDataCyclingCadenceAndDistanceSpeed(address, sensorName, cadence, speed);
- }
-
- public static SensorDataRunning parseRunningSpeedAndCadence(String address, String sensorName, @NonNull BluetoothGattCharacteristic characteristic) {
- // DOCUMENTATION https://www.bluetooth.com/wp-content/uploads/Sitecore-Media-Library/Gatt/Xml/Characteristics/org.bluetooth.characteristic.rsc_measurement.xml
- int valueLength = characteristic.getValue().length;
- if (valueLength == 0) {
- return null;
- }
-
- int flags = characteristic.getValue()[0];
- boolean hasStrideLength = (flags & 0x01) > 0;
- boolean hasTotalDistance = (flags & 0x02) > 0;
- boolean hasStatus = (flags & 0x03) > 0; // walking vs running
-
- Speed speed = null;
- Cadence cadence = null;
- Distance totalDistance = null;
-
- int index = 1;
- if (valueLength - index >= 2) {
- speed = Speed.of(characteristic.getIntValue(BluetoothGattCharacteristic.FORMAT_UINT16, index) / 256f);
- }
-
- index = 3;
- if (valueLength - index >= 1) {
- cadence = Cadence.of(characteristic.getIntValue(BluetoothGattCharacteristic.FORMAT_UINT8, index));
-
- // Hacky workaround as the Wahoo Tickr X provides cadence in SPM (steps per minute) in violation to the standard.
- if (sensorName != null && sensorName.startsWith("TICKR X")) {
- cadence = Cadence.of(cadence.getRPM() / 2);
- }
- }
-
- index = 4;
- if (hasStrideLength && valueLength - index >= 2) {
- Distance strideDistance = Distance.ofCM(characteristic.getIntValue(BluetoothGattCharacteristic.FORMAT_UINT16, index));
- index += 2;
- }
-
- if (hasTotalDistance && valueLength - index >= 4) {
- totalDistance = Distance.ofDM(characteristic.getIntValue(BluetoothGattCharacteristic.FORMAT_UINT32, index));
- }
-
- return new SensorDataRunning(address, sensorName, speed, cadence, totalDistance);
- }
}
diff --git a/src/main/java/de/dennisguse/opentracks/sensors/GainManager.java b/src/main/java/de/dennisguse/opentracks/sensors/GainManager.java
new file mode 100644
index 000000000..40e1749f9
--- /dev/null
+++ b/src/main/java/de/dennisguse/opentracks/sensors/GainManager.java
@@ -0,0 +1,46 @@
+package de.dennisguse.opentracks.sensors;
+
+import android.content.Context;
+import android.os.Handler;
+import android.util.Log;
+
+import de.dennisguse.opentracks.data.models.AtmosphericPressure;
+import de.dennisguse.opentracks.sensors.driver.BarometerInternal;
+import de.dennisguse.opentracks.sensors.sensorData.AggregatorBarometer;
+import de.dennisguse.opentracks.sensors.sensorData.Raw;
+
+/**
+ * Estimates the altitude gain and altitude loss using the device's pressure sensor (i.e., barometer).
+ */
+public class GainManager implements SensorConnector {
+
+ private static final String TAG = GainManager.class.getSimpleName();
+
+ private final BarometerInternal driver;
+
+ private final SensorManager.SensorDataChangedObserver listener;
+
+ public GainManager(SensorManager.SensorDataChangedObserver listener) {
+ this.listener = listener;
+ driver = new BarometerInternal();
+ }
+
+ public void start(Context context, Handler handler) {
+ driver.connect(context, handler, this);
+
+ if (driver.isConnected()) {
+ listener.onConnect(new AggregatorBarometer("internal"));
+ }
+ }
+
+ public void stop(Context context) {
+ Log.d(TAG, "Stop");
+
+ driver.disconnect(context);
+ listener.onDisconnect(new AggregatorBarometer("internal"));
+ }
+
+ public void onSensorValueChanged(AtmosphericPressure currentSensorValue) {
+ listener.onChange(new Raw<>(currentSensorValue));
+ }
+}
diff --git a/src/main/java/de/dennisguse/opentracks/sensors/SensorManager.java b/src/main/java/de/dennisguse/opentracks/sensors/SensorManager.java
index 789bee4be..385d69ca7 100644
--- a/src/main/java/de/dennisguse/opentracks/sensors/SensorManager.java
+++ b/src/main/java/de/dennisguse/opentracks/sensors/SensorManager.java
@@ -9,7 +9,8 @@ import androidx.annotation.Nullable;
import androidx.annotation.VisibleForTesting;
import de.dennisguse.opentracks.data.models.TrackPoint;
-import de.dennisguse.opentracks.sensors.sensorData.SensorData;
+import de.dennisguse.opentracks.sensors.sensorData.Aggregator;
+import de.dennisguse.opentracks.sensors.sensorData.Raw;
import de.dennisguse.opentracks.sensors.sensorData.SensorDataSet;
import de.dennisguse.opentracks.services.handlers.GPSManager;
import de.dennisguse.opentracks.services.handlers.TrackPointCreator;
@@ -27,13 +28,19 @@ public class SensorManager implements SharedPreferences.OnSharedPreferenceChange
private final SensorDataChangedObserver listener = new SensorDataChangedObserver() {
@Override
- public void onChange(SensorData> sensorData) {
- sensorDataSet.set(sensorData);
+ public void onConnect(Aggregator, ?> sensorData) {
+ sensorDataSet.add(sensorData);
observer.onChange(new SensorDataSet(sensorDataSet));
}
@Override
- public void onDisconnect(SensorData> sensorData) {
+ public void onChange(Raw> sensorData) {
+ sensorDataSet.update(sensorData);
+ observer.onChange(new SensorDataSet(sensorDataSet));
+ }
+
+ @Override
+ public void onDisconnect(Aggregator, ?> sensorData) {
sensorDataSet.remove(sensorData);
observer.onChange(new SensorDataSet(sensorDataSet));
}
@@ -41,7 +48,7 @@ public class SensorManager implements SharedPreferences.OnSharedPreferenceChange
private BluetoothRemoteSensorManager bluetoothSensorManager;
- private AltitudeSumManager altitudeSumManager;
+ private GainManager altitudeSumManager;
private GPSManager gpsManager;
@@ -51,7 +58,7 @@ public class SensorManager implements SharedPreferences.OnSharedPreferenceChange
public void start(Context context, Handler handler) {
gpsManager = new GPSManager(observer); //TODO Pass listener
- altitudeSumManager = new AltitudeSumManager();
+ altitudeSumManager = new GainManager(listener);
bluetoothSensorManager = new BluetoothRemoteSensorManager(context, handler, listener);
onSharedPreferenceChanged(null, null);
@@ -75,7 +82,6 @@ public class SensorManager implements SharedPreferences.OnSharedPreferenceChange
}
public SensorDataSet fill(TrackPoint trackPoint) {
- altitudeSumManager.fill(trackPoint);
sensorDataSet.fillTrackPoint(trackPoint);
return new SensorDataSet(sensorDataSet);
}
@@ -86,13 +92,12 @@ public class SensorManager implements SharedPreferences.OnSharedPreferenceChange
return;
}
sensorDataSet.reset();
- altitudeSumManager.reset();
}
@Deprecated
@VisibleForTesting
- public BluetoothRemoteSensorManager getBluetoothSensorManager() {
- return bluetoothSensorManager;
+ public void onChanged(Raw> data) {
+ listener.onChange(data);
}
public GPSManager getGpsManager() {
@@ -101,13 +106,13 @@ public class SensorManager implements SharedPreferences.OnSharedPreferenceChange
@Deprecated
@VisibleForTesting
- public AltitudeSumManager getAltitudeSumManager() {
+ public GainManager getAltitudeSumManager() {
return altitudeSumManager;
}
@Deprecated
@VisibleForTesting
- public void setAltitudeSumManager(AltitudeSumManager altitudeSumManager) {
+ public void setAltitudeSumManager(GainManager altitudeSumManager) {
this.altitudeSumManager = altitudeSumManager;
}
@@ -120,8 +125,10 @@ public class SensorManager implements SharedPreferences.OnSharedPreferenceChange
}
public interface SensorDataChangedObserver {
- void onChange(SensorData> sensorData);
- void onDisconnect(SensorData> sensorData);
+ void onConnect(Aggregator, ?> sensorData);
+ void onChange(Raw> sensorData);
+
+ void onDisconnect(Aggregator, ?> sensorData);
}
}
diff --git a/src/main/java/de/dennisguse/opentracks/sensors/driver/BarometerInternal.java b/src/main/java/de/dennisguse/opentracks/sensors/driver/BarometerInternal.java
index 8a405f720..4fc47b116 100644
--- a/src/main/java/de/dennisguse/opentracks/sensors/driver/BarometerInternal.java
+++ b/src/main/java/de/dennisguse/opentracks/sensors/driver/BarometerInternal.java
@@ -11,7 +11,7 @@ import android.util.Log;
import java.util.concurrent.TimeUnit;
import de.dennisguse.opentracks.data.models.AtmosphericPressure;
-import de.dennisguse.opentracks.sensors.AltitudeSumManager;
+import de.dennisguse.opentracks.sensors.GainManager;
public class BarometerInternal {
@@ -19,7 +19,7 @@ public class BarometerInternal {
private static final int SAMPLING_PERIOD = (int) TimeUnit.SECONDS.toMicros(5);
- private AltitudeSumManager observer;
+ private GainManager observer;
private final SensorEventListener listener = new SensorEventListener() {
@Override
@@ -38,14 +38,12 @@ public class BarometerInternal {
}
};
- public void connect(Context context, Handler handler, AltitudeSumManager observer) {
- this.observer = observer;
+ public void connect(Context context, Handler handler, GainManager observer) {
SensorManager sensorManager = (SensorManager) context.getSystemService(Context.SENSOR_SERVICE);
Sensor pressureSensor = sensorManager.getDefaultSensor(Sensor.TYPE_PRESSURE);
if (pressureSensor == null) {
Log.w(TAG, "No pressure sensor available.");
this.observer = null;
- return;
}
if (sensorManager.registerListener(listener, pressureSensor, SAMPLING_PERIOD, handler)) {
@@ -59,6 +57,7 @@ public class BarometerInternal {
public void disconnect(Context context) {
SensorManager sensorManager = (SensorManager) context.getSystemService(Context.SENSOR_SERVICE);
sensorManager.unregisterListener(listener);
+ observer = null;
}
public boolean isConnected() {
diff --git a/src/main/java/de/dennisguse/opentracks/sensors/sensorData/SensorData.java b/src/main/java/de/dennisguse/opentracks/sensors/sensorData/Aggregator.java
similarity index 59%
rename from src/main/java/de/dennisguse/opentracks/sensors/sensorData/SensorData.java
rename to src/main/java/de/dennisguse/opentracks/sensors/sensorData/Aggregator.java
index 365d6bc48..82ae79c2f 100644
--- a/src/main/java/de/dennisguse/opentracks/sensors/sensorData/SensorData.java
+++ b/src/main/java/de/dennisguse/opentracks/sensors/sensorData/Aggregator.java
@@ -1,48 +1,48 @@
package de.dennisguse.opentracks.sensors.sensorData;
import androidx.annotation.NonNull;
-import androidx.annotation.VisibleForTesting;
import java.time.Instant;
import de.dennisguse.opentracks.sensors.BluetoothRemoteSensorManager;
-public abstract class SensorData {
+public abstract class Aggregator {
- protected T value;
+ protected Raw previous;
+
+ protected Output value;
private final String sensorAddress;
private final String sensorName;
- private final Instant time;
-
- SensorData(String sensorAddress) {
+ Aggregator(String sensorAddress) {
this(sensorAddress, null);
}
- SensorData(String sensorAddress, String sensorName) {
- this(sensorAddress, sensorName, Instant.now());
- }
-
- @VisibleForTesting
- SensorData(String sensorAddress, String sensorName, Instant time) {
+ Aggregator(String sensorAddress, String sensorName) {
this.sensorAddress = sensorAddress;
this.sensorName = sensorName;
- this.time = time;
}
public String getSensorNameOrAddress() {
return sensorName != null ? sensorName : sensorAddress;
}
+ public final void add(Raw current) {
+ computeValue(current);
+ previous = current;
+ }
+
+ protected abstract void computeValue(Raw current);
+
public boolean hasValue() {
return value != null;
}
@NonNull
- protected abstract T getNoneValue();
+ protected abstract Output getNoneValue();
- public T getValue() {
+ public Output getValue() {
if (!hasValue()) {
return null;
}
@@ -55,20 +55,23 @@ public abstract class SensorData {
/**
* Reset long term aggregated values (more than derived from previous SensorData). e.g. overall distance.
*/
- public void reset() {
- }
+ public void reset() {};
/**
* Is the data recent considering the current time.
*/
private boolean isRecent() {
+ if (previous == null) {
+ return false;
+ }
+
return Instant.now()
- .isBefore(time.plus(BluetoothRemoteSensorManager.MAX_SENSOR_DATE_SET_AGE));
+ .isBefore(previous.time().plus(BluetoothRemoteSensorManager.MAX_SENSOR_DATE_SET_AGE));
}
@NonNull
@Override
public String toString() {
- return "sensorAddress='" + sensorAddress;
+ return "sensorAddress=" + sensorAddress + " data=" + value;
}
}
diff --git a/src/main/java/de/dennisguse/opentracks/sensors/sensorData/AggregatorBarometer.java b/src/main/java/de/dennisguse/opentracks/sensors/sensorData/AggregatorBarometer.java
new file mode 100644
index 000000000..7e5ed7c57
--- /dev/null
+++ b/src/main/java/de/dennisguse/opentracks/sensors/sensorData/AggregatorBarometer.java
@@ -0,0 +1,46 @@
+package de.dennisguse.opentracks.sensors.sensorData;
+
+import androidx.annotation.NonNull;
+
+import de.dennisguse.opentracks.data.models.Altitude;
+import de.dennisguse.opentracks.data.models.AltitudeGainLoss;
+import de.dennisguse.opentracks.data.models.AtmosphericPressure;
+import de.dennisguse.opentracks.sensors.PressureSensorUtils;
+
+public class AggregatorBarometer extends Aggregator {
+
+ private AtmosphericPressure lastAcceptedSensorValue;
+
+ public AggregatorBarometer(String sensorAddress) {
+ super(sensorAddress);
+ }
+
+ @Override
+ protected void computeValue(Raw current) {
+ if (previous == null) {
+ lastAcceptedSensorValue = current.value();
+ value = getNoneValue();
+ return;
+ }
+
+ PressureSensorUtils.AltitudeChange altitudeChange = PressureSensorUtils.computeChangesWithSmoothing_m(lastAcceptedSensorValue, previous.value(), current.value());
+ if (altitudeChange != null) {
+ value = new AltitudeGainLoss(value.gain_m() + altitudeChange.getAltitudeGain_m(), value.loss_m() + altitudeChange.getAltitudeLoss_m());
+
+ lastAcceptedSensorValue = altitudeChange.currentSensorValue();
+ }
+ }
+
+ @NonNull
+ @Override
+ protected AltitudeGainLoss getNoneValue() {
+ return new AltitudeGainLoss(0f, 0f);
+ }
+
+ @Override
+ public void reset() {
+ value = getNoneValue();
+ }
+
+ public record Data(Altitude gain, Altitude loss) {}
+}
diff --git a/src/main/java/de/dennisguse/opentracks/sensors/sensorData/AggregatorCyclingCadence.java b/src/main/java/de/dennisguse/opentracks/sensors/sensorData/AggregatorCyclingCadence.java
new file mode 100644
index 000000000..61a348354
--- /dev/null
+++ b/src/main/java/de/dennisguse/opentracks/sensors/sensorData/AggregatorCyclingCadence.java
@@ -0,0 +1,52 @@
+package de.dennisguse.opentracks.sensors.sensorData;
+
+import android.util.Log;
+
+import androidx.annotation.NonNull;
+
+import java.time.Duration;
+
+import de.dennisguse.opentracks.data.models.Cadence;
+import de.dennisguse.opentracks.sensors.BluetoothHandlerCyclingCadence;
+import de.dennisguse.opentracks.sensors.UintUtils;
+
+public class AggregatorCyclingCadence extends Aggregator {
+
+ private final String TAG = AggregatorCyclingCadence.class.getSimpleName();
+
+ public AggregatorCyclingCadence(String sensorAddress) {
+ super(sensorAddress);
+ }
+
+ public AggregatorCyclingCadence(String sensorAddress, String sensorName) {
+ super(sensorAddress, sensorName);
+ }
+
+ @Override
+ protected void computeValue(Raw current) {
+ if (previous != null) {
+ float timeDiff_ms = UintUtils.diff(current.value().crankRevolutionsTime(), previous.value().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 (current.value().crankRevolutionsCount() < previous.value().crankRevolutionsCount()) {
+ Log.e(TAG, "Crank revolutions count difference is invalid: cannot compute cadence.");
+ return;
+ }
+
+ long crankDiff = UintUtils.diff(current.value().crankRevolutionsCount(), previous.value().crankRevolutionsCount(), UintUtils.UINT32_MAX);
+ value = Cadence.of(crankDiff, timeDiff);
+ }
+ }
+
+ @NonNull
+ @Override
+ protected Cadence getNoneValue() {
+ return Cadence.of(0);
+ }
+}
diff --git a/src/main/java/de/dennisguse/opentracks/sensors/sensorData/AggregatorCyclingDistanceSpeed.java b/src/main/java/de/dennisguse/opentracks/sensors/sensorData/AggregatorCyclingDistanceSpeed.java
new file mode 100644
index 000000000..71374c019
--- /dev/null
+++ b/src/main/java/de/dennisguse/opentracks/sensors/sensorData/AggregatorCyclingDistanceSpeed.java
@@ -0,0 +1,77 @@
+package de.dennisguse.opentracks.sensors.sensorData;
+
+import android.util.Log;
+
+import androidx.annotation.NonNull;
+
+import java.time.Duration;
+
+import de.dennisguse.opentracks.data.models.Distance;
+import de.dennisguse.opentracks.data.models.Speed;
+import de.dennisguse.opentracks.sensors.BluetoothHandlerCyclingDistanceSpeed;
+import de.dennisguse.opentracks.sensors.UintUtils;
+
+public class AggregatorCyclingDistanceSpeed extends Aggregator {
+
+ private final String TAG = AggregatorCyclingDistanceSpeed.class.getSimpleName();
+
+ private Distance wheelCircumference;
+
+ public AggregatorCyclingDistanceSpeed(String sensorAddress) {
+ super(sensorAddress);
+ }
+
+ public AggregatorCyclingDistanceSpeed(String sensorAddress, String sensorName) {
+ super(sensorAddress, sensorName);
+ }
+
+ @Override
+ protected void computeValue(Raw current) {
+ if (previous != null) {
+ float timeDiff_ms = UintUtils.diff(current.value().wheelRevolutionsTime(), previous.value().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 (current.value().wheelRevolutionsCount() < previous.value().wheelRevolutionsCount()) {
+ Log.e(TAG, "Wheel revolutions count difference is invalid: cannot compute speed.");
+ return;
+ }
+ long wheelDiff = UintUtils.diff(current.value().wheelRevolutionsCount(), previous.value().wheelRevolutionsCount(), UintUtils.UINT32_MAX);
+
+ Distance distance = wheelCircumference.multipliedBy(wheelDiff);
+ Distance distanceOverall = distance;
+ if (value != null) {
+ distanceOverall = distance.plus(value.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
+ 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 setWheelCircumference(Distance wheelCircumference) {
+ this.wheelCircumference = wheelCircumference;
+ }
+
+ public record Data(Distance distance, Distance distanceOverall, Speed speed) {}
+}
diff --git a/src/main/java/de/dennisguse/opentracks/sensors/sensorData/AggregatorCyclingPower.java b/src/main/java/de/dennisguse/opentracks/sensors/sensorData/AggregatorCyclingPower.java
new file mode 100644
index 000000000..e60e08ee6
--- /dev/null
+++ b/src/main/java/de/dennisguse/opentracks/sensors/sensorData/AggregatorCyclingPower.java
@@ -0,0 +1,27 @@
+package de.dennisguse.opentracks.sensors.sensorData;
+
+import androidx.annotation.NonNull;
+
+import de.dennisguse.opentracks.data.models.Power;
+
+public class AggregatorCyclingPower extends Aggregator {
+
+ public AggregatorCyclingPower(String address) {
+ super(address);
+ }
+
+ public AggregatorCyclingPower(String name, String address) {
+ super(name, address);
+ }
+
+ @Override
+ public void computeValue(Raw current) {
+ this.value = current.value();
+ }
+
+ @NonNull
+ @Override
+ protected Power getNoneValue() {
+ return Power.of(0f);
+ }
+}
diff --git a/src/main/java/de/dennisguse/opentracks/sensors/sensorData/SensorDataHeartRate.java b/src/main/java/de/dennisguse/opentracks/sensors/sensorData/AggregatorHeartRate.java
similarity index 50%
rename from src/main/java/de/dennisguse/opentracks/sensors/sensorData/SensorDataHeartRate.java
rename to src/main/java/de/dennisguse/opentracks/sensors/sensorData/AggregatorHeartRate.java
index 3f02eca7d..68879cb59 100644
--- a/src/main/java/de/dennisguse/opentracks/sensors/sensorData/SensorDataHeartRate.java
+++ b/src/main/java/de/dennisguse/opentracks/sensors/sensorData/AggregatorHeartRate.java
@@ -4,21 +4,19 @@ import androidx.annotation.NonNull;
import de.dennisguse.opentracks.data.models.HeartRate;
-public class SensorDataHeartRate extends SensorData {
+public class AggregatorHeartRate extends Aggregator {
- public SensorDataHeartRate(String address) {
+ public AggregatorHeartRate(String address) {
super(address);
}
- public SensorDataHeartRate(String name, String address, @NonNull HeartRate heartRate) {
+ public AggregatorHeartRate(String name, String address) {
super(name, address);
- this.value = heartRate;
}
- @NonNull
@Override
- public String toString() {
- return super.toString() + " heart=" + value;
+ protected void computeValue(Raw current) {
+ this.value = current.value();
}
@NonNull
diff --git a/src/main/java/de/dennisguse/opentracks/sensors/sensorData/AggregatorRunning.java b/src/main/java/de/dennisguse/opentracks/sensors/sensorData/AggregatorRunning.java
new file mode 100644
index 000000000..425f20705
--- /dev/null
+++ b/src/main/java/de/dennisguse/opentracks/sensors/sensorData/AggregatorRunning.java
@@ -0,0 +1,61 @@
+package de.dennisguse.opentracks.sensors.sensorData;
+
+import androidx.annotation.NonNull;
+
+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.BluetoothHandlerRunningSpeedAndCadence;
+
+/**
+ * Provides cadence in rpm and speed in milliseconds from Bluetooth LE Running Speed and Cadence sensors.
+ */
+public final class AggregatorRunning extends Aggregator {
+
+ private static final String TAG = AggregatorRunning.class.getSimpleName();
+
+ public AggregatorRunning(String sensorAddress) {
+ super(sensorAddress);
+ }
+
+ public AggregatorRunning(String sensorAddress, String sensorName) {
+ super(sensorAddress, sensorName);
+ }
+
+ @Override
+ public void computeValue(Raw current) {
+ if (previous != null) {
+
+ Distance distance = null;
+ if (previous.value().totalDistance() != null && current.value().totalDistance() != null) {
+ distance = current.value().totalDistance().minus(previous.value().totalDistance());
+ if (value != null) {
+ distance = distance.plus(value.distance);
+ }
+ }
+
+ value = new Data(current.value().speed(), current.value().cadence(), distance);
+ }
+ }
+
+ @Override
+ public void reset() {
+ if (value != null) {
+ value = new Data(value.speed, value.cadence, Distance.of(0));
+ }
+ }
+
+ @NonNull
+ @Override
+ protected Data getNoneValue() {
+ if (value != null) {
+ return new Data(Speed.zero(), Cadence.of(0f), value.distance);
+ } else {
+ return new Data(Speed.zero(), Cadence.of(0f), Distance.of(0));
+ }
+ }
+
+ public record Data(Speed speed, Cadence cadence, @NonNull Distance distance) {
+ }
+}
+
diff --git a/src/main/java/de/dennisguse/opentracks/sensors/sensorData/Raw.java b/src/main/java/de/dennisguse/opentracks/sensors/sensorData/Raw.java
new file mode 100644
index 000000000..5f88bb229
--- /dev/null
+++ b/src/main/java/de/dennisguse/opentracks/sensors/sensorData/Raw.java
@@ -0,0 +1,16 @@
+package de.dennisguse.opentracks.sensors.sensorData;
+
+
+import androidx.annotation.NonNull;
+
+import java.time.Instant;
+
+public record Raw(
+ @NonNull T value,
+
+ @NonNull Instant time
+) {
+ public Raw(@NonNull T value) {
+ this(value, Instant.now()); //TODO We should be using the MonotonicClock
+ }
+}
diff --git a/src/main/java/de/dennisguse/opentracks/sensors/sensorData/SensorDataCyclingCadence.java b/src/main/java/de/dennisguse/opentracks/sensors/sensorData/SensorDataCyclingCadence.java
deleted file mode 100644
index 0fbdb193e..000000000
--- a/src/main/java/de/dennisguse/opentracks/sensors/sensorData/SensorDataCyclingCadence.java
+++ /dev/null
@@ -1,87 +0,0 @@
-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 comp)) return false;
-
- 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
deleted file mode 100644
index 26d4cd9c2..000000000
--- a/src/main/java/de/dennisguse/opentracks/sensors/sensorData/SensorDataCyclingCadenceAndDistanceSpeed.java
+++ /dev/null
@@ -1,27 +0,0 @@
-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
deleted file mode 100644
index 0e76713ac..000000000
--- a/src/main/java/de/dennisguse/opentracks/sensors/sensorData/SensorDataCyclingDistanceSpeed.java
+++ /dev/null
@@ -1,138 +0,0 @@
-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 comp)) return false;
-
- 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
deleted file mode 100644
index 6cf450da0..000000000
--- a/src/main/java/de/dennisguse/opentracks/sensors/sensorData/SensorDataCyclingPower.java
+++ /dev/null
@@ -1,46 +0,0 @@
-package de.dennisguse.opentracks.sensors.sensorData;
-
-import androidx.annotation.NonNull;
-import androidx.annotation.Nullable;
-
-import de.dennisguse.opentracks.data.models.Power;
-
-public class SensorDataCyclingPower extends SensorData {
-
- public SensorDataCyclingPower(String address) {
- super(address);
- }
-
- public SensorDataCyclingPower(String name, String address, Power power) {
- super(name, address);
- this.value = power;
- }
-
- @NonNull
- @Override
- protected Power getNoneValue() {
- return Power.of(0f);
- }
-
- @NonNull
- @Override
- public String toString() {
- return super.toString() + " data=" + value;
- }
-
- public record Data(SensorDataCyclingPower power, SensorDataCyclingCadence cadence) {
- public Data(SensorDataCyclingPower power, @Nullable SensorDataCyclingCadence cadence) {
- this.power = power;
- this.cadence = cadence;
- }
-
- @NonNull
- @Override
- public String toString() {
- return "Data{" +
- "power=" + power +
- ", cadence=" + cadence +
- '}';
- }
- }
-}
diff --git a/src/main/java/de/dennisguse/opentracks/sensors/sensorData/SensorDataRunning.java b/src/main/java/de/dennisguse/opentracks/sensors/sensorData/SensorDataRunning.java
deleted file mode 100644
index 58ef3abd4..000000000
--- a/src/main/java/de/dennisguse/opentracks/sensors/sensorData/SensorDataRunning.java
+++ /dev/null
@@ -1,89 +0,0 @@
-package de.dennisguse.opentracks.sensors.sensorData;
-
-import androidx.annotation.NonNull;
-import androidx.annotation.Nullable;
-import androidx.annotation.VisibleForTesting;
-
-import de.dennisguse.opentracks.data.models.Cadence;
-import de.dennisguse.opentracks.data.models.Distance;
-import de.dennisguse.opentracks.data.models.Speed;
-
-/**
- * Provides cadence in rpm and speed in milliseconds from Bluetooth LE Running Speed and Cadence sensors.
- */
-public final class SensorDataRunning extends SensorData {
-
- private static final String TAG = SensorDataRunning.class.getSimpleName();
-
- private final Speed speed;
-
- private final Cadence cadence;
-
- private final Distance totalDistance;
-
- public SensorDataRunning(String sensorAddress) {
- super(sensorAddress);
- this.speed = null;
- this.cadence = null;
- this.totalDistance = null;
- }
-
- public SensorDataRunning(String sensorAddress, String sensorName, Speed speed, Cadence cadence, Distance totalDistance) {
- super(sensorAddress, sensorName);
- this.speed = speed;
- this.cadence = cadence;
- this.totalDistance = totalDistance;
- }
-
- private boolean hasTotalDistance() {
- return totalDistance != null;
- }
-
-
- public Cadence getCadence() {
- return cadence;
- }
-
- public Speed getSpeed() {
- return speed;
- }
-
- @VisibleForTesting
- public Distance getTotalDistance() {
- return totalDistance;
- }
-
- @NonNull
- @Override
- protected Data getNoneValue() {
- if (value != null) {
- return new Data(Speed.zero(), Cadence.of(0f), value.distance);
- } else {
- return new Data(Speed.zero(), Cadence.of(0f), Distance.of(0));
- }
- }
-
- public void compute(SensorDataRunning previous) {
- if (speed != null && hasTotalDistance()) {
- Distance overallDistance = null;
- if (previous != null && previous.hasTotalDistance()) {
- overallDistance = this.totalDistance.minus(previous.totalDistance);
- if (previous.hasValue() && previous.getValue().distance() != null) {
- overallDistance = overallDistance.plus(previous.getValue().distance());
- }
- }
-
- value = new Data(speed, cadence, overallDistance);
- }
- }
-
- @Override
- public void reset() {
- if (value != null) {
- value = new Data(value.speed, value.cadence, Distance.of(0));
- }
- }
-
- public record Data(Speed speed, Cadence cadence, @Nullable Distance distance) {}
-}
-
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 1522f056e..485760817 100644
--- a/src/main/java/de/dennisguse/opentracks/sensors/sensorData/SensorDataSet.java
+++ b/src/main/java/de/dennisguse/opentracks/sensors/sensorData/SensorDataSet.java
@@ -5,27 +5,41 @@ import android.util.Pair;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
+import androidx.annotation.VisibleForTesting;
+import de.dennisguse.opentracks.data.models.AtmosphericPressure;
import de.dennisguse.opentracks.data.models.Cadence;
-import de.dennisguse.opentracks.data.models.Distance;
import de.dennisguse.opentracks.data.models.HeartRate;
+import de.dennisguse.opentracks.data.models.Power;
import de.dennisguse.opentracks.data.models.Speed;
import de.dennisguse.opentracks.data.models.TrackPoint;
+import de.dennisguse.opentracks.sensors.BluetoothHandlerCyclingCadence;
+import de.dennisguse.opentracks.sensors.BluetoothHandlerCyclingDistanceSpeed;
+import de.dennisguse.opentracks.sensors.BluetoothHandlerManagerCyclingPower;
+import de.dennisguse.opentracks.sensors.BluetoothHandlerRunningSpeedAndCadence;
import de.dennisguse.opentracks.settings.PreferencesUtils;
public final class SensorDataSet {
private static final String TAG = SensorDataSet.class.getSimpleName();
- private SensorDataHeartRate heartRate;
+ @VisibleForTesting
+ public AggregatorHeartRate heartRate;
- private SensorDataCyclingCadence cyclingCadence;
+ @VisibleForTesting
+ public AggregatorCyclingCadence cyclingCadence;
- private SensorDataCyclingDistanceSpeed cyclingDistanceSpeed;
+ @VisibleForTesting
+ public AggregatorCyclingDistanceSpeed cyclingDistanceSpeed;
- private SensorDataCyclingPower cyclingPower;
+ @VisibleForTesting
+ public AggregatorCyclingPower cyclingPower;
- private SensorDataRunning runningDistanceSpeedCadence;
+ @VisibleForTesting
+ public AggregatorRunning runningDistanceSpeedCadence;
+
+ @VisibleForTesting
+ public AggregatorBarometer barometer;
public SensorDataSet() {
}
@@ -36,6 +50,7 @@ public final class SensorDataSet {
this.cyclingDistanceSpeed = toCopy.cyclingDistanceSpeed;
this.cyclingPower = toCopy.cyclingPower;
this.runningDistanceSpeedCadence = toCopy.runningDistanceSpeedCadence;
+ this.barometer = toCopy.barometer;
}
public Pair getHeartRate() {
@@ -51,55 +66,79 @@ public final class SensorDataSet {
return new Pair<>(cyclingCadence.getValue(), cyclingCadence.getSensorNameOrAddress());
}
- if (runningDistanceSpeedCadence != null) {
- return new Pair<>(runningDistanceSpeedCadence.getCadence(), runningDistanceSpeedCadence.getSensorNameOrAddress());
+ if (runningDistanceSpeedCadence != null && runningDistanceSpeedCadence.hasValue() && runningDistanceSpeedCadence.value.cadence() != null) {
+ return new Pair<>(runningDistanceSpeedCadence.value.cadence(), runningDistanceSpeedCadence.getSensorNameOrAddress());
}
return null;
}
public Pair getSpeed() {
- if (cyclingDistanceSpeed != null && cyclingDistanceSpeed.hasValue() && cyclingDistanceSpeed.getValue().getSpeed() != null) {
- return new Pair<>(cyclingDistanceSpeed.getValue().getSpeed(), cyclingDistanceSpeed.getSensorNameOrAddress());
+ if (cyclingDistanceSpeed != null && cyclingDistanceSpeed.hasValue() && cyclingDistanceSpeed.getValue().speed() != null) {
+ return new Pair<>(cyclingDistanceSpeed.getValue().speed(), cyclingDistanceSpeed.getSensorNameOrAddress());
}
if (runningDistanceSpeedCadence != null && runningDistanceSpeedCadence.hasValue() && runningDistanceSpeedCadence.getValue().speed() != null) {
- return new Pair<>(runningDistanceSpeedCadence.getSpeed(), runningDistanceSpeedCadence.getSensorNameOrAddress());
+ return new Pair<>(runningDistanceSpeedCadence.value.speed(), runningDistanceSpeedCadence.getSensorNameOrAddress());
}
return null;
}
- public SensorDataCyclingCadence getCyclingCadence() {
- return cyclingCadence;
- }
-
- public SensorDataCyclingDistanceSpeed getCyclingDistanceSpeed() {
- return cyclingDistanceSpeed;
- }
-
- public SensorDataCyclingPower getCyclingPower() {
+ public AggregatorCyclingPower getCyclingPower() {
return cyclingPower;
}
- public SensorDataRunning getRunningDistanceSpeedCadence() {
- return runningDistanceSpeedCadence;
- }
-
- public void set(SensorData> data) {
+ public void add(@NonNull Aggregator, ?> data) {
set(data, data);
}
- public void remove(SensorData> type) {
+ public void update(@NonNull Raw> data) {
+ Record value = data.value();
+
+ if (value instanceof HeartRate) {
+ this.heartRate.add((Raw) data);
+ return;
+ }
+
+ if (value instanceof BluetoothHandlerCyclingCadence.CrankData) {
+ this.cyclingCadence.add((Raw) data);
+ return;
+ }
+ if (value instanceof BluetoothHandlerCyclingDistanceSpeed.WheelData ) {
+ this.cyclingDistanceSpeed.setWheelCircumference(PreferencesUtils.getWheelCircumference()); //TODO Fetch once and then listen for changes.
+ this.cyclingDistanceSpeed.add((Raw) data);
+ return;
+ }
+ if (value instanceof BluetoothHandlerRunningSpeedAndCadence.Data) {
+ this.runningDistanceSpeedCadence.add((Raw) data);
+
+ return;
+ }
+ if (value instanceof BluetoothHandlerManagerCyclingPower.Data) {
+ this.cyclingPower.add((Raw) data);
+ return;
+ }
+ if (value instanceof AtmosphericPressure) {
+ this.barometer.add((Raw) data);
+ return;
+ }
+
+ throw new UnsupportedOperationException(data.getClass().getCanonicalName() + " " + data.value().getClass().getCanonicalName());
+ }
+
+ public void remove(@NonNull Aggregator, ?> type) {
set(type, null);
}
public void clear() {
+ Log.i(TAG, "Removing all aggregators");
this.heartRate = null;
this.cyclingCadence = null;
this.cyclingDistanceSpeed = null;
this.cyclingPower = null;
this.runningDistanceSpeedCadence = null;
+ this.barometer = null;
}
public void fillTrackPoint(TrackPoint trackPoint) {
@@ -116,7 +155,7 @@ public final class SensorDataSet {
}
if (cyclingDistanceSpeed != null && cyclingDistanceSpeed.hasValue()) {
- trackPoint.setSensorDistance(cyclingDistanceSpeed.getValue().getDistanceOverall());
+ trackPoint.setSensorDistance(cyclingDistanceSpeed.getValue().distanceOverall());
}
if (cyclingPower != null && cyclingPower.hasValue()) {
@@ -126,81 +165,49 @@ public final class SensorDataSet {
if (runningDistanceSpeedCadence != null && runningDistanceSpeedCadence.hasValue()) {
trackPoint.setSensorDistance(runningDistanceSpeedCadence.getValue().distance());
}
+
+ if (barometer != null && barometer.hasValue()) {
+ trackPoint.setAltitudeGain(barometer.getValue().gain_m());
+ trackPoint.setAltitudeLoss(barometer.getValue().loss_m());
+ }
}
public void reset() {
+ Log.i(TAG, "Resetting data");
+
if (heartRate != null) heartRate.reset();
if (cyclingCadence != null) cyclingCadence.reset();
if (cyclingDistanceSpeed != null) cyclingDistanceSpeed.reset();
if (cyclingPower != null) cyclingPower.reset();
if (runningDistanceSpeedCadence != null) runningDistanceSpeedCadence.reset();
+ if (barometer != null) barometer.reset();
}
- @NonNull
- @Override
- public String toString() {
- return (heartRate != null ? "" + heartRate : "")
- + (cyclingCadence != null ? " " + cyclingCadence : "")
- + (cyclingDistanceSpeed != null ? " " + cyclingDistanceSpeed : "")
- + (cyclingPower != null ? " " + cyclingPower : "")
- + (runningDistanceSpeedCadence != null ? " " + runningDistanceSpeedCadence : "");
- }
+ private void set(@NonNull Aggregator, ?> type, @Nullable Aggregator, ?> sensorData) {
+ Log.i(TAG, "Setting aggregator " + type.getClass().getCanonicalName());
- private void set(@NonNull SensorData> type, @Nullable SensorData> sensorData) {
- if (type instanceof SensorDataHeartRate) {
- this.heartRate = (SensorDataHeartRate) sensorData;
+ if (type instanceof AggregatorHeartRate) {
+ heartRate = (AggregatorHeartRate) sensorData;
return;
}
-
- if (type instanceof SensorDataCyclingCadence) {
- SensorDataCyclingCadence previous = getCyclingCadence();
- Log.d(TAG, "Previous: " + previous + "; current: " + sensorData);
-
- if (sensorData != null && sensorData.equals(previous)) {
- Log.d(TAG, "onChanged: cadence data repeated.");
- return;
- }
-
- this.cyclingCadence = (SensorDataCyclingCadence) sensorData;
- if (this.cyclingCadence != null) {
- this.cyclingCadence.compute(previous);
- }
+ if (type instanceof AggregatorCyclingCadence) {
+ cyclingCadence = (AggregatorCyclingCadence) sensorData;
return;
}
-
- if (type instanceof SensorDataCyclingDistanceSpeed) {
- SensorDataCyclingDistanceSpeed previous = getCyclingDistanceSpeed();
- Log.d(TAG, "Previous: " + previous + "; Current" + sensorData);
- if (sensorData != null && sensorData.equals(previous)) {
- Log.d(TAG, "onChanged: cycling speed data repeated.");
- return;
- }
- Distance preferenceWheelCircumference = PreferencesUtils.getWheelCircumference(); //TODO Fetch once and then listen for changes.
-
- this.cyclingDistanceSpeed = (SensorDataCyclingDistanceSpeed) sensorData;
- if (this.cyclingDistanceSpeed != null) {
- this.cyclingDistanceSpeed.compute(previous, preferenceWheelCircumference);
- }
+ if (type instanceof AggregatorCyclingDistanceSpeed) {
+ cyclingDistanceSpeed = (AggregatorCyclingDistanceSpeed) sensorData;
return;
}
-
- if (type instanceof SensorDataCyclingPower) {
- this.cyclingPower = (SensorDataCyclingPower) sensorData;
+ if (type instanceof AggregatorCyclingPower) {
+ cyclingPower = (AggregatorCyclingPower) sensorData;
return;
}
-
- if (type instanceof SensorDataRunning) {
- SensorDataRunning previous = getRunningDistanceSpeedCadence();
- Log.d(TAG, "Previous: " + previous + "; Current" + sensorData);
- if (sensorData != null && sensorData.equals(previous)) {
- Log.d(TAG, "onChanged: running speed data repeated.");
- return;
- }
-
- this.runningDistanceSpeedCadence = (SensorDataRunning) sensorData;
- if (this.runningDistanceSpeedCadence != null) {
- this.runningDistanceSpeedCadence.compute(previous);
- }
+ if (type instanceof AggregatorRunning) {
+ runningDistanceSpeedCadence = (AggregatorRunning) sensorData;
+ return;
+ }
+ if (type instanceof AggregatorBarometer) {
+ barometer = (AggregatorBarometer) sensorData;
return;
}
diff --git a/src/main/java/de/dennisguse/opentracks/sensors/sensorData/SensorHandlerInterface.java b/src/main/java/de/dennisguse/opentracks/sensors/sensorData/SensorHandlerInterface.java
new file mode 100644
index 000000000..89e409f5a
--- /dev/null
+++ b/src/main/java/de/dennisguse/opentracks/sensors/sensorData/SensorHandlerInterface.java
@@ -0,0 +1,17 @@
+package de.dennisguse.opentracks.sensors.sensorData;
+
+import android.bluetooth.BluetoothGattCharacteristic;
+
+import java.util.List;
+
+import de.dennisguse.opentracks.sensors.SensorManager;
+import de.dennisguse.opentracks.sensors.ServiceMeasurementUUID;
+
+public interface SensorHandlerInterface {
+
+ List getServices();
+
+ Aggregator, ?> createEmptySensorData(String address);
+
+ void handlePayload(SensorManager.SensorDataChangedObserver observer, ServiceMeasurementUUID serviceMeasurementUUID, String sensorName, String address, BluetoothGattCharacteristic characteristic);
+}
diff --git a/src/main/java/de/dennisguse/opentracks/services/handlers/TrackPointCreator.java b/src/main/java/de/dennisguse/opentracks/services/handlers/TrackPointCreator.java
index 49384b291..f779e6ddc 100644
--- a/src/main/java/de/dennisguse/opentracks/services/handlers/TrackPointCreator.java
+++ b/src/main/java/de/dennisguse/opentracks/services/handlers/TrackPointCreator.java
@@ -34,7 +34,7 @@ public class TrackPointCreator implements SharedPreferences.OnSharedPreferenceCh
@NonNull
private Clock clock = new MonotonicClock();
- private SensorManager sensorManager;
+ private final SensorManager sensorManager;
public TrackPointCreator(Callback service) {
this.service = service;
@@ -64,7 +64,7 @@ public class TrackPointCreator implements SharedPreferences.OnSharedPreferenceCh
return sensorManager.fill(trackPoint);
}
- public synchronized void stop() {
+ public void stop() {
sensorManager.stop(context);
this.context = null;
}
@@ -139,11 +139,6 @@ public class TrackPointCreator implements SharedPreferences.OnSharedPreferenceCh
return sensorManager;
}
- @VisibleForTesting
- public void setSensorManager(SensorManager sensorManager) {
- this.sensorManager = sensorManager;
- }
-
@VisibleForTesting
public void setClock(@NonNull String time) {
this.clock = Clock.fixed(Instant.parse(time), ZoneId.of("CET"));
diff --git a/src/main/java/de/dennisguse/opentracks/settings/bluetooth/BluetoothLeCyclingCadencePreference.java b/src/main/java/de/dennisguse/opentracks/settings/bluetooth/BluetoothLeCyclingCadencePreference.java
index bf9577621..ce0d9a2f7 100644
--- a/src/main/java/de/dennisguse/opentracks/settings/bluetooth/BluetoothLeCyclingCadencePreference.java
+++ b/src/main/java/de/dennisguse/opentracks/settings/bluetooth/BluetoothLeCyclingCadencePreference.java
@@ -5,7 +5,7 @@ import android.util.AttributeSet;
import androidx.preference.PreferenceDialogFragmentCompat;
-import de.dennisguse.opentracks.sensors.BluetoothUtils;
+import de.dennisguse.opentracks.sensors.BluetoothHandlerCyclingCadence;
public class BluetoothLeCyclingCadencePreference extends BluetoothLeSensorPreference {
@@ -27,6 +27,6 @@ public class BluetoothLeCyclingCadencePreference extends BluetoothLeSensorPrefer
@Override
public PreferenceDialogFragmentCompat createInstance() {
- return BluetoothLeSensorPreferenceDialog.newInstance(getKey(), BluetoothUtils.CYCLING_CADENCE);
+ return BluetoothLeSensorPreferenceDialog.newInstance(getKey(), BluetoothHandlerCyclingCadence.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 af020ce07..025251bb0 100644
--- a/src/main/java/de/dennisguse/opentracks/settings/bluetooth/BluetoothLeCyclingPowerPreference.java
+++ b/src/main/java/de/dennisguse/opentracks/settings/bluetooth/BluetoothLeCyclingPowerPreference.java
@@ -5,7 +5,7 @@ import android.util.AttributeSet;
import androidx.preference.PreferenceDialogFragmentCompat;
-import de.dennisguse.opentracks.sensors.BluetoothUtils;
+import de.dennisguse.opentracks.sensors.BluetoothHandlerManagerCyclingPower;
public class BluetoothLeCyclingPowerPreference extends BluetoothLeSensorPreference {
@@ -27,6 +27,6 @@ public class BluetoothLeCyclingPowerPreference extends BluetoothLeSensorPreferen
@Override
public PreferenceDialogFragmentCompat createInstance() {
- return BluetoothLeSensorPreference.BluetoothLeSensorPreferenceDialog.newInstance(getKey(), BluetoothUtils.CYCLING_POWER);
+ return BluetoothLeSensorPreference.BluetoothLeSensorPreferenceDialog.newInstance(getKey(), BluetoothHandlerManagerCyclingPower.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
index db918d97d..5b37a9ca3 100644
--- a/src/main/java/de/dennisguse/opentracks/settings/bluetooth/BluetoothLeCyclingSpeedPreference.java
+++ b/src/main/java/de/dennisguse/opentracks/settings/bluetooth/BluetoothLeCyclingSpeedPreference.java
@@ -5,7 +5,7 @@ import android.util.AttributeSet;
import androidx.preference.PreferenceDialogFragmentCompat;
-import de.dennisguse.opentracks.sensors.BluetoothUtils;
+import de.dennisguse.opentracks.sensors.BluetoothHandlerCyclingDistanceSpeed;
public class BluetoothLeCyclingSpeedPreference extends BluetoothLeSensorPreference {
@@ -27,6 +27,6 @@ public class BluetoothLeCyclingSpeedPreference extends BluetoothLeSensorPreferen
@Override
public PreferenceDialogFragmentCompat createInstance() {
- return BluetoothLeSensorPreference.BluetoothLeSensorPreferenceDialog.newInstance(getKey(), BluetoothUtils.CYCLING_SPEED_CADENCE);
+ return BluetoothLeSensorPreference.BluetoothLeSensorPreferenceDialog.newInstance(getKey(), BluetoothHandlerCyclingDistanceSpeed.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 db6df1979..17183bf53 100644
--- a/src/main/java/de/dennisguse/opentracks/settings/bluetooth/BluetoothLeHeartRatePreference.java
+++ b/src/main/java/de/dennisguse/opentracks/settings/bluetooth/BluetoothLeHeartRatePreference.java
@@ -5,7 +5,7 @@ import android.util.AttributeSet;
import androidx.preference.PreferenceDialogFragmentCompat;
-import de.dennisguse.opentracks.sensors.BluetoothUtils;
+import de.dennisguse.opentracks.sensors.BluetoothHandlerManagerHeartRate;
public class BluetoothLeHeartRatePreference extends BluetoothLeSensorPreference {
@@ -27,6 +27,6 @@ public class BluetoothLeHeartRatePreference extends BluetoothLeSensorPreference
@Override
public PreferenceDialogFragmentCompat createInstance() {
- return BluetoothLeSensorPreference.BluetoothLeSensorPreferenceDialog.newInstance(getKey(), BluetoothUtils.HEART_RATE_SUPPORTING_DEVICES);
+ return BluetoothLeSensorPreference.BluetoothLeSensorPreferenceDialog.newInstance(getKey(), BluetoothHandlerManagerHeartRate.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 856c79b92..d857ace9e 100644
--- a/src/main/java/de/dennisguse/opentracks/settings/bluetooth/BluetoothLeRunningSpeedAndCadencePreference.java
+++ b/src/main/java/de/dennisguse/opentracks/settings/bluetooth/BluetoothLeRunningSpeedAndCadencePreference.java
@@ -5,7 +5,7 @@ import android.util.AttributeSet;
import androidx.preference.PreferenceDialogFragmentCompat;
-import de.dennisguse.opentracks.sensors.BluetoothUtils;
+import de.dennisguse.opentracks.sensors.BluetoothHandlerRunningSpeedAndCadence;
public class BluetoothLeRunningSpeedAndCadencePreference extends BluetoothLeSensorPreference {
@@ -27,6 +27,6 @@ public class BluetoothLeRunningSpeedAndCadencePreference extends BluetoothLeSens
@Override
public PreferenceDialogFragmentCompat createInstance() {
- return BluetoothLeSensorPreference.BluetoothLeSensorPreferenceDialog.newInstance(getKey(), BluetoothUtils.RUNNING_SPEED_CADENCE);
+ return BluetoothLeSensorPreference.BluetoothLeSensorPreferenceDialog.newInstance(getKey(), BluetoothHandlerRunningSpeedAndCadence.RUNNING_SPEED_CADENCE);
}
}