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 6743393fc..bf12ed3ab 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 @@ -66,7 +66,8 @@ import de.dennisguse.opentracks.data.models.TrackBuilder; 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.BluetoothHandlerCyclingPower; +import de.dennisguse.opentracks.sensors.SensorManager; +import de.dennisguse.opentracks.sensors.driver.CyclingPowerBluetooth; import de.dennisguse.opentracks.sensors.sensorData.Aggregator; import de.dennisguse.opentracks.sensors.sensorData.AggregatorBarometer; import de.dennisguse.opentracks.sensors.sensorData.AggregatorCyclingCadence; @@ -74,10 +75,9 @@ import de.dennisguse.opentracks.sensors.sensorData.AggregatorCyclingDistanceSpee import de.dennisguse.opentracks.sensors.sensorData.AggregatorCyclingPower; import de.dennisguse.opentracks.sensors.sensorData.AggregatorHeartRate; import de.dennisguse.opentracks.sensors.sensorData.AggregatorTemperature; -import de.dennisguse.opentracks.sensors.sensorData.Raw; -import de.dennisguse.opentracks.sensors.sensorData.SensorDataAggregator; import de.dennisguse.opentracks.services.TrackRecordingService; import de.dennisguse.opentracks.services.handlers.TrackPointCreator; +import de.dennisguse.opentracks.settings.PreferencesUtils; /** * Export a track to {@link TrackFileFormat} and verify that the import is identical. @@ -89,7 +89,7 @@ import de.dennisguse.opentracks.services.handlers.TrackPointCreator; public class ExportImportTest { @Rule - public final ServiceTestRule mServiceRule = ServiceTestRule.withTimeout(5, TimeUnit.SECONDS); + public final ServiceTestRule mServiceRule = ServiceTestRule.withTimeout(50, TimeUnit.SECONDS); //TODO 5s @Rule public GrantPermissionRule mGrantPermissionRule = TestUtil.createGrantPermissionRule(); @@ -98,13 +98,15 @@ public class ExportImportTest { @Rule public TimezoneRule timezoneRule = new TimezoneRule(TimeZone.getTimeZone("Europe/Berlin")); + private static final Context context = ApplicationProvider.getApplicationContext(); + @BeforeClass public static void preSetUp() { // Prepare looper for Android's message queue if (Looper.myLooper() == null) Looper.prepare(); - } - private final Context context = ApplicationProvider.getApplicationContext(); + PreferencesUtils.resetPreferences(context, true); + } private final ContentProviderUtils contentProviderUtils = new ContentProviderUtils(context); @@ -162,7 +164,7 @@ public class ExportImportTest { "Marker 1 desc", "Marker 1 typeLocalized", new Position( - Instant.parse("2020-02-02T02:02:03Z"), + Instant.parse("2020-02-02T02:02:03Z"), 3.1234567, 14.0014567, Distance.of(10), Altitude.WGS84.of(1020.25), @@ -197,7 +199,7 @@ public class ExportImportTest { )); trackPointCreator.setClock("2020-02-02T02:02:18Z"); - trackPointCreator.getSensorManager().sensorDataAggregator = new SensorDataAggregator(); + trackPointCreator.getSensorManager().clear(); service.endCurrentTrack(); trackPointCreator.setClock("2020-02-02T02:03:20Z"); @@ -212,7 +214,7 @@ public class ExportImportTest { sendLocation(trackPointCreator, "2020-02-02T02:03:50Z", 3.1234567, 16.001, 10, 27, 15, 999.123, 0f); - trackPointCreator.getSensorManager().sensorDataAggregator = new SensorDataAggregator(); + trackPointCreator.getSensorManager().clear(); trackPointCreator.setClock("2020-02-02T02:04:00Z"); service.endCurrentTrack(); @@ -660,17 +662,17 @@ public class ExportImportTest { } } - private void mockSensorData(TrackPointCreator trackPointCreator, Float speed, Distance distance, float heartRate, float cadence, Float power, Float altitudeGain, float temperature) { - SensorDataAggregator sensorDataAggregator = trackPointCreator.getSensorManager().sensorDataAggregator; + private static void mockSensorData(TrackPointCreator trackPointCreator, Float speed, Distance distance, float heartRate, float cadence, Float power, Float altitudeGain, float temperature) { + SensorManager sensorManager = trackPointCreator.getSensorManager(); AggregatorCyclingPower cyclingPower = new AggregatorCyclingPower("", ""); - cyclingPower.add(new Raw<>(trackPointCreator.createNow(), new BluetoothHandlerCyclingPower.Data(Power.of(power), null))); - sensorDataAggregator.add(cyclingPower); + cyclingPower.add(trackPointCreator.getNow(), new CyclingPowerBluetooth.Data(Power.of(power), null)); + sensorManager.setAggregator(cyclingPower); AggregatorHeartRate avgHeartRate = new AggregatorHeartRate("", ""); - avgHeartRate.add(new Raw<>(trackPointCreator.createNow(), HeartRate.of(heartRate))); - sensorDataAggregator.add(avgHeartRate); + avgHeartRate.add(trackPointCreator.getNow(), HeartRate.of(heartRate)); + sensorManager.setAggregator(avgHeartRate); AggregatorCyclingCadence cyclingCadence = new AggregatorCyclingCadence("", "") { @NonNull @@ -684,7 +686,7 @@ public class ExportImportTest { return true; } }; - sensorDataAggregator.add(cyclingCadence); + sensorManager.setAggregator(cyclingCadence); if (distance != null && speed != null) { AggregatorCyclingDistanceSpeed aggregatorCyclingDistanceSpeed = new AggregatorCyclingDistanceSpeed("", "") { @@ -699,14 +701,14 @@ public class ExportImportTest { return true; } }; - sensorDataAggregator.add(aggregatorCyclingDistanceSpeed); + sensorManager.setAggregator(aggregatorCyclingDistanceSpeed); } else { - sensorDataAggregator.add(new AggregatorCyclingDistanceSpeed("", "")); + sensorManager.setAggregator(new AggregatorCyclingDistanceSpeed("", "")); } mockAltitudeChange(trackPointCreator, altitudeGain); - sensorDataAggregator.add(new AggregatorTemperature("", "'") { + sensorManager.setAggregator(new AggregatorTemperature("", "'") { @NonNull @Override public Temperature getAggregatedValue(Instant now) { @@ -722,11 +724,11 @@ public class ExportImportTest { trackPointCreator.onChange(); } - private void mockAltitudeChange(TrackPointCreator trackPointCreator, Float altitudeGain) { - SensorDataAggregator sensorDataAggregator = trackPointCreator.getSensorManager().sensorDataAggregator; + private static void mockAltitudeChange(TrackPointCreator trackPointCreator, Float altitudeGain) { + SensorManager sensorManager = trackPointCreator.getSensorManager(); if (altitudeGain == null) { - sensorDataAggregator.add(new AggregatorBarometer("test", null)); + sensorManager.getAltitudeChangeHandler().setAggregator(new AggregatorBarometer("test", null)); return; } @@ -742,7 +744,7 @@ public class ExportImportTest { return true; } }; - sensorDataAggregator.add(aggregatorBarometer); + sensorManager.getAltitudeChangeHandler().setAggregator(aggregatorBarometer); } private void sendLocation(TrackPointCreator trackPointCreator, String time, double latitude, double longitude, float accuracy, float verticalAccuracy, float speed, double altitude, Float altitudeGain) { @@ -757,6 +759,6 @@ public class ExportImportTest { mockAltitudeChange(trackPointCreator, altitudeGain); trackPointCreator.setClock(time); - trackPointCreator.getSensorManager().getGpsManager().onLocationChanged(location); + trackPointCreator.getSensorManager().getGpsHandler().onDataReceived(location); } } \ 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/driver/BluetoothHandlerBarometricPressureTest.java similarity index 69% rename from src/androidTest/java/de/dennisguse/opentracks/sensors/BluetoothHandlerBarometricPressureTest.java rename to src/androidTest/java/de/dennisguse/opentracks/sensors/driver/BluetoothHandlerBarometricPressureTest.java index 1a4ae59c7..e75ef520b 100644 --- a/src/androidTest/java/de/dennisguse/opentracks/sensors/BluetoothHandlerBarometricPressureTest.java +++ b/src/androidTest/java/de/dennisguse/opentracks/sensors/driver/BluetoothHandlerBarometricPressureTest.java @@ -1,4 +1,4 @@ -package de.dennisguse.opentracks.sensors; +package de.dennisguse.opentracks.sensors.driver; import static org.junit.Assert.assertEquals; @@ -13,11 +13,11 @@ public class BluetoothHandlerBarometricPressureTest { @Test public void parseEnvironmentalSensing_Pa() { // given - BluetoothGattCharacteristic characteristic = new BluetoothGattCharacteristic(BluetoothHandlerBarometricPressure.BAROMETRIC_PRESSURE.serviceUUID(), 0, 0); + BluetoothGattCharacteristic characteristic = new BluetoothGattCharacteristic(BarometerBluetooth.BAROMETRIC_PRESSURE.serviceUUID(), 0, 0); characteristic.setValue(new byte[]{(byte) 0xB2, (byte) 0x48, (byte) 0x0F, (byte) 0x00}); // when - AtmosphericPressure pressure = BluetoothHandlerBarometricPressure.parseEnvironmentalSensing(characteristic); + AtmosphericPressure pressure = BarometerBluetooth.parseEnvironmentalSensing(characteristic); // then assertEquals(AtmosphericPressure.ofHPA(1001.65f), pressure); diff --git a/src/androidTest/java/de/dennisguse/opentracks/sensors/BluetoothHandlerHeartRateTest.java b/src/androidTest/java/de/dennisguse/opentracks/sensors/driver/BluetoothHandlerHeartRateTest.java similarity index 67% rename from src/androidTest/java/de/dennisguse/opentracks/sensors/BluetoothHandlerHeartRateTest.java rename to src/androidTest/java/de/dennisguse/opentracks/sensors/driver/BluetoothHandlerHeartRateTest.java index 6cbfaa2d8..90f9b1301 100644 --- a/src/androidTest/java/de/dennisguse/opentracks/sensors/BluetoothHandlerHeartRateTest.java +++ b/src/androidTest/java/de/dennisguse/opentracks/sensors/driver/BluetoothHandlerHeartRateTest.java @@ -1,4 +1,4 @@ -package de.dennisguse.opentracks.sensors; +package de.dennisguse.opentracks.sensors.driver; import static org.junit.Assert.assertEquals; @@ -13,11 +13,11 @@ public class BluetoothHandlerHeartRateTest { @Test public void parseHeartRate_uint8() { // given - BluetoothGattCharacteristic characteristic = new BluetoothGattCharacteristic(BluetoothHandlerHeartRate.HEARTRATE.serviceUUID(), 0, 0); + BluetoothGattCharacteristic characteristic = new BluetoothGattCharacteristic(HeartRateBluetooth.HEARTRATE.serviceUUID(), 0, 0); characteristic.setValue(new byte[]{0x02, 0x3C}); // when - HeartRate heartRate = BluetoothHandlerHeartRate.parseHeartRate(characteristic); + HeartRate heartRate = HeartRateBluetooth.parseHeartRate(characteristic); // then assertEquals(HeartRate.of(60), heartRate); @@ -26,11 +26,11 @@ public class BluetoothHandlerHeartRateTest { @Test public void parseHeartRate_uint16() { // given - BluetoothGattCharacteristic characteristic = new BluetoothGattCharacteristic(BluetoothHandlerHeartRate.HEARTRATE.serviceUUID(), 0, 0); + BluetoothGattCharacteristic characteristic = new BluetoothGattCharacteristic(HeartRateBluetooth.HEARTRATE.serviceUUID(), 0, 0); characteristic.setValue(new byte[]{0x01, 0x01, 0x01}); // when - HeartRate heartRate = BluetoothHandlerHeartRate.parseHeartRate(characteristic); + HeartRate heartRate = HeartRateBluetooth.parseHeartRate(characteristic); // then assertEquals(HeartRate.of(257), heartRate); diff --git a/src/androidTest/java/de/dennisguse/opentracks/sensors/BluetoothHandlerCyclingDistanceSpeedTest.java b/src/androidTest/java/de/dennisguse/opentracks/sensors/driver/CyclingDistanceSpeedBluetoothTest.java similarity index 57% rename from src/androidTest/java/de/dennisguse/opentracks/sensors/BluetoothHandlerCyclingDistanceSpeedTest.java rename to src/androidTest/java/de/dennisguse/opentracks/sensors/driver/CyclingDistanceSpeedBluetoothTest.java index 58bbbfacd..989757209 100644 --- a/src/androidTest/java/de/dennisguse/opentracks/sensors/BluetoothHandlerCyclingDistanceSpeedTest.java +++ b/src/androidTest/java/de/dennisguse/opentracks/sensors/driver/CyclingDistanceSpeedBluetoothTest.java @@ -1,4 +1,4 @@ -package de.dennisguse.opentracks.sensors; +package de.dennisguse.opentracks.sensors.driver; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertNull; @@ -8,14 +8,14 @@ import android.util.Pair; import org.junit.Test; -public class BluetoothHandlerCyclingDistanceSpeedTest { +public class CyclingDistanceSpeedBluetoothTest { @Test public void parseCyclingSpeedCadence_crankOnly() { - BluetoothGattCharacteristic characteristic = new BluetoothGattCharacteristic(BluetoothHandlerCyclingDistanceSpeed.CYCLING_SPEED_CADENCE.serviceUUID(), 0, 0); + BluetoothGattCharacteristic characteristic = new BluetoothGattCharacteristic(CyclingDistanceSpeedBluetooth.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); + Pair sensor = CyclingDistanceSpeedBluetooth.parseCyclingCrankAndWheel(characteristic); // then assertNull(sensor.first); @@ -24,11 +24,11 @@ public class BluetoothHandlerCyclingDistanceSpeedTest { @Test public void parseCyclingSpeedCadence_wheelOnly() { - BluetoothGattCharacteristic characteristic = new BluetoothGattCharacteristic(BluetoothHandlerCyclingDistanceSpeed.CYCLING_SPEED_CADENCE.serviceUUID(), 0, 0); + BluetoothGattCharacteristic characteristic = new BluetoothGattCharacteristic(CyclingDistanceSpeedBluetooth.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); + Pair sensor = CyclingDistanceSpeedBluetooth.parseCyclingCrankAndWheel(characteristic); // then assertEquals(65535 + 16777216, sensor.first.wheelRevolutionsCount()); @@ -37,11 +37,11 @@ public class BluetoothHandlerCyclingDistanceSpeedTest { @Test public void parseCyclingSpeedCadence_crankWheel() { - BluetoothGattCharacteristic characteristic = new BluetoothGattCharacteristic(BluetoothHandlerCyclingDistanceSpeed.CYCLING_SPEED_CADENCE.serviceUUID(), 0, 0); + BluetoothGattCharacteristic characteristic = new BluetoothGattCharacteristic(CyclingDistanceSpeedBluetooth.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); + Pair sensor = CyclingDistanceSpeedBluetooth.parseCyclingCrankAndWheel(characteristic); // then assertEquals(200 + 16777216, sensor.first.wheelRevolutionsCount()); diff --git a/src/androidTest/java/de/dennisguse/opentracks/sensors/BluetoothHandlerCyclingPowerTest.java b/src/androidTest/java/de/dennisguse/opentracks/sensors/driver/CyclingPowerBluetoothTest.java similarity index 64% rename from src/androidTest/java/de/dennisguse/opentracks/sensors/BluetoothHandlerCyclingPowerTest.java rename to src/androidTest/java/de/dennisguse/opentracks/sensors/driver/CyclingPowerBluetoothTest.java index 494a1705e..9fa28f13c 100644 --- a/src/androidTest/java/de/dennisguse/opentracks/sensors/BluetoothHandlerCyclingPowerTest.java +++ b/src/androidTest/java/de/dennisguse/opentracks/sensors/driver/CyclingPowerBluetoothTest.java @@ -1,4 +1,4 @@ -package de.dennisguse.opentracks.sensors; +package de.dennisguse.opentracks.sensors.driver; import static org.junit.Assert.assertEquals; @@ -6,15 +6,15 @@ import android.bluetooth.BluetoothGattCharacteristic; import org.junit.Test; -public class BluetoothHandlerCyclingPowerTest { +public class CyclingPowerBluetoothTest { @Test public void parseCyclingPower_power() { - BluetoothGattCharacteristic characteristic = new BluetoothGattCharacteristic(BluetoothHandlerCyclingPower.CYCLING_POWER.serviceUUID(), 0, 0); + BluetoothGattCharacteristic characteristic = new BluetoothGattCharacteristic(CyclingPowerBluetooth.CYCLING_POWER.serviceUUID(), 0, 0); characteristic.setValue(new byte[]{0, 0, 40, 0}); // when - BluetoothHandlerCyclingPower.Data powerCadence = BluetoothHandlerCyclingPower.parseCyclingPower(characteristic); + CyclingPowerBluetooth.Data powerCadence = CyclingPowerBluetooth.parseCyclingPower(characteristic); // then assertEquals(40, powerCadence.power().getW(), 0.01); @@ -22,11 +22,11 @@ public class BluetoothHandlerCyclingPowerTest { @Test public void parseCyclingPower_power_with_cadence() { - BluetoothGattCharacteristic characteristic = new BluetoothGattCharacteristic(BluetoothHandlerCyclingPower.CYCLING_POWER.serviceUUID(), 0, 0); + BluetoothGattCharacteristic characteristic = new BluetoothGattCharacteristic(CyclingPowerBluetooth.CYCLING_POWER.serviceUUID(), 0, 0); characteristic.setValue(new byte[]{0x2C, 0x00, 0x00, 0x00, (byte) 0x9F, 0x00, 0x0C, 0x00, (byte) 0xE5, 0x42}); // when - BluetoothHandlerCyclingPower.Data powerCadence = BluetoothHandlerCyclingPower.parseCyclingPower(characteristic); + CyclingPowerBluetooth.Data powerCadence = CyclingPowerBluetooth.parseCyclingPower(characteristic); // then assertEquals(0, powerCadence.power().getW(), 0.01); diff --git a/src/androidTest/java/de/dennisguse/opentracks/sensors/BluetoothHandlerRunningSpeedAndCadenceTest.java b/src/androidTest/java/de/dennisguse/opentracks/sensors/driver/RunningSpeedAndCadenceBluetoothTest.java similarity index 67% rename from src/androidTest/java/de/dennisguse/opentracks/sensors/BluetoothHandlerRunningSpeedAndCadenceTest.java rename to src/androidTest/java/de/dennisguse/opentracks/sensors/driver/RunningSpeedAndCadenceBluetoothTest.java index 7ca2507e3..9442f4b80 100644 --- a/src/androidTest/java/de/dennisguse/opentracks/sensors/BluetoothHandlerRunningSpeedAndCadenceTest.java +++ b/src/androidTest/java/de/dennisguse/opentracks/sensors/driver/RunningSpeedAndCadenceBluetoothTest.java @@ -1,4 +1,4 @@ -package de.dennisguse.opentracks.sensors; +package de.dennisguse.opentracks.sensors.driver; import static org.junit.Assert.assertEquals; @@ -10,15 +10,15 @@ import de.dennisguse.opentracks.data.models.Cadence; import de.dennisguse.opentracks.data.models.Distance; import de.dennisguse.opentracks.data.models.Speed; -public class BluetoothHandlerRunningSpeedAndCadenceTest { +public class RunningSpeedAndCadenceBluetoothTest { @Test public void parseRunningSpeedAndCadence_with_distance() { - BluetoothGattCharacteristic characteristic = new BluetoothGattCharacteristic(BluetoothHandlerRunningSpeedAndCadence.RUNNING_SPEED_CADENCE.serviceUUID(), 0, 0); + BluetoothGattCharacteristic characteristic = new BluetoothGattCharacteristic(RunningSpeedAndCadenceBluetooth.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); + RunningSpeedAndCadenceBluetooth.Data sensor = RunningSpeedAndCadenceBluetooth.parseRunningSpeedAndCadence("sensorName", characteristic); // then assertEquals(Speed.of(5), sensor.speed()); diff --git a/src/androidTest/java/de/dennisguse/opentracks/sensors/sensorData/AggregatorBarometerTest.java b/src/androidTest/java/de/dennisguse/opentracks/sensors/sensorData/AggregatorBarometerTest.java index 01d929f3d..27466970d 100644 --- a/src/androidTest/java/de/dennisguse/opentracks/sensors/sensorData/AggregatorBarometerTest.java +++ b/src/androidTest/java/de/dennisguse/opentracks/sensors/sensorData/AggregatorBarometerTest.java @@ -11,7 +11,7 @@ public class AggregatorBarometerTest { private static void addSensorValue(AggregatorBarometer aggregatorBarometer, float[] values) { for (float f : values) { - aggregatorBarometer.add(new Raw<>(Instant.MIN, AtmosphericPressure.ofHPA(f))); + aggregatorBarometer.add(Instant.MIN, AtmosphericPressure.ofHPA(f)); } } @@ -24,7 +24,7 @@ public class AggregatorBarometerTest { 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.aggregatedValue.gain_m(), 0.01); - Assert.assertEquals(15f, subject.aggregatedValue.loss_m(), 0.01); + Assert.assertEquals(0f, subject.output.gain_m(), 0.01); + Assert.assertEquals(15f, subject.output.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 b317c718a..f7f06e13e 100644 --- a/src/androidTest/java/de/dennisguse/opentracks/sensors/sensorData/SensorDataCyclingTest.java +++ b/src/androidTest/java/de/dennisguse/opentracks/sensors/sensorData/SensorDataCyclingTest.java @@ -2,6 +2,7 @@ package de.dennisguse.opentracks.sensors.sensorData; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertNull; import androidx.test.ext.junit.runners.AndroidJUnit4; @@ -12,10 +13,9 @@ import java.time.Instant; 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.BluetoothHandlerCyclingCadence; -import de.dennisguse.opentracks.sensors.BluetoothHandlerCyclingDistanceSpeed; import de.dennisguse.opentracks.sensors.UintUtils; +import de.dennisguse.opentracks.sensors.driver.CyclingCadenceBluetooth; +import de.dennisguse.opentracks.sensors.driver.CyclingDistanceSpeedBluetooth; @RunWith(AndroidJUnit4.class) public class SensorDataCyclingTest { @@ -25,8 +25,8 @@ public class SensorDataCyclingTest { AggregatorCyclingCadence current = new AggregatorCyclingCadence("", ""); // when - current.add(new Raw<>(Instant.MIN, new BluetoothHandlerCyclingCadence.CrankData(1, 1024))); - current.add(new Raw<>(Instant.MIN, new BluetoothHandlerCyclingCadence.CrankData(2, 2048))); + current.add(Instant.MIN, new CyclingCadenceBluetooth.CrankData(1, 1024)); + current.add(Instant.MIN, new CyclingCadenceBluetooth.CrankData(2, 2048)); // then assertEquals(60, current.getAggregatedValue(Instant.MIN).getRPM(), 0.01); @@ -37,8 +37,8 @@ public class SensorDataCyclingTest { AggregatorCyclingCadence current = new AggregatorCyclingCadence("", ""); // when - current.add(new Raw<>(Instant.MIN, new BluetoothHandlerCyclingCadence.CrankData(1, 6184))); - current.add(new Raw<>(Instant.MIN, new BluetoothHandlerCyclingCadence.CrankData(2, 8016))); + current.add(Instant.MIN, new CyclingCadenceBluetooth.CrankData(1, 6184)); + current.add(Instant.MIN, new CyclingCadenceBluetooth.CrankData(2, 8016)); // then assertEquals(33.53, current.getAggregatedValue(Instant.MIN).getRPM(), 0.01); @@ -49,8 +49,8 @@ public class SensorDataCyclingTest { AggregatorCyclingCadence current = new AggregatorCyclingCadence("", ""); // when - current.add(new Raw<>(Instant.MIN, new BluetoothHandlerCyclingCadence.CrankData(1, 1024))); - current.add(new Raw<>(Instant.MIN, new BluetoothHandlerCyclingCadence.CrankData(1, 2048))); + current.add(Instant.MIN, new CyclingCadenceBluetooth.CrankData(1, 1024)); + current.add(Instant.MIN, new CyclingCadenceBluetooth.CrankData(1, 2048)); // then assertEquals(Cadence.of(0), current.getAggregatedValue(Instant.MIN)); @@ -62,8 +62,8 @@ public class SensorDataCyclingTest { AggregatorCyclingCadence current = new AggregatorCyclingCadence("", ""); // when - current.add(new Raw<>(Instant.MIN, new BluetoothHandlerCyclingCadence.CrankData(1, 1024))); - current.add(new Raw<>(Instant.MIN, new BluetoothHandlerCyclingCadence.CrankData(2, 1024))); + current.add(Instant.MIN, new CyclingCadenceBluetooth.CrankData(1, 1024)); + current.add(Instant.MIN, new CyclingCadenceBluetooth.CrankData(2, 1024)); // then assertFalse(current.hasReceivedData()); //TODO Cadence should be 0? @@ -74,8 +74,8 @@ public class SensorDataCyclingTest { AggregatorCyclingCadence current = new AggregatorCyclingCadence("", ""); // when - current.add(new Raw<>(Instant.MIN, new BluetoothHandlerCyclingCadence.CrankData(1, UintUtils.UINT16_MAX - 1024))); - current.add(new Raw<>(Instant.MIN, new BluetoothHandlerCyclingCadence.CrankData(2, 0))); + current.add(Instant.MIN, new CyclingCadenceBluetooth.CrankData(1, UintUtils.UINT16_MAX - 1024)); + current.add(Instant.MIN, new CyclingCadenceBluetooth.CrankData(2, 0)); // then assertEquals(60, current.getAggregatedValue(Instant.MIN).getRPM(), 0.01); @@ -87,13 +87,13 @@ public class SensorDataCyclingTest { AggregatorCyclingCadence current = new AggregatorCyclingCadence("", ""); // when - current.add(new Raw<>(Instant.MIN, new BluetoothHandlerCyclingCadence.CrankData(UintUtils.UINT32_MAX - 1, 1024))); - current.add(new Raw<>(Instant.MIN, new BluetoothHandlerCyclingCadence.CrankData(0, 2048))); + current.add(Instant.MIN, new CyclingCadenceBluetooth.CrankData(UintUtils.UINT32_MAX - 1, 1024)); + current.add(Instant.MIN, new CyclingCadenceBluetooth.CrankData(0, 2048)); // then // TODO See #953 // assertEquals(60, current.getValue().getRPM(), 0.01); - assertEquals(Cadence.of(0), current.getAggregatedValue(Instant.MIN)); + assertNull(current.getAggregatedValue(Instant.MIN)); } @Test @@ -102,8 +102,8 @@ public class SensorDataCyclingTest { current.setWheelCircumference(Distance.ofMM(2150)); // when - current.add(new Raw<>(Instant.MIN, new BluetoothHandlerCyclingDistanceSpeed.WheelData(1, 6184))); - current.add(new Raw<>(Instant.MIN, new BluetoothHandlerCyclingDistanceSpeed.WheelData(2, 8016))); + current.add(Instant.MIN, new CyclingDistanceSpeedBluetooth.WheelData(1, 6184)); + current.add(Instant.MIN, new CyclingDistanceSpeedBluetooth.WheelData(2, 8016)); // then assertEquals(2.15, current.getAggregatedValue(Instant.MIN).distance().toM(), 0.01); @@ -117,14 +117,13 @@ public class SensorDataCyclingTest { current.setWheelCircumference(Distance.ofMM(2000)); // when - current.add(new Raw<>(Instant.MIN, new BluetoothHandlerCyclingDistanceSpeed.WheelData(UintUtils.UINT32_MAX - 1, 1024))); - current.add(new Raw<>(Instant.MIN, new BluetoothHandlerCyclingDistanceSpeed.WheelData(0, 2048))); - + current.add(Instant.MIN, new CyclingDistanceSpeedBluetooth.WheelData(UintUtils.UINT32_MAX - 1, 1024)); + current.add(Instant.MIN, new CyclingDistanceSpeedBluetooth.WheelData(0, 2048)); // then // TODO See #953 // assertEquals(2, current.getValue().getDistance().toM(), 0.01); // assertEquals(2, current.getValue().getSpeed().toMPS(), 0.01); - assertEquals(new AggregatorCyclingDistanceSpeed.Data(Distance.ZERO, Distance.ZERO, Speed.ZERO), current.getAggregatedValue(Instant.MIN)); + assertNull(current.getAggregatedValue(Instant.MIN)); } } \ 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 74dec63dc..27c54e79c 100644 --- a/src/androidTest/java/de/dennisguse/opentracks/services/TrackRecordingServiceRecordingTest.java +++ b/src/androidTest/java/de/dennisguse/opentracks/services/TrackRecordingServiceRecordingTest.java @@ -6,7 +6,6 @@ import static org.junit.Assert.assertFalse; import android.content.Context; import android.content.Intent; import android.location.Location; -import android.location.LocationManager; import android.os.Looper; import androidx.annotation.NonNull; @@ -44,12 +43,12 @@ import de.dennisguse.opentracks.data.models.Statistics; 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.BluetoothHandlerRunningSpeedAndCadence; import de.dennisguse.opentracks.sensors.SensorManager; +import de.dennisguse.opentracks.sensors.driver.GpsInternal; +import de.dennisguse.opentracks.sensors.driver.RunningSpeedAndCadenceBluetooth; 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; @@ -154,7 +153,8 @@ public class TrackRecordingServiceRecordingTest { Track.Id trackId = service.startNewTrack(); //We do not want the real GPS to interfere. - LocationManagerCompat.removeUpdates((LocationManager) context.getSystemService(Context.LOCATION_SERVICE), trackPointCreator.getSensorManager().getGpsManager()); + GpsInternal gpsInternal = (GpsInternal)trackPointCreator.getSensorManager().getGpsHandler().driver; + LocationManagerCompat.removeUpdates(gpsInternal.locationManager, gpsInternal.locationListenerCompat); String gps1 = "2020-02-02T02:02:03Z"; sendGPSLocation(trackPointCreator, gps1, 45.0, 35.0, 1, 15); @@ -339,16 +339,21 @@ public class TrackRecordingServiceRecordingTest { mockAltitudeChange(trackPointCreator, 0); SensorManager sensorManager = trackPointCreator.getSensorManager(); - sensorManager.sensorDataAggregator.add(new AggregatorHeartRate("", "")); // when String sensor1 = "2020-02-02T02:02:03Z"; trackPointCreator.setClock(sensor1); - sensorManager.onChanged(new Raw<>(sensor1, HeartRate.of(5))); //Should be ignored + AggregatorHeartRate avgHeartRate = new AggregatorHeartRate("", ""); + sensorManager.setAggregator(avgHeartRate); + + //Should be ignored + avgHeartRate.add(trackPointCreator.getNow(), HeartRate.of(5)); + sensorManager.onChange(); String sensor3 = "2020-02-02T02:02:13Z"; trackPointCreator.setClock(sensor3); - sensorManager.onChanged(new Raw<>(sensor3, HeartRate.of(7))); + avgHeartRate.add(trackPointCreator.getNow(), HeartRate.of(7)); + sensorManager.onChange(); String stopTime = "2020-02-02T02:02:15Z"; trackPointCreator.setClock(stopTime); @@ -808,19 +813,22 @@ public class TrackRecordingServiceRecordingTest { trackPointCreator.setClock(startTime); Track.Id trackId = service.startNewTrack(); + AggregatorRunning aggregatorRunning = new AggregatorRunning("", ""); SensorManager sensorManager = trackPointCreator.getSensorManager(); - sensorManager.sensorDataAggregator.add(new AggregatorRunning("", "")); - sensorManager.sensorDataAggregator.barometer = null; + sensorManager.setAggregator(aggregatorRunning); // when String sensor1 = "2020-02-02T02:02:03Z"; trackPointCreator.setClock(sensor1); - sensorManager.onChanged(new Raw<>(sensor1, new BluetoothHandlerRunningSpeedAndCadence.Data(Speed.of(5), Cadence.of(1), Distance.ZERO))); //Should be ignored + //Should be ignored + aggregatorRunning.add(trackPointCreator.getNow(), new RunningSpeedAndCadenceBluetooth.Data(Speed.of(5), Cadence.of(1), Distance.ZERO)); + sensorManager.onChange(); // when String sensor2 = "2020-02-02T02:02:04Z"; trackPointCreator.setClock(sensor2); - sensorManager.onChanged(new Raw<>(sensor2, new BluetoothHandlerRunningSpeedAndCadence.Data(Speed.of(5), Cadence.of(2), Distance.of(2)))); + aggregatorRunning.add(trackPointCreator.getNow(), new RunningSpeedAndCadenceBluetooth.Data(Speed.of(5), Cadence.of(2), Distance.of(2))); + sensorManager.onChange(); // when String gps1 = "2020-02-02T02:02:05Z"; @@ -829,12 +837,15 @@ public class TrackRecordingServiceRecordingTest { // when String sensor3 = "2020-02-02T02:02:06Z"; trackPointCreator.setClock(sensor3); - sensorManager.onChanged(new Raw<>(sensor3, new BluetoothHandlerRunningSpeedAndCadence.Data(Speed.of(5), Cadence.of(3), Distance.of(12)))); + aggregatorRunning.add(trackPointCreator.getNow(), new RunningSpeedAndCadenceBluetooth.Data(Speed.of(5), Cadence.of(3), Distance.of(12))); + sensorManager.onChange(); // when String sensor4 = "2020-02-02T02:02:07Z"; trackPointCreator.setClock(sensor4); - sensorManager.onChanged(new Raw<>(sensor4, new BluetoothHandlerRunningSpeedAndCadence.Data(Speed.of(5), Cadence.of(4), Distance.of(14)))); //Should be ignored + //Should be ignored + aggregatorRunning.add(trackPointCreator.getNow(), new RunningSpeedAndCadenceBluetooth.Data(Speed.of(5), Cadence.of(4), Distance.of(14))); + sensorManager.onChange(); // when String gps2 = "2020-02-02T02:02:08Z"; @@ -843,7 +854,9 @@ public class TrackRecordingServiceRecordingTest { // when String sensor5 = "2020-02-02T02:02:10Z"; trackPointCreator.setClock(sensor5); - sensorManager.onChanged(new Raw<>(sensor5, new BluetoothHandlerRunningSpeedAndCadence.Data(Speed.of(5), Cadence.of(5), Distance.of(16)))); //Should be ignored + //Should be ignored + aggregatorRunning.add(trackPointCreator.getNow(), new RunningSpeedAndCadenceBluetooth.Data(Speed.of(5), Cadence.of(5), Distance.of(16))); + sensorManager.onChange(); // when String gps3 = "2020-02-02T02:02:12Z"; @@ -962,7 +975,7 @@ public class TrackRecordingServiceRecordingTest { } private void mockAltitudeChange(TrackPointCreator trackPointCreator, float altitudeGain) { - trackPointCreator.getSensorManager().sensorDataAggregator.barometer = new AggregatorBarometer("", "") { + trackPointCreator.getSensorManager().getAltitudeChangeHandler().setAggregator(new AggregatorBarometer("", "") { @Override public boolean hasReceivedData() { return true; @@ -973,7 +986,7 @@ public class TrackRecordingServiceRecordingTest { public AltitudeGainLoss getAggregatedValue(Instant now) { return new AltitudeGainLoss(altitudeGain, altitudeGain); } - }; + }); } private static void sendGPSLocation(TrackPointCreator trackPointCreator, String time, double latitude, double longitude, float accuracy, long speed) { @@ -985,6 +998,6 @@ public class TrackRecordingServiceRecordingTest { location.setSpeed(speed); trackPointCreator.setClock(time); - trackPointCreator.getSensorManager().getGpsManager().onLocationChanged(location); + trackPointCreator.getSensorManager().getGpsHandler().onDataReceived(location); } } diff --git a/src/main/java/de/dennisguse/opentracks/sensors/AltitudeChangeHandler.java b/src/main/java/de/dennisguse/opentracks/sensors/AltitudeChangeHandler.java new file mode 100644 index 000000000..1eee74bfc --- /dev/null +++ b/src/main/java/de/dennisguse/opentracks/sensors/AltitudeChangeHandler.java @@ -0,0 +1,50 @@ +package de.dennisguse.opentracks.sensors; + +import androidx.annotation.NonNull; + +import de.dennisguse.opentracks.R; +import de.dennisguse.opentracks.data.models.AltitudeGainLoss; +import de.dennisguse.opentracks.data.models.AtmosphericPressure; +import de.dennisguse.opentracks.sensors.driver.BarometerBluetooth; +import de.dennisguse.opentracks.sensors.driver.BarometerInternal; +import de.dennisguse.opentracks.sensors.driver.BluetoothDriver; +import de.dennisguse.opentracks.sensors.driver.Driver; +import de.dennisguse.opentracks.sensors.sensorData.AggregatorBarometer; + +/** + * Estimates the altitude gain and altitude loss using the device's pressure sensor (i.e., barometer). + */ +//TODO Rename class +public class AltitudeChangeHandler extends SensorHandler { + + public AltitudeChangeHandler(SensorManager sensorManager) { + super(sensorManager); + } + + @NonNull + @Override + protected Driver createDriverInternal() { + return new BarometerInternal(this); + } + + @NonNull + @Override + protected Driver createDriverBluetooth(String address) { + return new BluetoothDriver<>( + BluetoothUtils.getAdapter(getContext()), + this, + new BarometerBluetooth() + ); + } + + @NonNull + @Override + public AggregatorBarometer createAggregator(String sensorAddress, String sensorName) { + return new AggregatorBarometer(sensorAddress, sensorName); + } + + @Override + protected int getSensorPreferenceKey() { + return R.string.settings_sensor_bluetooth_pressure_key; + } +} diff --git a/src/main/java/de/dennisguse/opentracks/sensors/BluetoothHandlerCyclingCadence.java b/src/main/java/de/dennisguse/opentracks/sensors/BluetoothHandlerCyclingCadence.java deleted file mode 100644 index 64ba0e0b5..000000000 --- a/src/main/java/de/dennisguse/opentracks/sensors/BluetoothHandlerCyclingCadence.java +++ /dev/null @@ -1,57 +0,0 @@ -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( - BluetoothHandlerCyclingPower.CYCLING_POWER, - BluetoothHandlerCyclingDistanceSpeed.CYCLING_SPEED_CADENCE - ); - - @Override - public List getServices() { - return CYCLING_CADENCE; - } - - @Override - public AggregatorCyclingCadence createEmptySensorData(String address, String name) { - return new AggregatorCyclingCadence(address, name); - } - - @Override - public void handlePayload(SensorManager.SensorDataChangedObserver observer, ServiceMeasurementUUID serviceMeasurementUUID, String sensorName, String address, BluetoothGattCharacteristic characteristic) { - if (serviceMeasurementUUID.equals(BluetoothHandlerCyclingPower.CYCLING_POWER)) { - BluetoothHandlerCyclingPower.Data data = BluetoothHandlerCyclingPower.parseCyclingPower(characteristic); - if (data != null && data.crank() != null) { - observer.onChange(new Raw<>(observer.getNow(), 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<>(observer.getNow(), 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/BluetoothHandlerTemperature.java b/src/main/java/de/dennisguse/opentracks/sensors/BluetoothHandlerTemperature.java deleted file mode 100644 index 18b1ac1fa..000000000 --- a/src/main/java/de/dennisguse/opentracks/sensors/BluetoothHandlerTemperature.java +++ /dev/null @@ -1,53 +0,0 @@ -package de.dennisguse.opentracks.sensors; - -import static de.dennisguse.opentracks.sensors.BluetoothHandlerBarometricPressure.ENVIRONMENTAL_SENSING_SERVICE; - -import android.bluetooth.BluetoothGattCharacteristic; - -import java.util.List; -import java.util.UUID; - -import de.dennisguse.opentracks.data.models.Temperature; -import de.dennisguse.opentracks.sensors.sensorData.Aggregator; -import de.dennisguse.opentracks.sensors.sensorData.AggregatorTemperature; -import de.dennisguse.opentracks.sensors.sensorData.Raw; -import de.dennisguse.opentracks.sensors.sensorData.SensorHandlerInterface; - -public class BluetoothHandlerTemperature implements SensorHandlerInterface { - public static final ServiceMeasurementUUID TEMPERATURE = new ServiceMeasurementUUID( - ENVIRONMENTAL_SENSING_SERVICE, - new UUID(0x2A6E00001000L, 0x800000805f9b34fbL) - ); - - @Override - public List getServices() { - return List.of(TEMPERATURE); - } - - @Override - public Aggregator createEmptySensorData(String address, String name) { - return new AggregatorTemperature(address, name); - } - - @Override - public void handlePayload(SensorManager.SensorDataChangedObserver observer, ServiceMeasurementUUID serviceMeasurementUUID, String sensorName, String address, BluetoothGattCharacteristic characteristic) { - Temperature value = parseEnvironmentalSensing(characteristic); - if (value == null) return; - - observer.onChange(new Raw<>(observer.getNow(), value)); - } - - /** - * Decoding: org.bluetooth.characteristic.temperature.xml - */ - public static Temperature parseEnvironmentalSensing(BluetoothGattCharacteristic characteristic) { - byte[] raw = characteristic.getValue(); - - if (raw.length < 2) { - return null; - } - - Integer pressure = characteristic.getIntValue(BluetoothGattCharacteristic.FORMAT_SINT16, 0); - return Temperature.ofCentiCelsius(pressure); - } -} diff --git a/src/main/java/de/dennisguse/opentracks/sensors/BluetoothRemoteSensorManager.java b/src/main/java/de/dennisguse/opentracks/sensors/BluetoothRemoteSensorManager.java deleted file mode 100644 index 66bccbdce..000000000 --- a/src/main/java/de/dennisguse/opentracks/sensors/BluetoothRemoteSensorManager.java +++ /dev/null @@ -1,126 +0,0 @@ -/* - * Copyright 2010 Google Inc. - * - * Licensed under the Apache License, Version 2.0 (the "License"); you may not - * use this file except in compliance with the License. You may obtain a copy of - * the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations under - * the License. - */ - -package de.dennisguse.opentracks.sensors; - -import android.bluetooth.BluetoothAdapter; -import android.content.Context; -import android.content.SharedPreferences; -import android.os.Handler; - -import androidx.annotation.NonNull; -import androidx.annotation.Nullable; - -import de.dennisguse.opentracks.R; -import de.dennisguse.opentracks.settings.PreferencesUtils; - -/** - * Bluetooth LE sensor manager: manages connections to Bluetooth LE sensors. - *

- * Note: should only be instantiated once. - * TODO: listen for Bluetooth enabled/disabled events. - * @author Sandor Dornbush - */ -public class BluetoothRemoteSensorManager implements SensorConnector, SharedPreferences.OnSharedPreferenceChangeListener { - - private static final String TAG = BluetoothRemoteSensorManager.class.getSimpleName(); - - private final Context context; - private final Handler handler; - private boolean started = false; - - private final BluetoothConnectionManager heartRate; - private final BluetoothConnectionManager temperature; - 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; - - BluetoothAdapter bluetoothAdapter = BluetoothUtils.getAdapter(context); - this.heartRate = new BluetoothConnectionManager(bluetoothAdapter, observer, new BluetoothHandlerHeartRate()); - this.temperature = new BluetoothConnectionManager(bluetoothAdapter, observer, new BluetoothHandlerTemperature()); - this.cyclingCadence = new BluetoothConnectionManager(bluetoothAdapter, observer, new BluetoothHandlerCyclingCadence()); - this.cyclingSpeed = new BluetoothConnectionManager(bluetoothAdapter, observer, new BluetoothHandlerCyclingDistanceSpeed()); - this.cyclingPower = new BluetoothConnectionManager(bluetoothAdapter, observer, new BluetoothHandlerCyclingPower()); - this.runningSpeedAndCadence = new BluetoothConnectionManager(bluetoothAdapter, observer, new BluetoothHandlerRunningSpeedAndCadence()); - } - - @Override - public void start(Context context, Handler handler) { - started = true; - - // Triggers connection startup - onSharedPreferenceChanged(null, null); - } - - @Override - public synchronized void stop(Context context) { - heartRate.disconnect(); - temperature.disconnect(); - cyclingCadence.disconnect(); - cyclingSpeed.disconnect(); - cyclingPower.disconnect(); - runningSpeedAndCadence.disconnect(); - - started = false; - } - - private synchronized void connect(BluetoothConnectionManager connectionManager, String address) { - connectionManager.connect(context, handler, address); - } - - @Override - public void onSharedPreferenceChanged(SharedPreferences unused, @Nullable String key) { - if (!started) return; - - if (PreferencesUtils.isKey(R.string.settings_sensor_bluetooth_heart_rate_key, key)) { - String address = PreferencesUtils.getBluetoothHeartRateSensorAddress(); - connect(heartRate, address); - } - - if (PreferencesUtils.isKey(R.string.settings_sensor_bluetooth_temperature_key, key)) { - String address = PreferencesUtils.getBluetoothTemperatureSensorAddress(); - connect(temperature, address); - } - - if (PreferencesUtils.isKey(R.string.settings_sensor_bluetooth_cycling_cadence_key, key)) { - String address = PreferencesUtils.getBluetoothCyclingCadenceSensorAddress(); - connect(cyclingCadence, address); - } - - if (PreferencesUtils.isKey(R.string.settings_sensor_bluetooth_cycling_speed_key, key)) { - String address = PreferencesUtils.getBluetoothCyclingSpeedSensorAddress(); - - connect(cyclingSpeed, address); - } - - if (PreferencesUtils.isKey(R.string.settings_sensor_bluetooth_cycling_power_key, key)) { - String address = PreferencesUtils.getBluetoothCyclingPowerSensorAddress(); - - connect(cyclingPower, address); - } - - if (PreferencesUtils.isKey(R.string.settings_sensor_bluetooth_running_speed_and_cadence_key, key)) { - String address = PreferencesUtils.getBluetoothRunningSpeedAndCadenceAddress(); - - connect(runningSpeedAndCadence, address); - } - } -} diff --git a/src/main/java/de/dennisguse/opentracks/sensors/CyclingCadenceHandler.java b/src/main/java/de/dennisguse/opentracks/sensors/CyclingCadenceHandler.java new file mode 100644 index 000000000..f3a072d53 --- /dev/null +++ b/src/main/java/de/dennisguse/opentracks/sensors/CyclingCadenceHandler.java @@ -0,0 +1,38 @@ +package de.dennisguse.opentracks.sensors; + +import androidx.annotation.NonNull; + +import de.dennisguse.opentracks.R; +import de.dennisguse.opentracks.data.models.Cadence; +import de.dennisguse.opentracks.sensors.driver.BluetoothDriver; +import de.dennisguse.opentracks.sensors.driver.CyclingCadenceBluetooth; +import de.dennisguse.opentracks.sensors.driver.Driver; +import de.dennisguse.opentracks.sensors.sensorData.AggregatorCyclingCadence; + +public class CyclingCadenceHandler extends SensorHandler { + + protected CyclingCadenceHandler(SensorManager sensorManager) { + super(sensorManager); + } + + @NonNull + @Override + protected Driver createDriverBluetooth(String address) { + return new BluetoothDriver<>( + BluetoothUtils.getAdapter(getContext()), + this, + new CyclingCadenceBluetooth() + ); + } + + @NonNull + @Override + protected AggregatorCyclingCadence createAggregator(String sensorAddress, String sensorName) { + return new AggregatorCyclingCadence(sensorAddress, sensorName); + } + + @Override + protected int getSensorPreferenceKey() { + return R.string.settings_sensor_bluetooth_cycling_cadence_key; + } +} diff --git a/src/main/java/de/dennisguse/opentracks/sensors/CyclingDistanceSpeedHandler.java b/src/main/java/de/dennisguse/opentracks/sensors/CyclingDistanceSpeedHandler.java new file mode 100644 index 000000000..79dbd6d9a --- /dev/null +++ b/src/main/java/de/dennisguse/opentracks/sensors/CyclingDistanceSpeedHandler.java @@ -0,0 +1,46 @@ +package de.dennisguse.opentracks.sensors; + +import androidx.annotation.NonNull; + +import de.dennisguse.opentracks.R; +import de.dennisguse.opentracks.sensors.driver.BluetoothDriver; +import de.dennisguse.opentracks.sensors.driver.CyclingDistanceSpeedBluetooth; +import de.dennisguse.opentracks.sensors.driver.Driver; +import de.dennisguse.opentracks.sensors.sensorData.AggregatorCyclingDistanceSpeed; +import de.dennisguse.opentracks.settings.PreferencesUtils; + +public class CyclingDistanceSpeedHandler extends SensorHandler { + + protected CyclingDistanceSpeedHandler(SensorManager sensorManager) { + super(sensorManager); + } + + @NonNull + @Override + protected Driver createDriverBluetooth(String address) { + return new BluetoothDriver<>( + BluetoothUtils.getAdapter(getContext()), + this, + new CyclingDistanceSpeedBluetooth() + ); + } + + @NonNull + @Override + protected AggregatorCyclingDistanceSpeed createAggregator(String sensorAddress, String sensorName) { + return new AggregatorCyclingDistanceSpeed(sensorAddress, sensorName); + } + + @Override + protected int getSensorPreferenceKey() { + return R.string.settings_sensor_bluetooth_cycling_speed_key; + } + + @Override + public void onDataReceived(CyclingDistanceSpeedBluetooth.WheelData value) { + //TODO Fetch once and then listen for changes. + ((AggregatorCyclingDistanceSpeed) aggregator).setWheelCircumference(PreferencesUtils.getWheelCircumference()); + + super.onDataReceived(value); + } +} diff --git a/src/main/java/de/dennisguse/opentracks/sensors/CyclingPowerHandler.java b/src/main/java/de/dennisguse/opentracks/sensors/CyclingPowerHandler.java new file mode 100644 index 000000000..6cebe5b9a --- /dev/null +++ b/src/main/java/de/dennisguse/opentracks/sensors/CyclingPowerHandler.java @@ -0,0 +1,38 @@ +package de.dennisguse.opentracks.sensors; + +import androidx.annotation.NonNull; + +import de.dennisguse.opentracks.R; +import de.dennisguse.opentracks.data.models.Power; +import de.dennisguse.opentracks.sensors.driver.BluetoothDriver; +import de.dennisguse.opentracks.sensors.driver.CyclingPowerBluetooth; +import de.dennisguse.opentracks.sensors.driver.Driver; +import de.dennisguse.opentracks.sensors.sensorData.AggregatorCyclingPower; + +public class CyclingPowerHandler extends SensorHandler { + + protected CyclingPowerHandler(SensorManager sensorManager) { + super(sensorManager); + } + + @NonNull + @Override + protected Driver createDriverBluetooth(String address) { + return new BluetoothDriver<>( + BluetoothUtils.getAdapter(getContext()), + this, + new CyclingPowerBluetooth() + ); + } + + @NonNull + @Override + protected AggregatorCyclingPower createAggregator(String sensorAddress, String sensorName) { + return new AggregatorCyclingPower(sensorAddress, sensorName); + } + + @Override + protected int getSensorPreferenceKey() { + return R.string.settings_sensor_bluetooth_cycling_power_key; + } +} diff --git a/src/main/java/de/dennisguse/opentracks/sensors/GainManager.java b/src/main/java/de/dennisguse/opentracks/sensors/GainManager.java deleted file mode 100644 index 0d63227bf..000000000 --- a/src/main/java/de/dennisguse/opentracks/sensors/GainManager.java +++ /dev/null @@ -1,77 +0,0 @@ -package de.dennisguse.opentracks.sensors; - -import android.content.Context; -import android.content.SharedPreferences; -import android.os.Handler; -import android.util.Log; - -import de.dennisguse.opentracks.sensors.driver.BarometerInternal; -import de.dennisguse.opentracks.sensors.driver.Driver; -import de.dennisguse.opentracks.sensors.sensorData.AggregatorBarometer; -import de.dennisguse.opentracks.settings.PreferencesUtils; - -/** - * 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 SensorManager.SensorDataChangedObserver listener; - - private final SharedPreferences.OnSharedPreferenceChangeListener sharedPreferenceChangeListener = (sharedPreferences, key) -> connect(); - - private Context context; - private Handler handler; - private Driver driver; - - public GainManager(SensorManager.SensorDataChangedObserver listener) { - this.listener = listener; - } - - public void start(Context context, Handler handler) { - this.context = context; - this.handler = handler; - PreferencesUtils.registerOnSharedPreferenceChangeListener(sharedPreferenceChangeListener); - } - - public void stop(Context context) { - Log.d(TAG, "Stop"); - this.context = null; - this.handler = null; - PreferencesUtils.unregisterOnSharedPreferenceChangeListener(sharedPreferenceChangeListener); - - onDisconnect(); - } - - private void connect() { - onDisconnect(); - - String address = PreferencesUtils.getBarometerSensorAddress(); - switch (PreferencesUtils.getSensorType(address)) { - case NONE -> { - driver = null; - listener.onRemove(new AggregatorBarometer(null, null)); - return; - } - case INTERNAL -> driver = new BarometerInternal(listener); - case REMOTE -> driver = - new BluetoothConnectionManager( - BluetoothUtils.getAdapter(context), - listener, - new BluetoothHandlerBarometricPressure() - ); - default -> throw new RuntimeException("Not implemented"); - } - - driver.connect(context, handler, address); - } - - private void onDisconnect() { - if (driver == null) return; - - driver.disconnect(); - listener.onDisconnect(new AggregatorBarometer("GainManager", null)); - } -} diff --git a/src/main/java/de/dennisguse/opentracks/sensors/GpsHandler.java b/src/main/java/de/dennisguse/opentracks/sensors/GpsHandler.java new file mode 100644 index 000000000..ff2527720 --- /dev/null +++ b/src/main/java/de/dennisguse/opentracks/sensors/GpsHandler.java @@ -0,0 +1,120 @@ +package de.dennisguse.opentracks.sensors; + +import android.content.SharedPreferences; +import android.location.Location; +import android.util.Log; + +import androidx.annotation.NonNull; +import androidx.annotation.VisibleForTesting; + +import de.dennisguse.opentracks.R; +import de.dennisguse.opentracks.data.models.Distance; +import de.dennisguse.opentracks.data.models.Position; +import de.dennisguse.opentracks.sensors.driver.Driver; +import de.dennisguse.opentracks.sensors.driver.GpsInternal; +import de.dennisguse.opentracks.sensors.driver.SensorType; +import de.dennisguse.opentracks.sensors.sensorData.AggregatorGPS; +import de.dennisguse.opentracks.services.handlers.TrackPointCreator; +import de.dennisguse.opentracks.settings.PreferencesUtils; + +@VisibleForTesting(otherwise = VisibleForTesting.PACKAGE_PRIVATE) +public class GpsHandler extends SensorHandler { + + private final String TAG = GpsHandler.class.getSimpleName(); + + //TODO Refactor to just pass information via SensorManager. + private TrackPointCreator trackPointCreator; + + private GpsStatusManager gpsStatusManager; + private Distance thresholdHorizontalAccuracy; + + private final SharedPreferences.OnSharedPreferenceChangeListener sharedPreferenceChangeListener = (sharedPreferences, key) -> { + if (PreferencesUtils.isKey(R.string.recording_distance_interval_key, key)) { + if (gpsStatusManager != null) { + Distance gpsMinDistance = PreferencesUtils.getRecordingDistanceInterval(); + gpsStatusManager.onRecordingDistanceChanged(gpsMinDistance); + } + } + + if (PreferencesUtils.isKey(R.string.recording_gps_accuracy_key, key)) { + thresholdHorizontalAccuracy = PreferencesUtils.getThresholdHorizontalAccuracy(); + } + + if (PreferencesUtils.isKey(R.string.min_sampling_interval_key, key)) { + if (gpsStatusManager != null) { + gpsStatusManager.onMinSamplingIntervalChanged(PreferencesUtils.getMinSamplingInterval()); + } + + //TODO + connect(); + } + }; + + public GpsHandler(SensorManager sensorManager, TrackPointCreator trackPointCreator) { + super(sensorManager); + this.trackPointCreator = trackPointCreator; + gpsStatusManager = new GpsStatusManager(sensorManager.getContext(), trackPointCreator::sendGpsStatus, sensorManager.getHandler()); + + sharedPreferenceChangeListener.onSharedPreferenceChanged(null, null); + } + + @Override + public void onConnected(String sensorAddress, String sensorName) { + super.onConnected(sensorAddress, sensorName); + gpsStatusManager.onGpsEnabled(); + gpsStatusManager.start(); + } + + @Override + public void onDisconnected() { + super.onDisconnected(); + gpsStatusManager.stop(); + } + + @Override + public void onSensorDeactivated() { + super.onSensorDeactivated(); + gpsStatusManager.onGpsDisabled(); + } + + @Override + public void onDataReceived(Location location) { + // Send each update to the status; please note that this TrackPoint is not stored. + Position position = Position.of(location, trackPointCreator.getNow()); + gpsStatusManager.onNewTrackPoint(position); + + //TODO We could move the following check into the AggregatorGPS + if (!position.hasValidLocation()) { + Log.w(TAG, "Ignore newTrackPoint. Location is invalid."); + return; + } + if (!position.fulfillsAccuracy(thresholdHorizontalAccuracy)) { + Log.d(TAG, "Ignore newTrackPoint. Poor accuracy."); + return; + } + + super.onDataReceived(location); + } + + @NonNull + @Override + protected Driver createDriverInternal() { + return new GpsInternal(this); + } + + @NonNull + @Override + protected AggregatorGPS createAggregator(String sensorAddress, String sensorName) { + return new AggregatorGPS(sensorAddress, sensorName); + } + + @Override + protected int getSensorPreferenceKey() { + return R.string.settings_sensor_bluetooth_gps_key; + } + + @Override + protected String getSensorPreferenceDefaultValue() { + return SensorType.INTERNAL.getPreferenceValue(); + } +} diff --git a/src/main/java/de/dennisguse/opentracks/sensors/GpsManager.java b/src/main/java/de/dennisguse/opentracks/sensors/GpsManager.java deleted file mode 100644 index e1c5ef717..000000000 --- a/src/main/java/de/dennisguse/opentracks/sensors/GpsManager.java +++ /dev/null @@ -1,181 +0,0 @@ -package de.dennisguse.opentracks.sensors; - -import android.content.Context; -import android.content.SharedPreferences; -import android.location.Location; -import android.location.LocationManager; -import android.os.Bundle; -import android.os.Handler; -import android.util.Log; - -import androidx.annotation.NonNull; -import androidx.annotation.VisibleForTesting; -import androidx.core.location.LocationListenerCompat; -import androidx.core.location.LocationManagerCompat; -import androidx.core.location.LocationRequestCompat; - -import java.time.Duration; - -import de.dennisguse.opentracks.R; -import de.dennisguse.opentracks.data.models.Distance; -import de.dennisguse.opentracks.data.models.Position; -import de.dennisguse.opentracks.sensors.sensorData.AggregatorGPS; -import de.dennisguse.opentracks.sensors.sensorData.Raw; -import de.dennisguse.opentracks.services.handlers.TrackPointCreator; -import de.dennisguse.opentracks.settings.PreferencesUtils; -import de.dennisguse.opentracks.util.PermissionRequester; - -@VisibleForTesting(otherwise = VisibleForTesting.PACKAGE_PRIVATE) -public class GpsManager implements SensorConnector, LocationListenerCompat, GpsStatusManager.GpsStatusListener, SharedPreferences.OnSharedPreferenceChangeListener { - - private final String TAG = GpsManager.class.getSimpleName(); - - private static final String LOCATION_PROVIDER = LocationManager.GPS_PROVIDER; - - private TrackPointCreator trackPointCreator; - - private SensorManager.SensorDataChangedObserver observer; - private Context context; - private Handler handler; - - private LocationManager locationManager; - private GpsStatusManager gpsStatusManager; - private Duration gpsInterval; - private Distance thresholdHorizontalAccuracy; - - public GpsManager(TrackPointCreator trackPointCreator, SensorManager.SensorDataChangedObserver observer) { - this.trackPointCreator = trackPointCreator; - this.observer = observer; - } - - public void start(@NonNull Context context, @NonNull Handler handler) { - this.context = context; - this.handler = handler; - - onSharedPreferenceChanged(null, null); - - gpsStatusManager = new GpsStatusManager(context, this, handler); - locationManager = (LocationManager) context.getSystemService(Context.LOCATION_SERVICE); - - observer.onConnect(new AggregatorGPS("internal")); - - registerLocationListener(); - gpsStatusManager.start(); - } - - private boolean isStarted() { - return locationManager != null; - } - - @SuppressWarnings({"MissingPermission"}) - public void stop(Context context) { - if (isStarted()) { - LocationManagerCompat.removeUpdates(locationManager, this); - } - locationManager = null; - this.context = null; - handler = null; - - gpsStatusManager.stop(); - gpsStatusManager = null; - - observer.onDisconnect(new AggregatorGPS("internal")); - observer = null; - - trackPointCreator = null; - } - - @Override - public void onSharedPreferenceChanged(SharedPreferences sharedPreferences, String key) { - boolean registerListener = false; - - if (PreferencesUtils.isKey(R.string.min_sampling_interval_key, key)) { - registerListener = true; - - gpsInterval = PreferencesUtils.getMinSamplingInterval(); - - if (gpsStatusManager != null) { - gpsStatusManager.onMinSamplingIntervalChanged(gpsInterval); - } - } - if (PreferencesUtils.isKey(R.string.recording_gps_accuracy_key, key)) { - thresholdHorizontalAccuracy = PreferencesUtils.getThresholdHorizontalAccuracy(); - } - if (PreferencesUtils.isKey(R.string.recording_distance_interval_key, key)) { - registerListener = true; - - if (gpsStatusManager != null) { - Distance gpsMinDistance = PreferencesUtils.getRecordingDistanceInterval(); - gpsStatusManager.onRecordingDistanceChanged(gpsMinDistance); - } - } - - if (registerListener && isStarted()) { - registerLocationListener(); - } - } - - /** - * Checks if location is valid and builds a track point that will be send through TrackPointCreator. - * - * @param location {@link Location} object. - */ - @Override - public void onLocationChanged(@NonNull Location location) { - // Send each update to the status; please note that this TrackPoint is not stored. - Position position = Position.of(location, trackPointCreator.createNow()); - gpsStatusManager.onNewTrackPoint(position); - - if (!position.hasValidLocation()) { - Log.w(TAG, "Ignore newTrackPoint. Location is invalid."); - return; - } - - if (!position.fulfillsAccuracy(thresholdHorizontalAccuracy)) { - Log.d(TAG, "Ignore newTrackPoint. Poor accuracy."); - return; - } - - observer.onChange(new Raw<>(observer.getNow(), position)); - } - - @Override - public void onStatusChanged(@NonNull String provider, int status, Bundle extras) { - } - - @Override - public void onProviderEnabled(@NonNull String provider) { - gpsStatusManager.onGpsEnabled(); - } - - @Override - public void onProviderDisabled(@NonNull String provider) { - gpsStatusManager.onGpsDisabled(); - } - - private void registerLocationListener() { - if (!LocationManagerCompat.hasProvider(locationManager, LOCATION_PROVIDER)) { - Log.e(TAG, "Device doesn't have GPS."); - return; - } - - LocationRequestCompat locationRequest = new LocationRequestCompat.Builder(gpsInterval.toMillis()) - .setQuality(LocationRequestCompat.QUALITY_HIGH_ACCURACY) - .setMaxUpdateDelayMillis(0) - .build(); - - if (PermissionRequester.GPS.hasPermission(context)) { - try { - Log.i(TAG, "Register for location updates " + context); - LocationManagerCompat.requestLocationUpdates(locationManager, LOCATION_PROVIDER, locationRequest, handler::post, this); - } catch (SecurityException e) { - Log.e(TAG, "Could not register location listener; permissions not granted.", e); - } - } - } - - @Override - public void onGpsStatusChanged(GpsStatusValue currentStatus) { - trackPointCreator.sendGpsStatus(currentStatus); - } -} diff --git a/src/main/java/de/dennisguse/opentracks/sensors/GpsStatusManager.java b/src/main/java/de/dennisguse/opentracks/sensors/GpsStatusManager.java index 2b2296c35..5a5154d95 100644 --- a/src/main/java/de/dennisguse/opentracks/sensors/GpsStatusManager.java +++ b/src/main/java/de/dennisguse/opentracks/sensors/GpsStatusManager.java @@ -31,7 +31,7 @@ class GpsStatusManager { private Duration signalLostThreshold; private GpsStatusValue gpsStatus = GpsStatusValue.GPS_NONE; - private GpsStatusListener client; + private GpsStatusListener observer; private final Context context; @Nullable @@ -44,8 +44,8 @@ class GpsStatusManager { }; - public GpsStatusManager(Context context, GpsStatusListener client, Handler handler) { - this.client = client; + public GpsStatusManager(Context context, GpsStatusListener observer, Handler handler) { + this.observer = observer; this.context = context; this.handler = handler; @@ -54,7 +54,7 @@ class GpsStatusManager { } public void start() { - client.onGpsStatusChanged(GpsStatusValue.GPS_ENABLED); + observer.onGpsStatusChanged(GpsStatusValue.GPS_ENABLED); } /** @@ -62,9 +62,7 @@ class GpsStatusManager { */ public void stop() { stopTimer(); - client.onGpsStatusChanged(GpsStatusValue.GPS_NONE); - client = null; - handler = null; + observer.onGpsStatusChanged(GpsStatusValue.GPS_NONE); } /** @@ -159,8 +157,8 @@ class GpsStatusManager { private void setGpsStatus(GpsStatusValue current) { gpsStatus = current; - if (client != null) { - client.onGpsStatusChanged(current); + if (observer != null) { + observer.onGpsStatusChanged(current); } } diff --git a/src/main/java/de/dennisguse/opentracks/sensors/HeartRateHandler.java b/src/main/java/de/dennisguse/opentracks/sensors/HeartRateHandler.java new file mode 100644 index 000000000..d44bd8b66 --- /dev/null +++ b/src/main/java/de/dennisguse/opentracks/sensors/HeartRateHandler.java @@ -0,0 +1,38 @@ +package de.dennisguse.opentracks.sensors; + +import androidx.annotation.NonNull; + +import de.dennisguse.opentracks.R; +import de.dennisguse.opentracks.data.models.HeartRate; +import de.dennisguse.opentracks.sensors.driver.BluetoothDriver; +import de.dennisguse.opentracks.sensors.driver.Driver; +import de.dennisguse.opentracks.sensors.driver.HeartRateBluetooth; +import de.dennisguse.opentracks.sensors.sensorData.AggregatorHeartRate; + +public class HeartRateHandler extends SensorHandler { + + protected HeartRateHandler(SensorManager sensorManager) { + super(sensorManager); + } + + @NonNull + @Override + protected Driver createDriverBluetooth(String address) { + return new BluetoothDriver<>( + BluetoothUtils.getAdapter(getContext()), + this, + new HeartRateBluetooth() + ); + } + + @NonNull + @Override + protected AggregatorHeartRate createAggregator(String sensorAddress, String sensorName) { + return new AggregatorHeartRate(sensorAddress, sensorName); + } + + @Override + protected int getSensorPreferenceKey() { + return R.string.settings_sensor_bluetooth_heart_rate_key; + } +} diff --git a/src/main/java/de/dennisguse/opentracks/sensors/RunningSpeedCadenceDistanceHandler.java b/src/main/java/de/dennisguse/opentracks/sensors/RunningSpeedCadenceDistanceHandler.java new file mode 100644 index 000000000..d9581d669 --- /dev/null +++ b/src/main/java/de/dennisguse/opentracks/sensors/RunningSpeedCadenceDistanceHandler.java @@ -0,0 +1,37 @@ +package de.dennisguse.opentracks.sensors; + +import androidx.annotation.NonNull; + +import de.dennisguse.opentracks.R; +import de.dennisguse.opentracks.sensors.driver.BluetoothDriver; +import de.dennisguse.opentracks.sensors.driver.Driver; +import de.dennisguse.opentracks.sensors.driver.RunningSpeedAndCadenceBluetooth; +import de.dennisguse.opentracks.sensors.sensorData.AggregatorRunning; + +public class RunningSpeedCadenceDistanceHandler extends SensorHandler { + + protected RunningSpeedCadenceDistanceHandler(SensorManager sensorManager) { + super(sensorManager); + } + + @NonNull + @Override + protected Driver createDriverBluetooth(String address) { + return new BluetoothDriver<>( + BluetoothUtils.getAdapter(getContext()), + this, + new RunningSpeedAndCadenceBluetooth() + ); + } + + @NonNull + @Override + protected AggregatorRunning createAggregator(String sensorAddress, String sensorName) { + return new AggregatorRunning("", null); + } + + @Override + protected int getSensorPreferenceKey() { + return R.string.settings_sensor_bluetooth_running_speed_and_cadence_key; + } +} diff --git a/src/main/java/de/dennisguse/opentracks/sensors/SensorConnector.java b/src/main/java/de/dennisguse/opentracks/sensors/SensorConnector.java deleted file mode 100644 index cea9f44ee..000000000 --- a/src/main/java/de/dennisguse/opentracks/sensors/SensorConnector.java +++ /dev/null @@ -1,11 +0,0 @@ -package de.dennisguse.opentracks.sensors; - -import android.content.Context; -import android.os.Handler; - -public interface SensorConnector { - - void start(Context context, Handler handler); - - void stop(Context context); -} diff --git a/src/main/java/de/dennisguse/opentracks/sensors/SensorHandler.java b/src/main/java/de/dennisguse/opentracks/sensors/SensorHandler.java new file mode 100644 index 000000000..31433932d --- /dev/null +++ b/src/main/java/de/dennisguse/opentracks/sensors/SensorHandler.java @@ -0,0 +1,134 @@ +package de.dennisguse.opentracks.sensors; + +import android.content.Context; +import android.util.Log; + +import androidx.annotation.NonNull; +import androidx.annotation.Nullable; +import androidx.annotation.VisibleForTesting; + +import java.time.Instant; + +import de.dennisguse.opentracks.sensors.driver.Driver; +import de.dennisguse.opentracks.sensors.driver.DriverObserver; +import de.dennisguse.opentracks.sensors.driver.SensorType; +import de.dennisguse.opentracks.sensors.sensorData.Aggregator; +import de.dennisguse.opentracks.sensors.sensorData.SensorData; +import de.dennisguse.opentracks.settings.PreferencesUtils; + + +public abstract class SensorHandler implements DriverObserver { + + private static final String TAG = SensorHandler.class.getSimpleName(); + + private final SensorManager sensorManager; + + @VisibleForTesting + public Driver driver; + + protected Aggregator aggregator; + + protected SensorHandler(SensorManager sensorManager) { + this.sensorManager = sensorManager; + } + + void connect() { + disconnect(); + + String address = PreferencesUtils.getString(getSensorPreferenceKey(), getSensorPreferenceDefaultValue()); + + switch (PreferencesUtils.getSensorType(address)) { + case NONE -> { + onDisconnected(); + return; + } + case INTERNAL -> { + driver = createDriverInternal(); + } + case REMOTE -> { + driver = createDriverBluetooth(address); + } + default -> throw new UnsupportedOperationException(); + } + + driver.connect(sensorManager.getContext(), sensorManager.getHandler(), address); + } + + void disconnect() { + if (driver == null) return; + + driver.disconnect(); + driver = null; + } + + @NonNull + protected Driver createDriverInternal() { + throw new RuntimeException("Not implemented"); + } + + @NonNull + protected Driver createDriverBluetooth(String address) { + throw new RuntimeException("Not implemented"); + } + + @NonNull + protected abstract Aggregator createAggregator(String sensorAddress, String sensorName); + + protected abstract int getSensorPreferenceKey(); + + protected String getSensorPreferenceDefaultValue() { + return SensorType.NONE.getPreferenceValue(); + } + + protected Context getContext() { + return sensorManager.getContext(); + } + + void resetAggregated() { + if (aggregator != null) { + aggregator.resetAggregated(); + } + } + + @Nullable + SensorData getSensorData(Instant now) { + if (aggregator == null) return null; + if (!aggregator.hasReceivedData()) return null; + + return aggregator.getAggregatedValueWithSensorName(now); + } + + //TODO REMOVE + @VisibleForTesting + public void setAggregator(Aggregator aggregator) { + this.aggregator = aggregator; + } + + public void onConnected(String sensorAddress, String sensorName) { + SensorHandler.this.aggregator = createAggregator(sensorAddress, sensorName); + } + + @Override + public void onConnectionLost() { + if (SensorHandler.this.aggregator == null) { + Log.d(TAG, "Connection lost, but where not connected."); + return; + } + + SensorHandler.this.aggregator.resetAggregated(); + } + + @Override + public void onDataReceived(AggregatorInput value) { + if (aggregator == null) { + throw new RuntimeException("Received data while being disconnected."); + } + SensorHandler.this.aggregator.add(sensorManager.getNow(), value); + sensorManager.onChange(); + } + + @Override + public void onDisconnected() { + SensorHandler.this.aggregator = null; + } +} diff --git a/src/main/java/de/dennisguse/opentracks/sensors/SensorManager.java b/src/main/java/de/dennisguse/opentracks/sensors/SensorManager.java index d645748ea..6021848a7 100644 --- a/src/main/java/de/dennisguse/opentracks/sensors/SensorManager.java +++ b/src/main/java/de/dennisguse/opentracks/sensors/SensorManager.java @@ -6,153 +6,329 @@ import android.os.Handler; import android.os.PowerManager; import android.util.Log; +import androidx.annotation.NonNull; import androidx.annotation.Nullable; import androidx.annotation.VisibleForTesting; import java.time.Instant; -import de.dennisguse.opentracks.sensors.sensorData.Aggregator; -import de.dennisguse.opentracks.sensors.sensorData.Raw; -import de.dennisguse.opentracks.sensors.sensorData.SensorDataAggregator; +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.Position; +import de.dennisguse.opentracks.data.models.Speed; +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.AggregatorRunning; +import de.dennisguse.opentracks.sensors.sensorData.AggregatorTemperature; +import de.dennisguse.opentracks.sensors.sensorData.SensorData; import de.dennisguse.opentracks.sensors.sensorData.SensorDataSet; import de.dennisguse.opentracks.services.handlers.TrackPointCreator; +import de.dennisguse.opentracks.settings.PreferencesUtils; import de.dennisguse.opentracks.util.SystemUtils; public class SensorManager implements SharedPreferences.OnSharedPreferenceChangeListener { private static final String TAG = SensorManager.class.getSimpleName(); - //TODO Should be final and not be visible for testing - @VisibleForTesting - public SensorDataAggregator sensorDataAggregator; + private Context context; + + private Handler handler; private PowerManager.WakeLock wakeLock; private final TrackPointCreator observer; - private final SensorDataChangedObserver listener = new SensorDataChangedObserver() { + private HeartRateHandler heartRateHandler; + private TemperatureHandler temperatureHandler; + private CyclingCadenceHandler cyclingCadenceHandler; + private CyclingDistanceSpeedHandler cyclingDistanceSpeedHandler; + private CyclingPowerHandler cyclingPowerHandler; + private RunningSpeedCadenceDistanceHandler runningSpeedCadenceDistanceHandler; - @Override - public void onConnect(Aggregator aggregator) { - sensorDataAggregator.add(aggregator); - } + private AltitudeChangeHandler altitudeChangeHandler; - @Override - public void onChange(Raw data) { - sensorDataAggregator.update(data); - observer.onChange(); - } - - @Override - public void onDisconnect(Aggregator aggregator) { - sensorDataAggregator.add(aggregator); - } - - @Override - public void onRemove(Aggregator aggregator) { - sensorDataAggregator.remove(aggregator); - } - - @Override - public Instant getNow() { - return observer.createNow(); - } - }; - - private BluetoothRemoteSensorManager bluetoothSensorManager; - - private GainManager altitudeSumManager; - - private GpsManager gpsManager; + private GpsHandler gpsHandler; public SensorManager(TrackPointCreator observer) { this.observer = observer; - this.sensorDataAggregator = new SensorDataAggregator(); } public void start(Context context, Handler handler) { - if (gpsManager != null) { + if (isStarted()) { throw new RuntimeException("SensorManager cannot be started twice; stop first."); } wakeLock = SystemUtils.acquireWakeLock(context, wakeLock); + this.context = context; + this.handler = handler; - gpsManager = new GpsManager(observer, listener); - altitudeSumManager = new GainManager(listener); - bluetoothSensorManager = new BluetoothRemoteSensorManager(context, handler, listener); + gpsHandler = new GpsHandler(this, observer); + altitudeChangeHandler = new AltitudeChangeHandler(this); + heartRateHandler = new HeartRateHandler(this); + temperatureHandler = new TemperatureHandler(this); + cyclingCadenceHandler = new CyclingCadenceHandler(this); + cyclingDistanceSpeedHandler = new CyclingDistanceSpeedHandler(this); + cyclingPowerHandler = new CyclingPowerHandler(this); + runningSpeedCadenceDistanceHandler = new RunningSpeedCadenceDistanceHandler(this); onSharedPreferenceChanged(null, null); - - gpsManager.start(context, handler); - altitudeSumManager.start(context, handler); - bluetoothSensorManager.start(context, handler); } - public void stop(Context context) { + public void stop() { wakeLock = SystemUtils.releaseWakeLock(wakeLock); - bluetoothSensorManager.stop(context); - bluetoothSensorManager = null; + heartRateHandler.disconnect(); + heartRateHandler = null; - altitudeSumManager.stop(context); - altitudeSumManager = null; + temperatureHandler.disconnect(); + temperatureHandler = null; - gpsManager.stop(context); - gpsManager = null; + cyclingCadenceHandler.disconnect(); + cyclingCadenceHandler = null; - sensorDataAggregator.clear(); - } + cyclingDistanceSpeedHandler.disconnect(); + cyclingDistanceSpeedHandler = null; - public SensorDataSet getSensorDataSet(Instant now) { - return sensorDataAggregator.getSensorData(now); + cyclingPowerHandler.disconnect(); + cyclingPowerHandler = null; + + runningSpeedCadenceDistanceHandler.disconnect(); + runningSpeedCadenceDistanceHandler = null; + + altitudeChangeHandler.disconnect(); + altitudeChangeHandler = null; + + gpsHandler.disconnect(); + gpsHandler = null; + + context = null; + handler = null; } public void reset() { - if (bluetoothSensorManager == null || altitudeSumManager == null) { + if (gpsHandler == null || altitudeChangeHandler == null) { Log.d(TAG, "No recording running and no reset necessary."); return; } - sensorDataAggregator.reset(); + + Log.i(TAG, "Resetting data"); + + heartRateHandler.resetAggregated(); + temperatureHandler.resetAggregated(); + cyclingCadenceHandler.resetAggregated(); + cyclingDistanceSpeedHandler.resetAggregated(); + cyclingPowerHandler.resetAggregated(); + runningSpeedCadenceDistanceHandler.resetAggregated(); + altitudeChangeHandler.resetAggregated(); + gpsHandler.resetAggregated(); } - @VisibleForTesting - public void onChanged(Raw data) { - listener.onChange(data); - } - - public GpsManager getGpsManager() { - return gpsManager; + public GpsHandler getGpsHandler() { + return gpsHandler; } @Deprecated @VisibleForTesting - public GainManager getAltitudeSumManager() { - return altitudeSumManager; - } - - @Deprecated - @VisibleForTesting - public void setAltitudeSumManager(GainManager altitudeSumManager) { - this.altitudeSumManager = altitudeSumManager; + public AltitudeChangeHandler getAltitudeChangeHandler() { + return altitudeChangeHandler; } @Override public void onSharedPreferenceChanged(SharedPreferences sharedPreferences, @Nullable String key) { - if (gpsManager != null) { - gpsManager.onSharedPreferenceChanged(sharedPreferences, key); - bluetoothSensorManager.onSharedPreferenceChanged(sharedPreferences, key); + if (!isStarted()) return; + + if (PreferencesUtils.isKey(gpsHandler.getSensorPreferenceKey(), key)) { + gpsHandler.connect(); + } + + if (PreferencesUtils.isKey(altitudeChangeHandler.getSensorPreferenceKey(), key)) { + altitudeChangeHandler.connect(); + } + + if (PreferencesUtils.isKey(heartRateHandler.getSensorPreferenceKey(), key)) { + heartRateHandler.connect(); + } + + if (PreferencesUtils.isKey(temperatureHandler.getSensorPreferenceKey(), key)) { + temperatureHandler.connect(); + } + + if (PreferencesUtils.isKey(cyclingCadenceHandler.getSensorPreferenceKey(), key)) { + cyclingCadenceHandler.connect(); + } + + if (PreferencesUtils.isKey(cyclingDistanceSpeedHandler.getSensorPreferenceKey(), key)) { + cyclingDistanceSpeedHandler.connect(); + } + + if (PreferencesUtils.isKey(cyclingCadenceHandler.getSensorPreferenceKey(), key)) { + cyclingPowerHandler.connect(); + } + + if (PreferencesUtils.isKey(runningSpeedCadenceDistanceHandler.getSensorPreferenceKey(), key)) { + runningSpeedCadenceDistanceHandler.connect(); } } - public interface SensorDataChangedObserver { + public Instant getNow() { + return observer.getNow(); + } - void onConnect(Aggregator sensorData); - void onChange(Raw sensorData); + public Context getContext() { + return context; + } - void onDisconnect(Aggregator sensorData); + public Handler getHandler() { + return handler; + } - void onRemove(Aggregator sensorData); + private SensorData getHeartRate(Instant now) { + SensorData value = heartRateHandler.getSensorData(now); + if (value != null) { - Instant getNow(); + //TODO Move this check into HeartRateHandler + if (value.data().isValid()) return value; + } + + return null; + } + + private SensorData getCadence(Instant now) { + { + SensorData value = cyclingCadenceHandler.getSensorData(now); + if (value != null) { + return cyclingCadenceHandler.getSensorData(now); + } + } + + SensorData value = runningSpeedCadenceDistanceHandler.getSensorData(now); + if (value != null && value.data().cadence() != null) { + return new SensorData<>(value.data().cadence(), value.sensorNameOrAddress()); + } + + return null; + } + + private SensorData getDistance(Instant now) { + { + SensorData value = cyclingDistanceSpeedHandler.getSensorData(now); + if (value != null) { + return new SensorData<>(value.data().distanceOverall(), value.sensorNameOrAddress()); + } + } + + SensorData value = runningSpeedCadenceDistanceHandler.getSensorData(now); + if (value != null) { + return new SensorData<>(value.data().distance(), value.sensorNameOrAddress()); + } + + return null; + } + + //TOOD simplify? + private SensorData getSpeed(Instant now) { + { + SensorData value = cyclingDistanceSpeedHandler.getSensorData(now); + if (value != null && value.data() != null && value.data().speed() != null) { + return new SensorData<>(value.data().speed(), value.sensorNameOrAddress()); + } + } + + SensorData value = runningSpeedCadenceDistanceHandler.getSensorData(now); + if (value != null && value.data() != null && value.data().speed() != null) { + return new SensorData<>(value.data().speed(), value.sensorNameOrAddress()); + } + + return null; + } + + public void onChange() { + observer.onChange(); + } + + public SensorDataSet getSensorDataSet(Instant now) { + SensorData gpsSensorData = gpsHandler.getSensorData(now); + + var a = temperatureHandler.getSensorData(now); + var b = altitudeChangeHandler.getSensorData(now); + + return new SensorDataSet( + gpsSensorData != null ? gpsSensorData : new SensorData<>(Position.of(now), ""), //We always need a Position(now) + getSpeed(now), + getDistance(now), + getHeartRate(now), + temperatureHandler.getSensorData(now), + getCadence(now), + cyclingPowerHandler.getSensorData(now), + altitudeChangeHandler.getSensorData(now) + ); + } + + @VisibleForTesting + public void clear() { + Log.i(TAG, "Removing all aggregators"); + + //TODO This is to ExportImportTest alive; refactor test. + if (this.altitudeChangeHandler != null) { + this.altitudeChangeHandler.setAggregator(null); + } + if (this.heartRateHandler != null) { + this.heartRateHandler.setAggregator(null); + } + if (this.temperatureHandler != null) { + this.temperatureHandler.setAggregator(null); + } + if (this.cyclingCadenceHandler != null) { + this.cyclingCadenceHandler.setAggregator(null); + } + if (cyclingDistanceSpeedHandler != null) { + cyclingDistanceSpeedHandler.setAggregator(null); + } + if (cyclingPowerHandler != null) { + cyclingPowerHandler.setAggregator(null); + } + if (runningSpeedCadenceDistanceHandler != null) { + runningSpeedCadenceDistanceHandler.setAggregator(null); + } + if (this.gpsHandler != null) { + this.gpsHandler.setAggregator(null); + } + } + + private boolean isStarted() { + return wakeLock != null; + } + + @VisibleForTesting + public void setAggregator(AggregatorHeartRate data) { + heartRateHandler.setAggregator(data); + } + + @VisibleForTesting + public void setAggregator(@NonNull AggregatorTemperature data) { + temperatureHandler.setAggregator(data); + } + + @VisibleForTesting + public void setAggregator(@NonNull AggregatorCyclingCadence data) { + cyclingCadenceHandler.setAggregator(data); + } + + @VisibleForTesting + public void setAggregator(@NonNull AggregatorCyclingDistanceSpeed data) { + cyclingDistanceSpeedHandler.setAggregator(data); + } + + @VisibleForTesting + public void setAggregator(@NonNull AggregatorCyclingPower data) { + cyclingPowerHandler.setAggregator(data); + } + + @VisibleForTesting + public void setAggregator(@NonNull AggregatorRunning data) { + runningSpeedCadenceDistanceHandler.setAggregator(data); } } diff --git a/src/main/java/de/dennisguse/opentracks/sensors/TemperatureHandler.java b/src/main/java/de/dennisguse/opentracks/sensors/TemperatureHandler.java new file mode 100644 index 000000000..146797d5d --- /dev/null +++ b/src/main/java/de/dennisguse/opentracks/sensors/TemperatureHandler.java @@ -0,0 +1,38 @@ +package de.dennisguse.opentracks.sensors; + +import androidx.annotation.NonNull; + +import de.dennisguse.opentracks.R; +import de.dennisguse.opentracks.data.models.Temperature; +import de.dennisguse.opentracks.sensors.driver.BluetoothDriver; +import de.dennisguse.opentracks.sensors.driver.Driver; +import de.dennisguse.opentracks.sensors.driver.TemperatureBluetooth; +import de.dennisguse.opentracks.sensors.sensorData.AggregatorTemperature; + +public class TemperatureHandler extends SensorHandler { + + protected TemperatureHandler(SensorManager sensorManager) { + super(sensorManager); + } + + @NonNull + @Override + protected Driver createDriverBluetooth(String address) { + return new BluetoothDriver<>( + BluetoothUtils.getAdapter(getContext()), + this, + new TemperatureBluetooth() + ); + } + + @NonNull + @Override + protected AggregatorTemperature createAggregator(String sensorAddress, String sensorName) { + return new AggregatorTemperature(sensorAddress, sensorName); + } + + @Override + protected int getSensorPreferenceKey() { + return R.string.settings_sensor_bluetooth_temperature_key; + } +} diff --git a/src/main/java/de/dennisguse/opentracks/sensors/BluetoothHandlerBarometricPressure.java b/src/main/java/de/dennisguse/opentracks/sensors/driver/BarometerBluetooth.java similarity index 54% rename from src/main/java/de/dennisguse/opentracks/sensors/BluetoothHandlerBarometricPressure.java rename to src/main/java/de/dennisguse/opentracks/sensors/driver/BarometerBluetooth.java index ce7f58823..cfb2186b9 100644 --- a/src/main/java/de/dennisguse/opentracks/sensors/BluetoothHandlerBarometricPressure.java +++ b/src/main/java/de/dennisguse/opentracks/sensors/driver/BarometerBluetooth.java @@ -1,4 +1,4 @@ -package de.dennisguse.opentracks.sensors; +package de.dennisguse.opentracks.sensors.driver; import android.bluetooth.BluetoothGattCharacteristic; @@ -6,12 +6,9 @@ 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.AggregatorBarometer; -import de.dennisguse.opentracks.sensors.sensorData.Raw; -import de.dennisguse.opentracks.sensors.sensorData.SensorHandlerInterface; +import de.dennisguse.opentracks.sensors.ServiceMeasurementUUID; -public class BluetoothHandlerBarometricPressure implements SensorHandlerInterface { +public class BarometerBluetooth implements BluetoothDriver.BluetoothParser { static final UUID ENVIRONMENTAL_SENSING_SERVICE = new UUID(0x181A00001000L, 0x800000805f9b34fbL); public static final ServiceMeasurementUUID BAROMETRIC_PRESSURE = new ServiceMeasurementUUID( ENVIRONMENTAL_SENSING_SERVICE, @@ -24,16 +21,8 @@ public class BluetoothHandlerBarometricPressure implements SensorHandlerInterfac } @Override - public Aggregator createEmptySensorData(String address, String name) { - return new AggregatorBarometer(address, name); - } - - @Override - public void handlePayload(SensorManager.SensorDataChangedObserver observer, ServiceMeasurementUUID serviceMeasurementUUID, String sensorName, String address, BluetoothGattCharacteristic characteristic) { - AtmosphericPressure value = parseEnvironmentalSensing(characteristic); - if (value == null) return; - - observer.onChange(new Raw<>(observer.getNow(), value)); + public AtmosphericPressure parsePayload(ServiceMeasurementUUID serviceMeasurementUUID, String sensorName, BluetoothGattCharacteristic characteristic) { + return parseEnvironmentalSensing(characteristic); } /** 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 0929e875c..b200d1f09 100644 --- a/src/main/java/de/dennisguse/opentracks/sensors/driver/BarometerInternal.java +++ b/src/main/java/de/dennisguse/opentracks/sensors/driver/BarometerInternal.java @@ -12,9 +12,6 @@ import androidx.annotation.NonNull; import java.util.concurrent.TimeUnit; import de.dennisguse.opentracks.data.models.AtmosphericPressure; -import de.dennisguse.opentracks.sensors.SensorManager; -import de.dennisguse.opentracks.sensors.sensorData.AggregatorBarometer; -import de.dennisguse.opentracks.sensors.sensorData.Raw; public class BarometerInternal implements Driver { @@ -22,9 +19,9 @@ public class BarometerInternal implements Driver { private static final int SAMPLING_PERIOD = (int) TimeUnit.SECONDS.toMicros(5); - private final SensorManager.SensorDataChangedObserver observer; + private final DriverObserver observer; - private Context context; + private android.hardware.SensorManager sensorService; private final SensorEventListener sensorEventListener = new SensorEventListener() { @Override @@ -34,7 +31,7 @@ public class BarometerInternal implements Driver { return; } - observer.onChange(new Raw<>(observer.getNow(), AtmosphericPressure.ofHPA(event.values[0]))); + observer.onDataReceived(AtmosphericPressure.ofHPA(event.values[0])); } @Override @@ -43,40 +40,34 @@ public class BarometerInternal implements Driver { } }; - public BarometerInternal(@NonNull SensorManager.SensorDataChangedObserver observer) { + public BarometerInternal(@NonNull DriverObserver observer) { this.observer = observer; - } @Override public void connect(Context context, Handler handler, String addressIgnored) { + sensorService = (android.hardware.SensorManager) context.getSystemService(Context.SENSOR_SERVICE); + Sensor pressureSensor = sensorService.getDefaultSensor(Sensor.TYPE_PRESSURE); - android.hardware.SensorManager sensorManager = (android.hardware.SensorManager) context.getSystemService(Context.SENSOR_SERVICE); - Sensor pressureSensor = sensorManager.getDefaultSensor(Sensor.TYPE_PRESSURE); - if (pressureSensor == null) { - Log.w(TAG, "No pressure sensor available."); + if (pressureSensor != null && sensorService.registerListener(sensorEventListener, pressureSensor, SAMPLING_PERIOD, handler)) { + observer.onConnected(null, null); return; } - if (sensorManager.registerListener(sensorEventListener, pressureSensor, SAMPLING_PERIOD, handler)) { - this.context = context; - observer.onConnect(new AggregatorBarometer("internal", null)); - return; - } - - disconnect(); + Log.w(TAG, "No pressure sensor available."); + observer.onDisconnected(); } + @Override public boolean isConnected() { - return context != null; + return sensorService != null; } @Override public void disconnect() { if (!isConnected()) return; - android.hardware.SensorManager sensorManager = (android.hardware.SensorManager) context.getSystemService(Context.SENSOR_SERVICE); - sensorManager.unregisterListener(sensorEventListener); - this.context = null; + sensorService.unregisterListener(sensorEventListener); + sensorService = null; } } diff --git a/src/main/java/de/dennisguse/opentracks/sensors/BluetoothConnectionManager.java b/src/main/java/de/dennisguse/opentracks/sensors/driver/BluetoothDriver.java similarity index 71% rename from src/main/java/de/dennisguse/opentracks/sensors/BluetoothConnectionManager.java rename to src/main/java/de/dennisguse/opentracks/sensors/driver/BluetoothDriver.java index ac1cc7ee2..ad30977ef 100644 --- a/src/main/java/de/dennisguse/opentracks/sensors/BluetoothConnectionManager.java +++ b/src/main/java/de/dennisguse/opentracks/sensors/driver/BluetoothDriver.java @@ -14,7 +14,7 @@ * the License. */ -package de.dennisguse.opentracks.sensors; +package de.dennisguse.opentracks.sensors.driver; import android.annotation.SuppressLint; import android.bluetooth.BluetoothAdapter; @@ -31,23 +31,25 @@ 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.driver.Driver; -import de.dennisguse.opentracks.sensors.sensorData.SensorHandlerInterface; +import de.dennisguse.opentracks.sensors.BluetoothUtils; +import de.dennisguse.opentracks.sensors.ServiceMeasurementUUID; /** * Manages connection to a Bluetooth LE sensor and subscribes for onChange-notifications. */ +//TODO Re-act to Bluetooth disabled/enabled @SuppressLint("MissingPermission") -public class BluetoothConnectionManager implements Driver { +public class BluetoothDriver implements Driver { - private static final String TAG = BluetoothConnectionManager.class.getSimpleName(); + private static final String TAG = BluetoothDriver.class.getSimpleName(); - private final SensorManager.SensorDataChangedObserver observer; + private final DriverObserver observer; - private final SensorHandlerInterface sensorHandler; + private final BluetoothParser sensorHandler; private final BluetoothAdapter bluetoothAdapter; private BluetoothGatt bluetoothGatt; @@ -55,27 +57,29 @@ public class BluetoothConnectionManager implements Driver { @Override public void onConnectionStateChange(BluetoothGatt gatt, int status, int newState) { switch (newState) { - case BluetoothProfile.STATE_CONNECTING -> - Log.i(TAG, gatt.getDevice() + ": connecting to sensor"); + case BluetoothProfile.STATE_CONNECTING -> { + Log.i(TAG, gatt.getDevice() + ": connecting to sensor"); + } case BluetoothProfile.STATE_CONNECTED -> { Log.i(TAG, gatt.getDevice() + ": connected to sensor; discovering services"); gatt.discoverServices(); } - case BluetoothProfile.STATE_DISCONNECTING -> - Log.i(TAG, gatt.getDevice() + ": disconnecting from sensor: "); + case BluetoothProfile.STATE_DISCONNECTING -> Log.i(TAG, gatt.getDevice() + ": disconnecting from sensor: "); case BluetoothProfile.STATE_DISCONNECTED -> { //This is also triggered, if no connection was established (ca. 30s) Log.i(TAG, gatt.getDevice() + ": disconnected from sensor: trying to reconnect"); if (gatt.connect()) { Log.e(TAG, gatt.getDevice() + ": could not trigger reconnect for sensor"); } - clearData(); + observer.onConnectionLost(); } } } @Override public void onServicesDiscovered(@NonNull BluetoothGatt gatt, int status) { + BluetoothDevice device = gatt.getDevice(); + BluetoothGattService gattService = null; ServiceMeasurementUUID serviceMeasurement = null; for (ServiceMeasurementUUID s : sensorHandler.getServices()) { @@ -87,13 +91,13 @@ public class BluetoothConnectionManager implements Driver { } if (gattService == null) { - Log.e(TAG, gatt.getDevice() + ": could not get gattService for serviceUUID=" + serviceMeasurement); + Log.e(TAG, device + ": could not get gattService for serviceUUID=" + serviceMeasurement); return; } BluetoothGattCharacteristic characteristic = gattService.getCharacteristic(serviceMeasurement.measurementUUID()); if (characteristic == null) { - Log.e(TAG, gatt.getDevice() + ": could not get BluetoothCharacteristic for serviceUUID=" + serviceMeasurement.serviceUUID() + " characteristicUUID=" + serviceMeasurement.measurementUUID()); + Log.e(TAG, device + ": could not get BluetoothCharacteristic for serviceUUID=" + serviceMeasurement.serviceUUID() + " characteristicUUID=" + serviceMeasurement.measurementUUID()); return; } gatt.setCharacteristicNotification(characteristic, true); @@ -111,6 +115,8 @@ public class BluetoothConnectionManager implements Driver { if (!gatt.writeDescriptor(descriptor)) { Log.e(TAG, "CLIENT_CHARACTERISTIC_CONFIG_UUID descriptor could not be written"); } + + observer.onConnected(device.getAddress(), device.getName()); } @Override @@ -127,11 +133,15 @@ public class BluetoothConnectionManager implements Driver { return; } - sensorHandler.handlePayload(observer, serviceMeasurementUUID.get(), gatt.getDevice().getName(), gatt.getDevice().getAddress(), characteristic); + T value = sensorHandler.parsePayload(serviceMeasurementUUID.get(), gatt.getDevice().getName(), characteristic); + if (value == null) { + return; + } + observer.onDataReceived(value); } }; - BluetoothConnectionManager(BluetoothAdapter bluetoothAdapter, SensorManager.SensorDataChangedObserver observer, SensorHandlerInterface sensorHandler) { + public BluetoothDriver(BluetoothAdapter bluetoothAdapter, DriverObserver observer, BluetoothParser sensorHandler) { this.bluetoothAdapter = bluetoothAdapter; this.observer = observer; this.sensorHandler = sensorHandler; @@ -144,26 +154,11 @@ public class BluetoothConnectionManager implements Driver { return; } - if (SensorType.NONE.getPreferenceValue().equals(address)) { - Log.w(TAG, "NONE: going to disconnect"); - if (isConnected()) { - disconnect(); - observer.onRemove(sensorHandler.createEmptySensorData(null, null)); - } - return; - } - if (isConnected()) { - Log.w(TAG, "Already connected; ignoring."); + Log.e(TAG, "Already connected; ignoring."); return; } - if (isSameBluetoothDevice(address)) { - return; - } else { - disconnect(); - } - BluetoothDevice device; try { device = bluetoothAdapter.getRemoteDevice(address); @@ -175,37 +170,23 @@ public class BluetoothConnectionManager implements Driver { Log.d(TAG, device + ": trying to connect"); bluetoothGatt = device.connectGatt(context, false, connectCallback, BluetoothDevice.TRANSPORT_AUTO, 0, handler); - - observer.onConnect(sensorHandler.createEmptySensorData(device.getAddress(), device.getName())); - } - - private synchronized void clearData() { - observer.onDisconnect(sensorHandler.createEmptySensorData(bluetoothGatt.getDevice().getAddress(), bluetoothGatt.getDevice().getName())); } @Override public synchronized void disconnect() { if (!isConnected()) { - Log.w(TAG, "Not connected; no need to re-connect."); + Log.w(TAG, "Not connected; cannot disconnect."); return; } Log.i(TAG, bluetoothGatt.getDevice() + ": start disconnect"); bluetoothGatt.disconnect(); bluetoothGatt.close(); - clearData(); Log.i(TAG, bluetoothGatt.getDevice() + ": disconnect finished"); bluetoothGatt = null; + + observer.onDisconnected(); } - private synchronized boolean isSameBluetoothDevice(String address) { - if (bluetoothGatt == null) { - return false; - } - - return address.equals(bluetoothGatt.getDevice().getAddress()); - } - - private boolean isBluetoothEnabled() { return bluetoothAdapter != null && bluetoothAdapter.isEnabled(); } @@ -214,4 +195,11 @@ public class BluetoothConnectionManager implements Driver { public boolean isConnected() { return bluetoothGatt != null; } + + public interface BluetoothParser { + + List getServices(); + + T parsePayload(ServiceMeasurementUUID serviceMeasurementUUID, String sensorName, BluetoothGattCharacteristic characteristic); + } } diff --git a/src/main/java/de/dennisguse/opentracks/sensors/driver/CyclingCadenceBluetooth.java b/src/main/java/de/dennisguse/opentracks/sensors/driver/CyclingCadenceBluetooth.java new file mode 100644 index 000000000..6177f131b --- /dev/null +++ b/src/main/java/de/dennisguse/opentracks/sensors/driver/CyclingCadenceBluetooth.java @@ -0,0 +1,49 @@ +package de.dennisguse.opentracks.sensors.driver; + +import android.bluetooth.BluetoothGattCharacteristic; +import android.util.Log; +import android.util.Pair; + +import java.util.List; + +import de.dennisguse.opentracks.sensors.ServiceMeasurementUUID; + +public class CyclingCadenceBluetooth implements BluetoothDriver.BluetoothParser { + private static final String TAG = CyclingCadenceBluetooth.class.getSimpleName(); + + public static final List CYCLING_CADENCE = List.of( + CyclingPowerBluetooth.CYCLING_POWER, + CyclingDistanceSpeedBluetooth.CYCLING_SPEED_CADENCE + ); + + @Override + public List getServices() { + return CYCLING_CADENCE; + } + + @Override + public CrankData parsePayload(ServiceMeasurementUUID serviceMeasurementUUID, String sensorName, BluetoothGattCharacteristic characteristic) { + if (serviceMeasurementUUID.equals(CyclingPowerBluetooth.CYCLING_POWER)) { + CyclingPowerBluetooth.Data data = CyclingPowerBluetooth.parseCyclingPower(characteristic); + if (data != null && data.crank() != null) { + return data.crank(); + } + } + + if (serviceMeasurementUUID.equals(CyclingDistanceSpeedBluetooth.CYCLING_SPEED_CADENCE)) { + Pair data = CyclingDistanceSpeedBluetooth.parseCyclingCrankAndWheel(characteristic); + + if (data != null && data.second != null) { + return data.second; + } + } + + Log.e(TAG, "Don't know how to decode this payload."); + return null; + } + + 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/driver/CyclingDistanceSpeedBluetooth.java similarity index 61% rename from src/main/java/de/dennisguse/opentracks/sensors/BluetoothHandlerCyclingDistanceSpeed.java rename to src/main/java/de/dennisguse/opentracks/sensors/driver/CyclingDistanceSpeedBluetooth.java index 28219799f..30959e422 100644 --- a/src/main/java/de/dennisguse/opentracks/sensors/BluetoothHandlerCyclingDistanceSpeed.java +++ b/src/main/java/de/dennisguse/opentracks/sensors/driver/CyclingDistanceSpeedBluetooth.java @@ -1,4 +1,4 @@ -package de.dennisguse.opentracks.sensors; +package de.dennisguse.opentracks.sensors.driver; import android.bluetooth.BluetoothGattCharacteristic; import android.util.Pair; @@ -9,11 +9,9 @@ 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; +import de.dennisguse.opentracks.sensors.ServiceMeasurementUUID; -public class BluetoothHandlerCyclingDistanceSpeed implements SensorHandlerInterface { +public class CyclingDistanceSpeedBluetooth implements BluetoothDriver.BluetoothParser { public static final ServiceMeasurementUUID CYCLING_SPEED_CADENCE = new ServiceMeasurementUUID( new UUID(0x181600001000L, 0x800000805f9b34fbL), @@ -26,21 +24,18 @@ public class BluetoothHandlerCyclingDistanceSpeed implements SensorHandlerInterf } @Override - public AggregatorCyclingDistanceSpeed createEmptySensorData(String address, String name) { - return new AggregatorCyclingDistanceSpeed(address, name); - } - - @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<>(observer.getNow(), data.first)); + public WheelData parsePayload(ServiceMeasurementUUID serviceMeasurementUUID, String sensorName, BluetoothGattCharacteristic characteristic) { + Pair data = parseCyclingCrankAndWheel(characteristic); + if (data != null && data.first != null) { + return data.first; } + + return null; } @VisibleForTesting(otherwise = VisibleForTesting.PACKAGE_PRIVATE) - public static Pair parseCyclingCrankAndWheel(String address, String sensorName, @NonNull BluetoothGattCharacteristic characteristic) { + public static Pair parseCyclingCrankAndWheel(@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) { @@ -61,13 +56,13 @@ public class BluetoothHandlerCyclingDistanceSpeed implements SensorHandlerInterf index += 2; } - BluetoothHandlerCyclingCadence.CrankData crankData = null; + CyclingCadenceBluetooth.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); + crankData = new CyclingCadenceBluetooth.CrankData(crankCount, crankTime); } return new Pair<>(wheelData, crankData); @@ -78,5 +73,6 @@ public class BluetoothHandlerCyclingDistanceSpeed implements SensorHandlerInterf long wheelRevolutionsCount, // UINT32 int wheelRevolutionsTime // UINT16; 1/1024s - ) {} + ) { + } } diff --git a/src/main/java/de/dennisguse/opentracks/sensors/BluetoothHandlerCyclingPower.java b/src/main/java/de/dennisguse/opentracks/sensors/driver/CyclingPowerBluetooth.java similarity index 66% rename from src/main/java/de/dennisguse/opentracks/sensors/BluetoothHandlerCyclingPower.java rename to src/main/java/de/dennisguse/opentracks/sensors/driver/CyclingPowerBluetooth.java index 126bea6b0..15c70c069 100644 --- a/src/main/java/de/dennisguse/opentracks/sensors/BluetoothHandlerCyclingPower.java +++ b/src/main/java/de/dennisguse/opentracks/sensors/driver/CyclingPowerBluetooth.java @@ -1,4 +1,4 @@ -package de.dennisguse.opentracks.sensors; +package de.dennisguse.opentracks.sensors.driver; import android.bluetooth.BluetoothGattCharacteristic; @@ -9,11 +9,9 @@ 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; +import de.dennisguse.opentracks.sensors.ServiceMeasurementUUID; -public class BluetoothHandlerCyclingPower implements SensorHandlerInterface { +public class CyclingPowerBluetooth implements BluetoothDriver.BluetoothParser { public static final ServiceMeasurementUUID CYCLING_POWER = new ServiceMeasurementUUID( new UUID(0x181800001000L, 0x800000805f9b34fbL), @@ -26,17 +24,8 @@ public class BluetoothHandlerCyclingPower implements SensorHandlerInterface { } @Override - public AggregatorCyclingPower createEmptySensorData(String address, String name) { - return new AggregatorCyclingPower(address, name); - } - - @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<>(observer.getNow(), cyclingPower)); - } + public CyclingPowerBluetooth.Data parsePayload(@NonNull ServiceMeasurementUUID serviceMeasurementUUID, String sensorName, BluetoothGattCharacteristic characteristic) { + return parseCyclingPower(characteristic); } @@ -69,19 +58,22 @@ public class BluetoothHandlerCyclingPower implements SensorHandlerInterface { index += 2 + 2; } - BluetoothHandlerCyclingCadence.CrankData crankData = null; + CyclingCadenceBluetooth.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); + crankData = new CyclingCadenceBluetooth.CrankData(crankCount, crankTime); } return new Data(Power.of(instantaneousPower), crankData); } - public record Data(Power power, BluetoothHandlerCyclingCadence.CrankData crank) {} - + public record Data( + Power power, + CyclingCadenceBluetooth.CrankData crank + ) { + } } diff --git a/src/main/java/de/dennisguse/opentracks/sensors/driver/Driver.java b/src/main/java/de/dennisguse/opentracks/sensors/driver/Driver.java index 98157de60..bc5628393 100644 --- a/src/main/java/de/dennisguse/opentracks/sensors/driver/Driver.java +++ b/src/main/java/de/dennisguse/opentracks/sensors/driver/Driver.java @@ -4,8 +4,11 @@ import android.content.Context; import android.os.Handler; public interface Driver { + + //TODO Address is optional for internal drivers. Move address to driver instantiation. void connect(Context context, Handler handler, String address); + //TODO Remove from interface? boolean isConnected(); void disconnect(); diff --git a/src/main/java/de/dennisguse/opentracks/sensors/driver/DriverObserver.java b/src/main/java/de/dennisguse/opentracks/sensors/driver/DriverObserver.java new file mode 100644 index 000000000..538c5f48b --- /dev/null +++ b/src/main/java/de/dennisguse/opentracks/sensors/driver/DriverObserver.java @@ -0,0 +1,31 @@ +package de.dennisguse.opentracks.sensors.driver; + + +public interface DriverObserver { + /** + * The Driver could connect to the sensor. + */ + void onConnected(String sensorAddress, String sensorName); + + /** + * Sensor got temporarily disconnected, Driver should try to re-connect. + */ + void onConnectionLost(); + + /** + * The Driver provided data from the sensor. + */ + void onDataReceived(T value); + + /** + * The Driver was shutdown and no further data will be provided. + */ + //TODO check if really need this, because the driver was told to disconnect and then just signalizes that it finished this task. + void onDisconnected(); + + /** + * The Driver detected that the sensor got deactivated. + * For example, the user deactivated the internal GPS. + */ + default void onSensorDeactivated() {} +} diff --git a/src/main/java/de/dennisguse/opentracks/sensors/driver/GpsInternal.java b/src/main/java/de/dennisguse/opentracks/sensors/driver/GpsInternal.java new file mode 100644 index 000000000..cb9316d56 --- /dev/null +++ b/src/main/java/de/dennisguse/opentracks/sensors/driver/GpsInternal.java @@ -0,0 +1,95 @@ +package de.dennisguse.opentracks.sensors.driver; + +import android.content.Context; +import android.location.Location; +import android.location.LocationManager; +import android.os.Handler; +import android.util.Log; + +import androidx.annotation.NonNull; +import androidx.annotation.VisibleForTesting; +import androidx.core.location.LocationListenerCompat; +import androidx.core.location.LocationManagerCompat; +import androidx.core.location.LocationRequestCompat; + +import de.dennisguse.opentracks.settings.PreferencesUtils; +import de.dennisguse.opentracks.util.PermissionRequester; + +public class GpsInternal implements Driver { + + private static final String TAG = GpsInternal.class.getSimpleName(); + + public static final String LOCATION_PROVIDER = LocationManager.GPS_PROVIDER; + + @VisibleForTesting + public final LocationListenerCompat locationListenerCompat = new LocationListenerCompat() { + @Override + public void onLocationChanged(@NonNull Location location) { + driverObserver.onDataReceived(location); + } + + @Override + public void onProviderEnabled(@NonNull String provider) { + driverObserver.onConnected(null, provider); + } + + @Override + public void onProviderDisabled(@NonNull String provider) { + driverObserver.onSensorDeactivated(); + } + }; + + private final DriverObserver driverObserver; + + @VisibleForTesting + public LocationManager locationManager; + + public GpsInternal(DriverObserver driverObserver) { + this.driverObserver = driverObserver; + } + + @Override + public void connect(Context context, Handler handler, String address) { + locationManager = (LocationManager) context.getSystemService(Context.LOCATION_SERVICE); + + if (!LocationManagerCompat.hasProvider(locationManager, LOCATION_PROVIDER)) { + Log.e(TAG, "Device doesn't have GPS."); + return; + } + + LocationRequestCompat locationRequest = new LocationRequestCompat.Builder(PreferencesUtils.getMinSamplingInterval().toMillis()) + .setQuality(LocationRequestCompat.QUALITY_HIGH_ACCURACY) + .setMaxUpdateDelayMillis(0) + .build(); + + if (PermissionRequester.GPS.hasPermission(context)) { + try { + Log.i(TAG, "Register for location updates " + context); + driverObserver.onConnected(null, null); + LocationManagerCompat.requestLocationUpdates(locationManager, LOCATION_PROVIDER, locationRequest, handler::post, locationListenerCompat); + return; + } catch (SecurityException e) { + Log.e(TAG, "Could not register location listener; permissions not granted.", e); + } + } + + driverObserver.onDisconnected(); + } + + @SuppressWarnings({"MissingPermission"}) + @Override + public void disconnect() { + if (!isConnected()) return; + + LocationManagerCompat.removeUpdates(locationManager, locationListenerCompat); + + locationManager = null; + driverObserver.onDisconnected(); + } + + + @Override + public boolean isConnected() { + return locationManager != null; + } +} diff --git a/src/main/java/de/dennisguse/opentracks/sensors/BluetoothHandlerHeartRate.java b/src/main/java/de/dennisguse/opentracks/sensors/driver/HeartRateBluetooth.java similarity index 68% rename from src/main/java/de/dennisguse/opentracks/sensors/BluetoothHandlerHeartRate.java rename to src/main/java/de/dennisguse/opentracks/sensors/driver/HeartRateBluetooth.java index 7294b3136..15df1f19b 100644 --- a/src/main/java/de/dennisguse/opentracks/sensors/BluetoothHandlerHeartRate.java +++ b/src/main/java/de/dennisguse/opentracks/sensors/driver/HeartRateBluetooth.java @@ -1,4 +1,4 @@ -package de.dennisguse.opentracks.sensors; +package de.dennisguse.opentracks.sensors.driver; import android.bluetooth.BluetoothGattCharacteristic; @@ -9,11 +9,9 @@ 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; +import de.dennisguse.opentracks.sensors.ServiceMeasurementUUID; -public class BluetoothHandlerHeartRate implements SensorHandlerInterface { +public class HeartRateBluetooth implements BluetoothDriver.BluetoothParser { public static final ServiceMeasurementUUID HEARTRATE = new ServiceMeasurementUUID( new UUID(0x180D00001000L, 0x800000805f9b34fbL), @@ -36,17 +34,8 @@ public class BluetoothHandlerHeartRate implements SensorHandlerInterface { } @Override - public AggregatorHeartRate createEmptySensorData(String address, String name) { - return new AggregatorHeartRate(address, name); - } - - @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<>(observer.getNow(), heartRate)); - } + public HeartRate parsePayload(@NonNull ServiceMeasurementUUID serviceMeasurementUUID, String sensorName, BluetoothGattCharacteristic characteristic) { + return parseHeartRate(characteristic); } @VisibleForTesting diff --git a/src/main/java/de/dennisguse/opentracks/sensors/BluetoothHandlerRunningSpeedAndCadence.java b/src/main/java/de/dennisguse/opentracks/sensors/driver/RunningSpeedAndCadenceBluetooth.java similarity index 76% rename from src/main/java/de/dennisguse/opentracks/sensors/BluetoothHandlerRunningSpeedAndCadence.java rename to src/main/java/de/dennisguse/opentracks/sensors/driver/RunningSpeedAndCadenceBluetooth.java index fd147845c..43460f2ec 100644 --- a/src/main/java/de/dennisguse/opentracks/sensors/BluetoothHandlerRunningSpeedAndCadence.java +++ b/src/main/java/de/dennisguse/opentracks/sensors/driver/RunningSpeedAndCadenceBluetooth.java @@ -1,4 +1,4 @@ -package de.dennisguse.opentracks.sensors; +package de.dennisguse.opentracks.sensors.driver; import android.bluetooth.BluetoothGattCharacteristic; @@ -11,11 +11,9 @@ 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; +import de.dennisguse.opentracks.sensors.ServiceMeasurementUUID; -public class BluetoothHandlerRunningSpeedAndCadence implements SensorHandlerInterface { +public class RunningSpeedAndCadenceBluetooth implements BluetoothDriver.BluetoothParser { public static final ServiceMeasurementUUID RUNNING_SPEED_CADENCE = new ServiceMeasurementUUID( @@ -29,14 +27,8 @@ public class BluetoothHandlerRunningSpeedAndCadence implements SensorHandlerInte } @Override - public AggregatorRunning createEmptySensorData(String address, String name) { - return new AggregatorRunning(address, name); - } - - @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<>(observer.getNow(), data)); + public Data parsePayload(ServiceMeasurementUUID serviceMeasurementUUID, String sensorName, BluetoothGattCharacteristic characteristic) { + return parseRunningSpeedAndCadence(sensorName, characteristic); } @VisibleForTesting diff --git a/src/main/java/de/dennisguse/opentracks/sensors/SensorType.java b/src/main/java/de/dennisguse/opentracks/sensors/driver/SensorType.java similarity index 51% rename from src/main/java/de/dennisguse/opentracks/sensors/SensorType.java rename to src/main/java/de/dennisguse/opentracks/sensors/driver/SensorType.java index ad66d7fce..f49575eea 100644 --- a/src/main/java/de/dennisguse/opentracks/sensors/SensorType.java +++ b/src/main/java/de/dennisguse/opentracks/sensors/driver/SensorType.java @@ -1,8 +1,10 @@ -package de.dennisguse.opentracks.sensors; +package de.dennisguse.opentracks.sensors.driver; public enum SensorType { - NONE("NONE"), - INTERNAL("INTERNAL"), + NONE("NONE"), //TODO Use R.string.sensor_type_value_none + INTERNAL("INTERNAL"), //TODO Use R.string.sensor_type_value_none + + //NOTE: preferenceValue of REMOTE should not be used anywhere. REMOTE("*"); private final String preferenceValue; diff --git a/src/main/java/de/dennisguse/opentracks/sensors/driver/TemperatureBluetooth.java b/src/main/java/de/dennisguse/opentracks/sensors/driver/TemperatureBluetooth.java new file mode 100644 index 000000000..5f495848a --- /dev/null +++ b/src/main/java/de/dennisguse/opentracks/sensors/driver/TemperatureBluetooth.java @@ -0,0 +1,42 @@ +package de.dennisguse.opentracks.sensors.driver; + +import static de.dennisguse.opentracks.sensors.driver.BarometerBluetooth.ENVIRONMENTAL_SENSING_SERVICE; + +import android.bluetooth.BluetoothGattCharacteristic; + +import java.util.List; +import java.util.UUID; + +import de.dennisguse.opentracks.data.models.Temperature; +import de.dennisguse.opentracks.sensors.ServiceMeasurementUUID; + +public class TemperatureBluetooth implements BluetoothDriver.BluetoothParser { + public static final ServiceMeasurementUUID TEMPERATURE = new ServiceMeasurementUUID( + ENVIRONMENTAL_SENSING_SERVICE, + new UUID(0x2A6E00001000L, 0x800000805f9b34fbL) + ); + + @Override + public List getServices() { + return List.of(TEMPERATURE); + } + + @Override + public Temperature parsePayload(ServiceMeasurementUUID serviceMeasurementUUID, String sensorName, BluetoothGattCharacteristic characteristic) { + return parseEnvironmentalSensing(characteristic); + } + + /** + * Decoding: org.bluetooth.characteristic.temperature.xml + */ + public static Temperature parseEnvironmentalSensing(BluetoothGattCharacteristic characteristic) { + byte[] raw = characteristic.getValue(); + + if (raw.length < 2) { + return null; + } + + Integer pressure = characteristic.getIntValue(BluetoothGattCharacteristic.FORMAT_SINT16, 0); + return Temperature.ofCentiCelsius(pressure); + } +} diff --git a/src/main/java/de/dennisguse/opentracks/sensors/sensorData/Aggregator.java b/src/main/java/de/dennisguse/opentracks/sensors/sensorData/Aggregator.java index 44f66136c..0cec85cc3 100644 --- a/src/main/java/de/dennisguse/opentracks/sensors/sensorData/Aggregator.java +++ b/src/main/java/de/dennisguse/opentracks/sensors/sensorData/Aggregator.java @@ -1,79 +1,74 @@ package de.dennisguse.opentracks.sensors.sensorData; import androidx.annotation.NonNull; +import androidx.annotation.Nullable; import java.time.Duration; import java.time.Instant; -public abstract class Aggregator { +public abstract class Aggregator { private static final Duration MAX_SENSOR_DATE_SET_AGE = Duration.ofSeconds(5); - protected Raw previous; + @Nullable + protected Raw previous; - protected Output aggregatedValue; + //TODO Make private + protected OutputType output; private final String sensorAddress; private final String sensorName; - Aggregator(String sensorAddress) { - this(sensorAddress, null); - } - Aggregator(String sensorAddress, String sensorName) { this.sensorAddress = sensorAddress; this.sensorName = sensorName; } - public String getSensorNameOrAddress() { + private String getSensorNameOrAddress() { return sensorName != null ? sensorName : sensorAddress; } - public final void add(Raw current) { - computeValue(current); - previous = current; + public final void add(Instant now, InputType current) { + Raw next = new Raw<>(now, current); + computeValue(next); + previous = next; } - protected abstract void computeValue(Raw current); + protected abstract void computeValue(Raw current); /** * @return did we process data from a sensor. * NOTE: for some sensors this may require more than one measurement. */ public boolean hasReceivedData() { - return aggregatedValue != null; + return output != null; } - @Deprecated //TODO Remove: for sensor there is no default value. Make getAggregatedValue() @Nullable - @NonNull - protected abstract Output getNoneValue(); - - @NonNull - public Output getAggregatedValue(Instant now) { - if (!hasReceivedData()) { - return getNoneValue(); - } + @Nullable + protected OutputType getAggregatedValue(Instant now) { if (isOutdated(now)) { - resetImmediate(); + resetOutdated(); } - return aggregatedValue; + return output; } @NonNull - public SensorData getAggregatedValueWithSensorName(Instant now) { + public SensorData getAggregatedValueWithSensorName(Instant now) { return new SensorData<>(getAggregatedValue(now), getSensorNameOrAddress()); } /** * Reset short-term (i.e., non-aggregated) values that were directly derived from sensor data. */ - protected abstract void resetImmediate(); + protected void resetOutdated() { + output = null; + }; /** * Reset long-term (i.e., aggregated) values (more than derived from previous SensorData) like overall distance. */ - public abstract void resetAggregated(); + public void resetAggregated() {}; /** * Is the data recent considering the current time. @@ -97,6 +92,12 @@ public abstract class Aggregator { @NonNull @Override public String toString() { - return "sensorAddress=" + sensorAddress + " data=" + aggregatedValue; + return "sensorAddress=" + sensorAddress + " data=" + output; + } + + protected record Raw( + @NonNull Instant time, + @NonNull T 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 index bd6b1b017..c5b9be679 100644 --- a/src/main/java/de/dennisguse/opentracks/sensors/sensorData/AggregatorBarometer.java +++ b/src/main/java/de/dennisguse/opentracks/sensors/sensorData/AggregatorBarometer.java @@ -1,7 +1,5 @@ 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; @@ -19,31 +17,28 @@ public class AggregatorBarometer extends Aggregator current) { if (previous == null) { lastAcceptedSensorValue = current.value(); - aggregatedValue = getNoneValue(); + output = new AltitudeGainLoss(0f, 0f); return; } PressureSensorUtils.AltitudeChange altitudeChange = PressureSensorUtils.computeChangesWithSmoothing_m(lastAcceptedSensorValue, previous.value(), current.value()); if (altitudeChange != null) { - aggregatedValue = new AltitudeGainLoss(aggregatedValue.gain_m() + altitudeChange.getAltitudeGain_m(), aggregatedValue.loss_m() + altitudeChange.getAltitudeLoss_m()); + if (output == null) { + output = new AltitudeGainLoss(0f, 0f); + } + output = new AltitudeGainLoss(output.gain_m() + altitudeChange.getAltitudeGain_m(), output.loss_m() + altitudeChange.getAltitudeLoss_m()); lastAcceptedSensorValue = altitudeChange.currentSensorValue(); } } @Override - protected void resetImmediate() { + protected void resetOutdated() { } @Override public void resetAggregated() { - aggregatedValue = getNoneValue(); - } - - @NonNull - @Override - protected AltitudeGainLoss getNoneValue() { - return new AltitudeGainLoss(0f, 0f); + output = null; } 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 index dfb4542f3..7cb7c21fa 100644 --- a/src/main/java/de/dennisguse/opentracks/sensors/sensorData/AggregatorCyclingCadence.java +++ b/src/main/java/de/dennisguse/opentracks/sensors/sensorData/AggregatorCyclingCadence.java @@ -2,15 +2,13 @@ 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; +import de.dennisguse.opentracks.sensors.driver.CyclingCadenceBluetooth; -public class AggregatorCyclingCadence extends Aggregator { +public class AggregatorCyclingCadence extends Aggregator { private final String TAG = AggregatorCyclingCadence.class.getSimpleName(); @@ -19,7 +17,7 @@ public class AggregatorCyclingCadence extends Aggregator current) { + protected void computeValue(Raw current) { if (previous == null) { return; } @@ -32,32 +30,18 @@ public class AggregatorCyclingCadence extends Aggregator { +public class AggregatorCyclingDistanceSpeed extends Aggregator { private final String TAG = AggregatorCyclingDistanceSpeed.class.getSimpleName(); @@ -22,7 +20,7 @@ public class AggregatorCyclingDistanceSpeed extends Aggregator current) { + protected void computeValue(Raw current) { if (previous == null) { return; } @@ -35,43 +33,39 @@ public class AggregatorCyclingDistanceSpeed extends Aggregator { +public class AggregatorCyclingPower extends Aggregator { public AggregatorCyclingPower(String name, String address) { super(name, address); } @Override - public void computeValue(Raw current) { - this.aggregatedValue = current.value().power(); - } - - @Override - protected void resetImmediate() { - aggregatedValue = getNoneValue(); - } - - @Override - public void resetAggregated() { - } - - @NonNull - @Override - protected Power getNoneValue() { - return Power.of(0f); + public void computeValue(Raw current) { + this.output = current.value().power(); } } diff --git a/src/main/java/de/dennisguse/opentracks/sensors/sensorData/AggregatorGPS.java b/src/main/java/de/dennisguse/opentracks/sensors/sensorData/AggregatorGPS.java index 62838e11d..f57f3bbfb 100644 --- a/src/main/java/de/dennisguse/opentracks/sensors/sensorData/AggregatorGPS.java +++ b/src/main/java/de/dennisguse/opentracks/sensors/sensorData/AggregatorGPS.java @@ -1,26 +1,22 @@ package de.dennisguse.opentracks.sensors.sensorData; +import android.location.Location; + import androidx.annotation.NonNull; import java.time.Instant; import de.dennisguse.opentracks.data.models.Position; -public class AggregatorGPS extends Aggregator { +public class AggregatorGPS extends Aggregator { - - public AggregatorGPS(String sensorAddress) { - super(sensorAddress); + public AggregatorGPS(String sensorAddress, String sensorName) { + super(sensorAddress, sensorName); } @Override - protected void computeValue(Raw current) { - aggregatedValue = current.value(); - } - - @Override - protected void resetImmediate() { - aggregatedValue = Position.empty(); + protected void computeValue(Raw current) { + output = Position.of(current.value(), current.time()); } @Override @@ -30,19 +26,15 @@ public class AggregatorGPS extends Aggregator { * The data is too large to save it more often than needed (i.e., duplicated values). * TODO: this behavior can be changed if TrackRecordingManager.insertTrackPoint() would strip GPS data if it was already saved. This would simplify TrackPointCreator.createCurrentTrackPoint() */ - aggregatedValue = Position.empty(); - } - - @NonNull - @Override - protected Position getNoneValue() { - return Position.empty(); + output = null; } @NonNull @Override public Position getAggregatedValue(Instant now) { - return super.getAggregatedValue(now) - .with(now); + Position value = super.getAggregatedValue(now); + if (value == null) value = Position.empty(); + + return value.with(now); } } diff --git a/src/main/java/de/dennisguse/opentracks/sensors/sensorData/AggregatorHeartRate.java b/src/main/java/de/dennisguse/opentracks/sensors/sensorData/AggregatorHeartRate.java index 22c7bea24..b7bc6ad9d 100644 --- a/src/main/java/de/dennisguse/opentracks/sensors/sensorData/AggregatorHeartRate.java +++ b/src/main/java/de/dennisguse/opentracks/sensors/sensorData/AggregatorHeartRate.java @@ -1,7 +1,5 @@ package de.dennisguse.opentracks.sensors.sensorData; -import androidx.annotation.NonNull; - import de.dennisguse.opentracks.data.models.HeartRate; public class AggregatorHeartRate extends Aggregator { @@ -13,22 +11,7 @@ public class AggregatorHeartRate extends Aggregator { @Override protected void computeValue(Raw current) { if (current.value().isValid()) { - this.aggregatedValue = current.value(); + this.output = current.value(); } } - - @Override - protected void resetImmediate() { - aggregatedValue = getNoneValue(); - } - - @Override - public void resetAggregated() { - } - - @NonNull - @Override - protected HeartRate getNoneValue() { - return HeartRate.of(0); - } } diff --git a/src/main/java/de/dennisguse/opentracks/sensors/sensorData/AggregatorRunning.java b/src/main/java/de/dennisguse/opentracks/sensors/sensorData/AggregatorRunning.java index 2c415e59b..a5c7bc34e 100644 --- a/src/main/java/de/dennisguse/opentracks/sensors/sensorData/AggregatorRunning.java +++ b/src/main/java/de/dennisguse/opentracks/sensors/sensorData/AggregatorRunning.java @@ -5,12 +5,12 @@ 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; +import de.dennisguse.opentracks.sensors.driver.RunningSpeedAndCadenceBluetooth; /** * Provides cadence in rpm and speed in milliseconds from Bluetooth LE Running Speed and Cadence sensors. */ -public final class AggregatorRunning extends Aggregator { +public final class AggregatorRunning extends Aggregator { private static final String TAG = AggregatorRunning.class.getSimpleName(); @@ -19,7 +19,7 @@ public final class AggregatorRunning extends Aggregator current) { + public void computeValue(Raw current) { if (previous == null) { return; } @@ -27,33 +27,31 @@ public final class AggregatorRunning extends Aggregator } protected void computeValue(Raw current) { - this.aggregatedValue = current.value(); + this.output = current.value(); } - @NonNull - protected Temperature getNoneValue() { - return Temperature.ofCentiCelsius(0); - } - - protected void resetImmediate() { - aggregatedValue = getNoneValue(); - } - - public void resetAggregated() {}; - @Override protected Duration getMaxSensorAge() { return Duration.ofMinutes(1); diff --git a/src/main/java/de/dennisguse/opentracks/sensors/sensorData/Raw.java b/src/main/java/de/dennisguse/opentracks/sensors/sensorData/Raw.java deleted file mode 100644 index a946dca29..000000000 --- a/src/main/java/de/dennisguse/opentracks/sensors/sensorData/Raw.java +++ /dev/null @@ -1,22 +0,0 @@ -package de.dennisguse.opentracks.sensors.sensorData; - - -import androidx.annotation.NonNull; -import androidx.annotation.VisibleForTesting; - -import java.time.Clock; -import java.time.Instant; - -public record Raw( - @NonNull Instant time, - @NonNull T value -) { - public Raw(@NonNull Clock clock, @NonNull T value) { - this(clock.instant(), value); - } - - @VisibleForTesting - public Raw(@NonNull String time, @NonNull T value) { - this(Instant.parse(time), value); - } -} diff --git a/src/main/java/de/dennisguse/opentracks/sensors/sensorData/SensorData.java b/src/main/java/de/dennisguse/opentracks/sensors/sensorData/SensorData.java index 104bf718a..7758b02f1 100644 --- a/src/main/java/de/dennisguse/opentracks/sensors/sensorData/SensorData.java +++ b/src/main/java/de/dennisguse/opentracks/sensors/sensorData/SensorData.java @@ -1,7 +1,11 @@ package de.dennisguse.opentracks.sensors.sensorData; +import androidx.annotation.Nullable; + public record SensorData( + + @Nullable T data, String sensorNameOrAddress diff --git a/src/main/java/de/dennisguse/opentracks/sensors/sensorData/SensorDataAggregator.java b/src/main/java/de/dennisguse/opentracks/sensors/sensorData/SensorDataAggregator.java deleted file mode 100644 index 61990e1d6..000000000 --- a/src/main/java/de/dennisguse/opentracks/sensors/sensorData/SensorDataAggregator.java +++ /dev/null @@ -1,229 +0,0 @@ -package de.dennisguse.opentracks.sensors.sensorData; - -import android.util.Log; - -import androidx.annotation.NonNull; -import androidx.annotation.Nullable; -import androidx.annotation.VisibleForTesting; - -import java.time.Instant; - -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.Position; -import de.dennisguse.opentracks.data.models.Speed; -import de.dennisguse.opentracks.data.models.Temperature; -import de.dennisguse.opentracks.sensors.BluetoothHandlerCyclingCadence; -import de.dennisguse.opentracks.sensors.BluetoothHandlerCyclingDistanceSpeed; -import de.dennisguse.opentracks.sensors.BluetoothHandlerCyclingPower; -import de.dennisguse.opentracks.sensors.BluetoothHandlerRunningSpeedAndCadence; -import de.dennisguse.opentracks.settings.PreferencesUtils; - -public class SensorDataAggregator { - - private static final String TAG = SensorDataAggregator.class.getSimpleName(); - - @VisibleForTesting - public AggregatorHeartRate heartRate; - - @VisibleForTesting - public AggregatorTemperature temperature; - - @VisibleForTesting - public AggregatorCyclingCadence cyclingCadence; - - @VisibleForTesting - public AggregatorCyclingDistanceSpeed cyclingDistanceSpeed; - - @VisibleForTesting - public AggregatorCyclingPower cyclingPower; - - @VisibleForTesting - public AggregatorRunning runningDistanceSpeedCadence; - - @VisibleForTesting - public AggregatorBarometer barometer; - - public AggregatorGPS gps; - - @VisibleForTesting - public SensorData getHeartRate(Instant now) { - if (heartRate != null) { - SensorData value = heartRate.getAggregatedValueWithSensorName(now); - if (value.data().isValid()) return value; - } - - return null; - } - - private SensorData getCadence(Instant now) { - if (cyclingCadence != null) { - return cyclingCadence.getAggregatedValueWithSensorName(now); - } - - if (runningDistanceSpeedCadence != null && runningDistanceSpeedCadence.hasReceivedData() && runningDistanceSpeedCadence.getAggregatedValue(now).cadence() != null) { - return new SensorData<>(runningDistanceSpeedCadence.aggregatedValue.cadence(), runningDistanceSpeedCadence.getSensorNameOrAddress()); - } - - return null; - } - - private SensorData getDistance(Instant now) { - if (cyclingDistanceSpeed != null && cyclingDistanceSpeed.hasReceivedData()) { - return new SensorData<>(cyclingDistanceSpeed.getAggregatedValue(now).distanceOverall(), cyclingDistanceSpeed.getSensorNameOrAddress()); - } - - if (runningDistanceSpeedCadence != null && runningDistanceSpeedCadence.hasReceivedData()) { - return new SensorData<>(runningDistanceSpeedCadence.getAggregatedValue(now).distance(), runningDistanceSpeedCadence.getSensorNameOrAddress()); - - } - - return null; - } - - //TOOD simplify? - private SensorData getSpeed(Instant now) { - - if (cyclingDistanceSpeed != null && cyclingDistanceSpeed.hasReceivedData() && cyclingDistanceSpeed.getAggregatedValue(now).speed() != null) { - return new SensorData<>(cyclingDistanceSpeed.getAggregatedValue(now).speed(), cyclingDistanceSpeed.getSensorNameOrAddress()); - } - - if (runningDistanceSpeedCadence != null && runningDistanceSpeedCadence.hasReceivedData() && runningDistanceSpeedCadence.getAggregatedValue(now).speed() != null) { - return new SensorData<>(runningDistanceSpeedCadence.aggregatedValue.speed(), runningDistanceSpeedCadence.getSensorNameOrAddress()); - } - - return null; - } - - public void add(@NonNull Aggregator data) { - set(data, data); - } - - //TODO Refactor generic "fun" here: go explicit. - public void update(@NonNull Raw data) { - Object value = data.value(); - - if (value instanceof HeartRate) { - this.heartRate.add((Raw) data); - return; - } - - if (value instanceof Temperature) { - this.temperature.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 BluetoothHandlerCyclingPower.Data) { - this.cyclingPower.add((Raw) data); - return; - } - if (value instanceof AtmosphericPressure) { - this.barometer.add((Raw) data); - return; - } - if (value instanceof Position) { - this.gps.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.temperature = null; - this.cyclingCadence = null; - this.cyclingDistanceSpeed = null; - this.cyclingPower = null; - this.runningDistanceSpeedCadence = null; - this.barometer = null; - this.gps = null; - } - - public SensorDataSet getSensorData(Instant now) { - return new SensorDataSet( - gps != null && gps.hasReceivedData() ? gps.getAggregatedValueWithSensorName(now) : new SensorData<>(Position.of(now), ""), - getSpeed(now), - getDistance(now), - getHeartRate(now), - temperature != null && temperature.hasReceivedData() ? temperature.getAggregatedValueWithSensorName(now) : null, - getCadence(now), - cyclingPower != null && cyclingPower.hasReceivedData() ? cyclingPower.getAggregatedValueWithSensorName(now) : null, - barometer != null && barometer.hasReceivedData() ? barometer.getAggregatedValueWithSensorName(now) : null - ); - } - - public void reset() { - Log.i(TAG, "Resetting data"); - - if (heartRate != null) heartRate.resetAggregated(); - if (temperature != null) temperature.resetAggregated(); - if (cyclingCadence != null) cyclingCadence.resetAggregated(); - if (cyclingDistanceSpeed != null) cyclingDistanceSpeed.resetAggregated(); - if (cyclingPower != null) cyclingPower.resetAggregated(); - if (runningDistanceSpeedCadence != null) runningDistanceSpeedCadence.resetAggregated(); - if (barometer != null) barometer.resetAggregated(); - if (gps != null) gps.resetAggregated(); - } - - //TODO Refactor generic "fun" here: go explicit. - private void set(@NonNull Aggregator type, @Nullable Aggregator sensorData) { - Log.i(TAG, "Setting aggregator " + type.getClass().getCanonicalName() + " to " + sensorData); - - if (type instanceof AggregatorHeartRate) { - heartRate = (AggregatorHeartRate) sensorData; - return; - } - if (type instanceof AggregatorTemperature) { - temperature = (AggregatorTemperature) sensorData; - return; - } - if (type instanceof AggregatorCyclingCadence) { - cyclingCadence = (AggregatorCyclingCadence) sensorData; - return; - } - if (type instanceof AggregatorCyclingDistanceSpeed) { - cyclingDistanceSpeed = (AggregatorCyclingDistanceSpeed) sensorData; - return; - } - if (type instanceof AggregatorCyclingPower) { - cyclingPower = (AggregatorCyclingPower) sensorData; - return; - } - if (type instanceof AggregatorRunning) { - runningDistanceSpeedCadence = (AggregatorRunning) sensorData; - return; - } - if (type instanceof AggregatorBarometer) { - barometer = (AggregatorBarometer) sensorData; - return; - } - if (type instanceof AggregatorGPS) { - gps = (AggregatorGPS) sensorData; - return; - } - - throw new UnsupportedOperationException(type.getClass().getCanonicalName()); - } -} diff --git a/src/main/java/de/dennisguse/opentracks/sensors/sensorData/SensorHandlerInterface.java b/src/main/java/de/dennisguse/opentracks/sensors/sensorData/SensorHandlerInterface.java deleted file mode 100644 index 3406c907f..000000000 --- a/src/main/java/de/dennisguse/opentracks/sensors/sensorData/SensorHandlerInterface.java +++ /dev/null @@ -1,17 +0,0 @@ -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, String name); - - 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 ff1e6191a..59ff7a970 100644 --- a/src/main/java/de/dennisguse/opentracks/services/handlers/TrackPointCreator.java +++ b/src/main/java/de/dennisguse/opentracks/services/handlers/TrackPointCreator.java @@ -56,7 +56,7 @@ public class TrackPointCreator implements SharedPreferences.OnSharedPreferenceCh } public void stop() { - sensorManager.stop(context); + sensorManager.stop(); this.context = null; } @@ -73,7 +73,7 @@ public class TrackPointCreator implements SharedPreferences.OnSharedPreferenceCh } public synchronized TrackPoint createSegmentStartManual() { - return TrackPoint.createSegmentStartManualWithTime(createNow()); + return TrackPoint.createSegmentStartManualWithTime(getNow()); } public synchronized TrackPoint createSegmentEnd() { @@ -89,7 +89,7 @@ public class TrackPointCreator implements SharedPreferences.OnSharedPreferenceCh } public Pair createCurrentTrackPoint(@Nullable TrackPoint lastTrackPointUISpeed, @Nullable TrackPoint lastTrackPointUIAltitude, @Nullable TrackPoint lastStoredTrackPointWithLocation) { - Instant now = createNow(); + Instant now = getNow(); SensorDataSet sensorDataSet = sensorManager.getSensorDataSet(now); Position position = sensorDataSet.position().data(); @@ -123,7 +123,7 @@ public class TrackPointCreator implements SharedPreferences.OnSharedPreferenceCh } private TrackPoint createTrackPoint(TrackPoint.Type type) { - Instant now = createNow(); + Instant now = getNow(); SensorDataSet sensorDataSet = sensorManager.getSensorDataSet(now); @@ -146,7 +146,7 @@ public class TrackPointCreator implements SharedPreferences.OnSharedPreferenceCh ); } - public Instant createNow() { + public Instant getNow() { return Instant.now(clock); } diff --git a/src/main/java/de/dennisguse/opentracks/settings/PreferencesUtils.java b/src/main/java/de/dennisguse/opentracks/settings/PreferencesUtils.java index 2ed5d6ab9..8412b2266 100644 --- a/src/main/java/de/dennisguse/opentracks/settings/PreferencesUtils.java +++ b/src/main/java/de/dennisguse/opentracks/settings/PreferencesUtils.java @@ -48,7 +48,7 @@ import de.dennisguse.opentracks.data.models.HeartRateZones; import de.dennisguse.opentracks.data.models.Track; import de.dennisguse.opentracks.io.file.TrackFileFormat; import de.dennisguse.opentracks.io.file.TrackFilenameGenerator; -import de.dennisguse.opentracks.sensors.SensorType; +import de.dennisguse.opentracks.sensors.driver.SensorType; import de.dennisguse.opentracks.ui.customRecordingLayout.DataField; import de.dennisguse.opentracks.ui.customRecordingLayout.RecordingLayout; import de.dennisguse.opentracks.ui.customRecordingLayout.RecordingLayoutIO; @@ -253,10 +253,6 @@ public class PreferencesUtils { return isReportSpeed(track.activityTypeLocalized()); } - private static String getBluetoothSensorAddressNone() { - return SensorType.NONE.getPreferenceValue(); - } - public static SensorType getSensorType(String address) { return Arrays.stream(SensorType.values()) .filter(it -> it.getPreferenceValue().equals(address)) @@ -264,39 +260,11 @@ public class PreferencesUtils { .orElse(SensorType.REMOTE); } - public static String getBarometerSensorAddress() { - return getString(R.string.settings_sensor_bluetooth_pressure_key, getBluetoothSensorAddressNone()); - } - - public static String getBluetoothHeartRateSensorAddress() { - return getString(R.string.settings_sensor_bluetooth_heart_rate_key, getBluetoothSensorAddressNone()); - } - - public static String getBluetoothTemperatureSensorAddress() { - return getString(R.string.settings_sensor_bluetooth_temperature_key, getBluetoothSensorAddressNone()); - } - - public static String getBluetoothCyclingCadenceSensorAddress() { - return getString(R.string.settings_sensor_bluetooth_cycling_cadence_key, getBluetoothSensorAddressNone()); - } - - public static String getBluetoothCyclingSpeedSensorAddress() { - return getString(R.string.settings_sensor_bluetooth_cycling_speed_key, getBluetoothSensorAddressNone()); - } - public static Distance getWheelCircumference() { final int DEFAULT = Integer.parseInt(resources.getString(R.string.settings_sensor_bluetooth_cycling_speed_wheel_circumference_default)); return Distance.ofMM(getInt(R.string.settings_sensor_bluetooth_cycling_speed_wheel_circumference_key, DEFAULT)); } - public static String getBluetoothCyclingPowerSensorAddress() { - return getString(R.string.settings_sensor_bluetooth_cycling_power_key, getBluetoothSensorAddressNone()); - } - - public static String getBluetoothRunningSpeedAndCadenceAddress() { - return getString(R.string.settings_sensor_bluetooth_running_speed_and_cadence_key, getBluetoothSensorAddressNone()); - } - public static boolean getBluetoothFilterEnabled() { final boolean DEFAULT = resources.getBoolean(R.bool.settings_sensor_bluetooth_service_filter_enabled_default); return getBoolean(R.string.settings_sensor_bluetooth_service_filter_enabled_key, DEFAULT); @@ -579,6 +547,7 @@ public class PreferencesUtils { return entries; } + //TODO Rename to getGpsMinSamplingInterval public static Duration getMinSamplingInterval() { final Duration MIN_SAMPLING_INTERVAL = getMinSamplingIntervalDefault(); return Duration.ofSeconds(getInt(R.string.min_sampling_interval_key, (int) MIN_SAMPLING_INTERVAL.getSeconds())); 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 ce0d9a2f7..a078bdb34 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.BluetoothHandlerCyclingCadence; +import de.dennisguse.opentracks.sensors.driver.CyclingCadenceBluetooth; public class BluetoothLeCyclingCadencePreference extends BluetoothLeSensorPreference { @@ -27,6 +27,6 @@ public class BluetoothLeCyclingCadencePreference extends BluetoothLeSensorPrefer @Override public PreferenceDialogFragmentCompat createInstance() { - return BluetoothLeSensorPreferenceDialog.newInstance(getKey(), BluetoothHandlerCyclingCadence.CYCLING_CADENCE); + return BluetoothLeSensorPreferenceDialog.newInstance(getKey(), CyclingCadenceBluetooth.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 ff99809cd..8a2ac7294 100644 --- a/src/main/java/de/dennisguse/opentracks/settings/bluetooth/BluetoothLeCyclingPowerPreference.java +++ b/src/main/java/de/dennisguse/opentracks/settings/bluetooth/BluetoothLeCyclingPowerPreference.java @@ -7,7 +7,7 @@ import androidx.preference.PreferenceDialogFragmentCompat; import java.util.List; -import de.dennisguse.opentracks.sensors.BluetoothHandlerCyclingPower; +import de.dennisguse.opentracks.sensors.driver.CyclingPowerBluetooth; public class BluetoothLeCyclingPowerPreference extends BluetoothLeSensorPreference { @@ -29,6 +29,6 @@ public class BluetoothLeCyclingPowerPreference extends BluetoothLeSensorPreferen @Override public PreferenceDialogFragmentCompat createInstance() { - return BluetoothLeSensorPreference.BluetoothLeSensorPreferenceDialog.newInstance(getKey(), List.of(BluetoothHandlerCyclingPower.CYCLING_POWER)); + return BluetoothLeSensorPreference.BluetoothLeSensorPreferenceDialog.newInstance(getKey(), List.of(CyclingPowerBluetooth.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 bddce5a21..63ffcc644 100644 --- a/src/main/java/de/dennisguse/opentracks/settings/bluetooth/BluetoothLeCyclingSpeedPreference.java +++ b/src/main/java/de/dennisguse/opentracks/settings/bluetooth/BluetoothLeCyclingSpeedPreference.java @@ -7,7 +7,7 @@ import androidx.preference.PreferenceDialogFragmentCompat; import java.util.List; -import de.dennisguse.opentracks.sensors.BluetoothHandlerCyclingDistanceSpeed; +import de.dennisguse.opentracks.sensors.driver.CyclingDistanceSpeedBluetooth; public class BluetoothLeCyclingSpeedPreference extends BluetoothLeSensorPreference { @@ -29,6 +29,6 @@ public class BluetoothLeCyclingSpeedPreference extends BluetoothLeSensorPreferen @Override public PreferenceDialogFragmentCompat createInstance() { - return BluetoothLeSensorPreference.BluetoothLeSensorPreferenceDialog.newInstance(getKey(), List.of(BluetoothHandlerCyclingDistanceSpeed.CYCLING_SPEED_CADENCE)); + return BluetoothLeSensorPreference.BluetoothLeSensorPreferenceDialog.newInstance(getKey(), List.of(CyclingDistanceSpeedBluetooth.CYCLING_SPEED_CADENCE)); } } diff --git a/src/main/java/de/dennisguse/opentracks/settings/bluetooth/BluetoothLeGpsPreference.java b/src/main/java/de/dennisguse/opentracks/settings/bluetooth/BluetoothLeGpsPreference.java new file mode 100644 index 000000000..56dc14f8d --- /dev/null +++ b/src/main/java/de/dennisguse/opentracks/settings/bluetooth/BluetoothLeGpsPreference.java @@ -0,0 +1,41 @@ +package de.dennisguse.opentracks.settings.bluetooth; + +import android.content.Context; +import android.location.LocationManager; +import android.util.AttributeSet; + +import androidx.core.location.LocationManagerCompat; +import androidx.preference.PreferenceDialogFragmentCompat; + +import java.util.List; + +import de.dennisguse.opentracks.sensors.driver.GpsInternal; + +public class BluetoothLeGpsPreference extends BluetoothLeSensorPreference { + + public BluetoothLeGpsPreference(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) { + super(context, attrs, defStyleAttr, defStyleRes); + } + + public BluetoothLeGpsPreference(Context context, AttributeSet attrs, int defStyleAttr) { + super(context, attrs, defStyleAttr); + } + + public BluetoothLeGpsPreference(Context context, AttributeSet attrs) { + super(context, attrs); + } + + public BluetoothLeGpsPreference(Context context) { + super(context); + } + + @Override + public PreferenceDialogFragmentCompat createInstance() { + boolean hasInternalSensor = LocationManagerCompat.hasProvider((LocationManager) getContext().getSystemService(Context.LOCATION_SERVICE), GpsInternal.LOCATION_PROVIDER); + + return BluetoothLeSensorPreferenceDialog + .newInstance(getKey(), List.of(), hasInternalSensor); + } + + +} \ No newline at end of file diff --git a/src/main/java/de/dennisguse/opentracks/settings/bluetooth/BluetoothLeHeartRatePreference.java b/src/main/java/de/dennisguse/opentracks/settings/bluetooth/BluetoothLeHeartRatePreference.java index 97a447ddd..5ce0f6243 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.BluetoothHandlerHeartRate; +import de.dennisguse.opentracks.sensors.driver.HeartRateBluetooth; public class BluetoothLeHeartRatePreference extends BluetoothLeSensorPreference { @@ -27,6 +27,6 @@ public class BluetoothLeHeartRatePreference extends BluetoothLeSensorPreference @Override public PreferenceDialogFragmentCompat createInstance() { - return BluetoothLeSensorPreference.BluetoothLeSensorPreferenceDialog.newInstance(getKey(), BluetoothHandlerHeartRate.HEART_RATE_SUPPORTING_DEVICES); + return BluetoothLeSensorPreference.BluetoothLeSensorPreferenceDialog.newInstance(getKey(), HeartRateBluetooth.HEART_RATE_SUPPORTING_DEVICES); } } \ No newline at end of file diff --git a/src/main/java/de/dennisguse/opentracks/settings/bluetooth/BluetoothLePressurePreference.java b/src/main/java/de/dennisguse/opentracks/settings/bluetooth/BluetoothLePressurePreference.java index ee3e495a9..165600826 100644 --- a/src/main/java/de/dennisguse/opentracks/settings/bluetooth/BluetoothLePressurePreference.java +++ b/src/main/java/de/dennisguse/opentracks/settings/bluetooth/BluetoothLePressurePreference.java @@ -9,7 +9,7 @@ import androidx.preference.PreferenceDialogFragmentCompat; import java.util.List; -import de.dennisguse.opentracks.sensors.BluetoothHandlerBarometricPressure; +import de.dennisguse.opentracks.sensors.driver.BarometerBluetooth; public class BluetoothLePressurePreference extends BluetoothLeSensorPreference { @@ -36,6 +36,6 @@ public class BluetoothLePressurePreference extends BluetoothLeSensorPreference { boolean hasInternalSensor = pressureSensor != null; return BluetoothLeSensorPreference.BluetoothLeSensorPreferenceDialog - .newInstance(getKey(), List.of(BluetoothHandlerBarometricPressure.BAROMETRIC_PRESSURE), hasInternalSensor); + .newInstance(getKey(), List.of(BarometerBluetooth.BAROMETRIC_PRESSURE), hasInternalSensor); } } \ 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 470b8d636..426ad1972 100644 --- a/src/main/java/de/dennisguse/opentracks/settings/bluetooth/BluetoothLeRunningSpeedAndCadencePreference.java +++ b/src/main/java/de/dennisguse/opentracks/settings/bluetooth/BluetoothLeRunningSpeedAndCadencePreference.java @@ -7,7 +7,7 @@ import androidx.preference.PreferenceDialogFragmentCompat; import java.util.List; -import de.dennisguse.opentracks.sensors.BluetoothHandlerRunningSpeedAndCadence; +import de.dennisguse.opentracks.sensors.driver.RunningSpeedAndCadenceBluetooth; public class BluetoothLeRunningSpeedAndCadencePreference extends BluetoothLeSensorPreference { @@ -29,6 +29,6 @@ public class BluetoothLeRunningSpeedAndCadencePreference extends BluetoothLeSens @Override public PreferenceDialogFragmentCompat createInstance() { - return BluetoothLeSensorPreference.BluetoothLeSensorPreferenceDialog.newInstance(getKey(), List.of(BluetoothHandlerRunningSpeedAndCadence.RUNNING_SPEED_CADENCE)); + return BluetoothLeSensorPreference.BluetoothLeSensorPreferenceDialog.newInstance(getKey(), List.of(RunningSpeedAndCadenceBluetooth.RUNNING_SPEED_CADENCE)); } } diff --git a/src/main/java/de/dennisguse/opentracks/settings/bluetooth/BluetoothLeSensorPreference.java b/src/main/java/de/dennisguse/opentracks/settings/bluetooth/BluetoothLeSensorPreference.java index 531fb6f0d..7e90f94d9 100644 --- a/src/main/java/de/dennisguse/opentracks/settings/bluetooth/BluetoothLeSensorPreference.java +++ b/src/main/java/de/dennisguse/opentracks/settings/bluetooth/BluetoothLeSensorPreference.java @@ -10,6 +10,7 @@ import android.bluetooth.le.ScanResult; import android.bluetooth.le.ScanSettings; import android.content.Context; import android.content.DialogInterface; +import android.content.res.TypedArray; import android.os.Bundle; import android.os.ParcelUuid; import android.text.TextUtils; @@ -18,6 +19,7 @@ import android.util.Log; import android.widget.Toast; import androidx.annotation.NonNull; +import androidx.annotation.Nullable; import androidx.appcompat.app.AlertDialog; import androidx.preference.DialogPreference; import androidx.preference.PreferenceDialogFragmentCompat; @@ -28,8 +30,8 @@ import java.util.List; import de.dennisguse.opentracks.R; import de.dennisguse.opentracks.sensors.BluetoothUtils; -import de.dennisguse.opentracks.sensors.SensorType; import de.dennisguse.opentracks.sensors.ServiceMeasurementUUID; +import de.dennisguse.opentracks.sensors.driver.SensorType; import de.dennisguse.opentracks.settings.PreferencesUtils; import de.dennisguse.opentracks.util.PermissionRequester; @@ -80,6 +82,12 @@ public abstract class BluetoothLeSensorPreference extends DialogPreference { } } + @Nullable + @Override + protected Object onGetDefaultValue(@NonNull TypedArray a, int index) { + return a.getString(index); + } + @Override protected void onSetInitialValue(Object defaultValue) { setValue(getPersistedString((String) defaultValue)); diff --git a/src/main/java/de/dennisguse/opentracks/settings/bluetooth/BluetoothLeTemperaturePreference.java b/src/main/java/de/dennisguse/opentracks/settings/bluetooth/BluetoothLeTemperaturePreference.java index 6cca492ad..b79caf2c4 100644 --- a/src/main/java/de/dennisguse/opentracks/settings/bluetooth/BluetoothLeTemperaturePreference.java +++ b/src/main/java/de/dennisguse/opentracks/settings/bluetooth/BluetoothLeTemperaturePreference.java @@ -7,7 +7,7 @@ import androidx.preference.PreferenceDialogFragmentCompat; import java.util.List; -import de.dennisguse.opentracks.sensors.BluetoothHandlerTemperature; +import de.dennisguse.opentracks.sensors.driver.TemperatureBluetooth; public class BluetoothLeTemperaturePreference extends BluetoothLeSensorPreference { @@ -30,6 +30,6 @@ public class BluetoothLeTemperaturePreference extends BluetoothLeSensorPreferenc @Override public PreferenceDialogFragmentCompat createInstance() { return BluetoothLeSensorPreferenceDialog - .newInstance(getKey(), List.of(BluetoothHandlerTemperature.TEMPERATURE)); + .newInstance(getKey(), List.of(TemperatureBluetooth.TEMPERATURE)); } } \ No newline at end of file diff --git a/src/main/res/values/settings.xml b/src/main/res/values/settings.xml index 91f08907a..769c78495 100644 --- a/src/main/res/values/settings.xml +++ b/src/main/res/values/settings.xml @@ -26,6 +26,7 @@ bluetoothCyclingPowerSensor bluetoothRunningSpeedAndCadenceSensor bluetoothPressureSensor + bluetoothGPSSensor NONE INTERNAL diff --git a/src/main/res/xml/settings_sensors.xml b/src/main/res/xml/settings_sensors.xml index 582e1bcfe..ac18e1879 100644 --- a/src/main/res/xml/settings_sensors.xml +++ b/src/main/res/xml/settings_sensors.xml @@ -5,12 +5,15 @@ android:title="@string/settings_sensors_title"> + - + android:title="@string/settings_sensor_bluetooth_service_filter_title" /> \ No newline at end of file