From 0395394e26603423fdbcfbaca8a2a3b3989209c4 Mon Sep 17 00:00:00 2001 From: denta58 Date: Sun, 4 Dec 2022 21:01:57 +0100 Subject: [PATCH 1/7] Bluetooth pressure data parse --- .../opentracks/sensors/BluetoothUtilsTest.java | 14 ++++++++++++++ .../opentracks/sensors/BluetoothUtils.java | 16 ++++++++++++++++ 2 files changed, 30 insertions(+) diff --git a/src/androidTest/java/de/dennisguse/opentracks/sensors/BluetoothUtilsTest.java b/src/androidTest/java/de/dennisguse/opentracks/sensors/BluetoothUtilsTest.java index 6f37ea39a..8654a4658 100644 --- a/src/androidTest/java/de/dennisguse/opentracks/sensors/BluetoothUtilsTest.java +++ b/src/androidTest/java/de/dennisguse/opentracks/sensors/BluetoothUtilsTest.java @@ -43,6 +43,20 @@ public class BluetoothUtilsTest { assertEquals(HeartRate.of(257), heartRate); } + @Test + public void parsePressure_Pa() { + // given + BluetoothGattCharacteristic characteristic = new BluetoothGattCharacteristic(BluetoothUtils.PRESSURE.getServiceUUID(), 0, 0); + characteristic.setValue(new byte[]{(byte) 0xB2, (byte) 0x48, (byte) 0x0F, (byte) 0x00}); + + + // when + int pressure = BluetoothUtils.parsePressure(characteristic); + + // then + assertEquals(1001650, pressure); + } + @Test public void parseCyclingSpeedCadence_crankOnly() { BluetoothGattCharacteristic characteristic = new BluetoothGattCharacteristic(BluetoothUtils.CYCLING_SPEED_CADENCE.getServiceUUID(), 0, 0); diff --git a/src/main/java/de/dennisguse/opentracks/sensors/BluetoothUtils.java b/src/main/java/de/dennisguse/opentracks/sensors/BluetoothUtils.java index a5c11b382..23e099d81 100644 --- a/src/main/java/de/dennisguse/opentracks/sensors/BluetoothUtils.java +++ b/src/main/java/de/dennisguse/opentracks/sensors/BluetoothUtils.java @@ -69,6 +69,11 @@ public class BluetoothUtils { )) ); + public static final ServiceMeasurementUUID PRESSURE = new ServiceMeasurementUUID( + new UUID(0x181A00001000L, 0x800000805f9b34fbL), + new UUID(0x2A6D00001000L, 0x800000805f9b34fbL) + ); + public static final ServiceMeasurementUUID CYCLING_POWER = new ServiceMeasurementUUID( new UUID(0x181800001000L, 0x800000805f9b34fbL), new UUID(0x2A6300001000L, 0x800000805f9b34fbL) @@ -137,6 +142,17 @@ public class BluetoothUtils { return null; } + public static Integer parsePressure(BluetoothGattCharacteristic characteristic) { + byte[] raw = characteristic.getValue(); + + if (raw.length < 4) { + return null; + } + + Float fPressure = characteristic.getFloatValue(BluetoothGattCharacteristic.FORMAT_FLOAT, 0); + return Math.round(fPressure); + } + public static SensorDataCyclingPower.Data parseCyclingPower(String address, String sensorName, BluetoothGattCharacteristic characteristic) { // DOCUMENTATION https://www.bluetooth.com/wp-content/uploads/Sitecore-Media-Library/Gatt/Xml/Characteristics/org.bluetooth.characteristic.cycling_power_measurement.xml int valueLength = characteristic.getValue().length; From 4906d3f942c6bc263feec6c3db6b01340667bbb5 Mon Sep 17 00:00:00 2001 From: Dennis Guse Date: Tue, 23 May 2023 10:20:57 +0200 Subject: [PATCH 2/7] Introduced AtmosphericPressure. Part of #1424. --- .../sensors/AltitudeSumManagerTest.java | 10 ++-- .../sensors/BluetoothUtilsTest.java | 10 ++-- .../sensors/PressureSensorUtilsTest.java | 16 ++++--- .../data/models/AtmosphericPressure.java | 48 +++++++++++++++++++ .../sensors/AltitudeSumManager.java | 23 ++++----- .../opentracks/sensors/BluetoothUtils.java | 12 +++-- .../sensors/PressureSensorUtils.java | 30 ++++++------ 7 files changed, 103 insertions(+), 46 deletions(-) create mode 100644 src/main/java/de/dennisguse/opentracks/data/models/AtmosphericPressure.java diff --git a/src/androidTest/java/de/dennisguse/opentracks/sensors/AltitudeSumManagerTest.java b/src/androidTest/java/de/dennisguse/opentracks/sensors/AltitudeSumManagerTest.java index c194f7db7..fb8160c52 100644 --- a/src/androidTest/java/de/dennisguse/opentracks/sensors/AltitudeSumManagerTest.java +++ b/src/androidTest/java/de/dennisguse/opentracks/sensors/AltitudeSumManagerTest.java @@ -4,6 +4,8 @@ import org.junit.Assert; import org.junit.Before; import org.junit.Test; +import de.dennisguse.opentracks.data.models.AtmosphericPressure; + /** * NOTE: Test data is completely artificial. */ @@ -13,7 +15,7 @@ public class AltitudeSumManagerTest { private static void addSensorValue(AltitudeSumManager altitudeSumManager, float[] values) { for (float f : values) { - altitudeSumManager.onSensorValueChanged(f); + altitudeSumManager.onSensorValueChanged(AtmosphericPressure.ofHPA(f)); } } @@ -28,11 +30,11 @@ public class AltitudeSumManagerTest { subject.setConnected(true); // then - addSensorValue(subject, new float[]{1015f, 1015.01f, 1015.02f, 1015.03f, 1015.04f, 1015.05f, 1015.06f, 1015.07f, 1015.08f, 1015.09f, 1015.10f, 1015.11f, 1015.12f, 1015.13f, 1015.14f, 1015.15f}); + addSensorValue(subject, new float[]{1015f, 1015.01f, 1015.02f, 1015.03f, 1015.04f, 1015.05f, 1015.06f, 1015.07f, 1015.08f, 1015.09f, 1015.10f, 1015.11f, 1015.12f, 1015.13f, 1018f, 1018.1f, 1018.1f, 1018.1f, 1018.1f}); // then Assert.assertEquals(0f, subject.getAltitudeGain_m(), 0.01); - Assert.assertEquals(48.0, subject.getAltitudeLoss_m(), 0.01); + Assert.assertEquals(15f, subject.getAltitudeLoss_m(), 0.01); } @Test @@ -41,7 +43,7 @@ public class AltitudeSumManagerTest { subject.setConnected(false); // then - subject.onSensorValueChanged(999f); + subject.onSensorValueChanged(AtmosphericPressure.ofHPA(999f)); // then Assert.assertNull(subject.getAltitudeGain_m()); diff --git a/src/androidTest/java/de/dennisguse/opentracks/sensors/BluetoothUtilsTest.java b/src/androidTest/java/de/dennisguse/opentracks/sensors/BluetoothUtilsTest.java index 8654a4658..dd595458f 100644 --- a/src/androidTest/java/de/dennisguse/opentracks/sensors/BluetoothUtilsTest.java +++ b/src/androidTest/java/de/dennisguse/opentracks/sensors/BluetoothUtilsTest.java @@ -7,6 +7,7 @@ import android.bluetooth.BluetoothGattCharacteristic; import org.junit.Test; +import de.dennisguse.opentracks.data.models.AtmosphericPressure; import de.dennisguse.opentracks.data.models.Cadence; import de.dennisguse.opentracks.data.models.Distance; import de.dennisguse.opentracks.data.models.HeartRate; @@ -44,17 +45,16 @@ public class BluetoothUtilsTest { } @Test - public void parsePressure_Pa() { + public void parseEnvironmentalSensing_Pa() { // given - BluetoothGattCharacteristic characteristic = new BluetoothGattCharacteristic(BluetoothUtils.PRESSURE.getServiceUUID(), 0, 0); + BluetoothGattCharacteristic characteristic = new BluetoothGattCharacteristic(BluetoothUtils.BAROMETRIC_PRESSURE.getServiceUUID(), 0, 0); characteristic.setValue(new byte[]{(byte) 0xB2, (byte) 0x48, (byte) 0x0F, (byte) 0x00}); - // when - int pressure = BluetoothUtils.parsePressure(characteristic); + AtmosphericPressure pressure = BluetoothUtils.parseEnvironmentalSensing(characteristic); // then - assertEquals(1001650, pressure); + assertEquals(AtmosphericPressure.ofPA(100165), pressure); } @Test diff --git a/src/androidTest/java/de/dennisguse/opentracks/sensors/PressureSensorUtilsTest.java b/src/androidTest/java/de/dennisguse/opentracks/sensors/PressureSensorUtilsTest.java index feae236d7..d1ef901ec 100644 --- a/src/androidTest/java/de/dennisguse/opentracks/sensors/PressureSensorUtilsTest.java +++ b/src/androidTest/java/de/dennisguse/opentracks/sensors/PressureSensorUtilsTest.java @@ -5,6 +5,8 @@ import static org.junit.Assert.assertNull; import org.junit.Test; +import de.dennisguse.opentracks.data.models.AtmosphericPressure; + public class PressureSensorUtilsTest { // real data at 500Hz stationary @@ -12,11 +14,11 @@ public class PressureSensorUtilsTest { public void altitudeChanges_none() { // given float[] sensorValues_hPa = new float[]{1015.6876f, 1015.699f, 1015.70905f, 1015.71075f, 1015.7224f, 1015.72f, 1015.7373f, 1015.7481f, 1015.74133f, 1015.73553f, 1015.7462f, 1015.74896f, 1015.752f, 1015.754f, 1015.7456f, 1015.73303f, 1015.7288f, 1015.7239f, 1015.7298f, 1015.7387f, 1015.72534f, 1015.72577f, 1015.72797f, 1015.71796f, 1015.7113f, 1015.7097f, 1015.7096f, 1015.7013f, 1015.7072f, 1015.71063f, 1015.7088f, 1015.7115f, 1015.713f, 1015.7121f, 1015.7062f, 1015.7062f, 1015.6994f, 1015.69617f, 1015.68945f, 1015.698f, 1015.6913f, 1015.69696f, 1015.69293f, 1015.6955f, 1015.6928f, 1015.6939f, 1015.68787f, 1015.6881f, 1015.68536f, 1015.68726f, 1015.6879f, 1015.6904f, 1015.6937f, 1015.69476f, 1015.7046f, 1015.7019f, 1015.7079f, 1015.71124f, 1015.71216f, 1015.71436f, 1015.7171f, 1015.7186f, 1015.7186f, 1015.7246f, 1015.72046f, 1015.7211f, 1015.72375f, 1015.7194f, 1015.72125f, 1015.7161f, 1015.71185f, 1015.7188f, 1015.721f, 1015.7252f, 1015.72266f, 1015.7236f, 1015.72614f, 1015.7266f, 1015.7344f, 1015.7376f, 1015.7337f, 1015.7253f, 1015.71875f, 1015.7219f, 1015.7219f, 1015.7317f, 1015.7284f, 1015.73444f, 1015.73584f, 1015.7369f, 1015.73254f, 1015.7369f, 1015.7336f, 1015.73254f, 1015.73676f, 1015.74176f, 1015.74506f, 1015.7478f, 1015.755f, 1015.7515f, 1015.7543f, 1015.7459f, 1015.7468f, 1015.7492f, 1015.74585f, 1015.74756f, 1015.74756f, 1015.75085f, 1015.754f, 1015.75336f, 1015.7568f, 1015.75165f, 1015.761f, 1015.7527f, 1015.75684f, 1015.7635f, 1015.7585f, 1015.7552f, 1015.7475f, 1015.74506f, 1015.7542f, 1015.75586f, 1015.74176f, 1015.74414f, 1015.7501f, 1015.751f, 1015.7478f, 1015.755f, 1015.75836f, 1015.7592f, 1015.75256f, 1015.7559f, 1015.76654f, 1015.76154f, 1015.766f, 1015.77014f, 1015.76904f, 1015.76013f, 1015.7674f, 1015.76514f, 1015.7674f, 1015.7635f, 1015.76404f, 1015.7607f, 1015.754f, 1015.7574f, 1015.7607f, 1015.7607f, 1015.7607f, 1015.754f, 1015.7567f, 1015.7616f, 1015.7643f, 1015.7666f, 1015.7609f, 1015.766f, 1015.76764f, 1015.755f, 1015.75757f, 1015.74927f, 1015.74927f, 1015.7426f, 1015.745f, 1015.745f, 1015.7409f, 1015.7376f, 1015.7326f, 1015.74164f, 1015.74664f, 1015.745f, 1015.7459f, 1015.7502f, 1015.7493f, 1015.7476f, 1015.7493f, 1015.751f, 1015.7601f, 1015.76434f, 1015.766f, 1015.756f, 1015.7617f, 1015.7623f, 1015.76404f, 1015.76733f, 1015.7607f, 1015.7623f, 1015.76337f, 1015.7584f, 1015.7573f, 1015.7502f, 1015.7534f, 1015.7534f, 1015.7601f, 1015.75684f, 1015.7518f, 1015.75574f, 1015.7524f, 1015.7507f, 1015.7484f, 1015.7502f, 1015.7534f, 1015.7623f, 1015.75903f, 1015.75574f, 1015.75574f, 1015.7573f, 1015.7524f, 1015.75574f, 1015.75574f, 1015.7607f, 1015.7573f, 1015.754f, 1015.74677f, 1015.754f, 1015.75574f, 1015.75903f, 1015.7573f, 1015.7551f, 1015.7573f, 1015.754f, 1015.7507f, 1015.7374f, 1015.739f, 1015.73566f, 1015.73566f, 1015.7367f, 1015.7417f, 1015.7417f, 1015.739f, 1015.74835f, 1015.74567f, 1015.7517f, 1015.74066f, 1015.7374f, 1015.739f, 1015.7417f, 1015.73065f, 1015.72894f, 1015.73505f, 1015.73334f, 1015.74005f, 1015.74005f, 1015.7367f, 1015.7434f, 1015.744f, 1015.7423f, 1015.7384f, 1015.739f, 1015.7374f, 1015.7374f, 1015.7374f, 1015.7341f, 1015.73505f, 1015.7341f, 1015.7267f, 1015.72833f, 1015.7234f, 1015.7167f, 1015.725f, 1015.7217f, 1015.7234f, 1015.73004f, 1015.7324f, 1015.7423f, 1015.74005f, 1015.73505f, 1015.73004f, 1015.739f, 1015.7417f, 1015.74567f, 1015.74066f, 1015.74146f, 1015.74146f, 1015.73816f, 1015.7274f, 1015.72906f, 1015.7274f, 1015.7341f, 1015.739f, 1015.744f, 1015.7417f, 1015.7374f, 1015.7324f, 1015.73816f, 1015.74146f, 1015.7374f, 1015.7341f, 1015.7365f, 1015.7365f, 1015.74146f, 1015.74414f, 1015.7475f, 1015.7448f, 1015.74146f, 1015.74585f, 1015.7475f, 1015.75146f, 1015.75146f, 1015.7591f, 1015.75476f, 1015.7614f, 1015.75476f, 1015.7492f, 1015.74817f, 1015.7498f, 1015.7515f, 1015.75586f, 1015.7509f, 1015.7448f, 1015.7525f, 1015.7581f, 1015.7592f}; - float firstSensorValue = sensorValues_hPa[0]; + AtmosphericPressure firstSensorValue = AtmosphericPressure.ofHPA(sensorValues_hPa[0]); // when // then for (float v : sensorValues_hPa) { - assertNull(PressureSensorUtils.computeChanges(firstSensorValue, v)); + assertNull(PressureSensorUtils.computeChanges(firstSensorValue, AtmosphericPressure.ofHPA(v))); } } @@ -25,25 +27,25 @@ public class PressureSensorUtilsTest { @Test public void computeChanges_downhill() { float[] sensorValues_hPa = 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}; - float firstSensorValue = sensorValues_hPa[0]; + AtmosphericPressure firstSensorValue = AtmosphericPressure.ofHPA(sensorValues_hPa[0]); // when float altitudeGain_m = 0; float altitudeLoss = 0; - float lastUsedPressureValue_hPa = firstSensorValue; + AtmosphericPressure lastUsedPressureValue = firstSensorValue; for (float v : sensorValues_hPa) { - PressureSensorUtils.AltitudeChange altitudeChange = PressureSensorUtils.computeChanges(lastUsedPressureValue_hPa, v); + PressureSensorUtils.AltitudeChange altitudeChange = PressureSensorUtils.computeChanges(lastUsedPressureValue, AtmosphericPressure.ofHPA(v)); if (altitudeChange != null) { altitudeGain_m += altitudeChange.getAltitudeGain_m(); altitudeLoss += altitudeChange.getAltitudeLoss_m(); - lastUsedPressureValue_hPa = altitudeChange.getCurrentSensorValue_hPa(); + lastUsedPressureValue = altitudeChange.getCurrentSensorValue(); } } // then assertEquals(0f, altitudeGain_m, 0.01); assertEquals(15.0f, altitudeLoss, 0.01); - assertEquals(1016.80f, lastUsedPressureValue_hPa, 0.01); //Expect exponential smoothing + assertEquals(1016.80f, lastUsedPressureValue.getHPA(), 0.01); //Expect exponential smoothing } } \ No newline at end of file diff --git a/src/main/java/de/dennisguse/opentracks/data/models/AtmosphericPressure.java b/src/main/java/de/dennisguse/opentracks/data/models/AtmosphericPressure.java new file mode 100644 index 000000000..c32d5d6fb --- /dev/null +++ b/src/main/java/de/dennisguse/opentracks/data/models/AtmosphericPressure.java @@ -0,0 +1,48 @@ +package de.dennisguse.opentracks.data.models; + +import java.util.Objects; + +public class AtmosphericPressure { + + public static AtmosphericPressure ofPA(float value_Pa) { + return new AtmosphericPressure(value_Pa * 100); + } + + public static AtmosphericPressure ofHPA(float value_hPa) { + return new AtmosphericPressure(value_hPa); + } + + private final float value; + + private AtmosphericPressure(float value) { + this.value = value; + } + + public float getPA() { + return value * 100; + } + + public float getHPA() { + return value; + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + AtmosphericPressure that = (AtmosphericPressure) o; + return Float.compare(that.value, value) == 0; + } + + @Override + public int hashCode() { + return Objects.hash(value); + } + + @Override + public String toString() { + return "AtmosphericPressure{" + + "value=" + value + + '}'; + } +} diff --git a/src/main/java/de/dennisguse/opentracks/sensors/AltitudeSumManager.java b/src/main/java/de/dennisguse/opentracks/sensors/AltitudeSumManager.java index fbd7837aa..c44522405 100644 --- a/src/main/java/de/dennisguse/opentracks/sensors/AltitudeSumManager.java +++ b/src/main/java/de/dennisguse/opentracks/sensors/AltitudeSumManager.java @@ -14,6 +14,7 @@ import androidx.annotation.VisibleForTesting; import java.util.concurrent.TimeUnit; +import de.dennisguse.opentracks.data.models.AtmosphericPressure; import de.dennisguse.opentracks.data.models.TrackPoint; /** @@ -25,9 +26,9 @@ public class AltitudeSumManager implements SensorConnector, SensorEventListener private boolean isConnected = false; - private float lastAcceptedPressureValue_hPa; + private AtmosphericPressure lastAcceptedSensorValue; - private float lastSeenSensorValue_hPa; + private AtmosphericPressure lastSeenSensorValue; private Float altitudeGain_m; private Float altitudeLoss_m; @@ -43,7 +44,7 @@ public class AltitudeSumManager implements SensorConnector, SensorEventListener isConnected = sensorManager.registerListener(this, pressureSensor, (int) TimeUnit.SECONDS.toMicros(5), handler); } - lastAcceptedPressureValue_hPa = Float.NaN; + lastAcceptedSensorValue = null; reset(); } @@ -116,30 +117,30 @@ public class AltitudeSumManager implements SensorConnector, SensorEventListener Log.w(TAG, "Not connected to sensor, cannot process data."); return; } - onSensorValueChanged(event.values[0]); + onSensorValueChanged(AtmosphericPressure.ofHPA(event.values[0])); } @VisibleForTesting - void onSensorValueChanged(float value_hPa) { - if (Float.isNaN(lastAcceptedPressureValue_hPa)) { - lastAcceptedPressureValue_hPa = value_hPa; - lastSeenSensorValue_hPa = value_hPa; + void onSensorValueChanged(AtmosphericPressure currentSensorValue) { + if (lastAcceptedSensorValue == null) { + lastAcceptedSensorValue = currentSensorValue; + lastSeenSensorValue = currentSensorValue; return; } altitudeGain_m = altitudeGain_m != null ? altitudeGain_m : 0; altitudeLoss_m = altitudeLoss_m != null ? altitudeLoss_m : 0; - PressureSensorUtils.AltitudeChange altitudeChange = PressureSensorUtils.computeChangesWithSmoothing_m(lastAcceptedPressureValue_hPa, lastSeenSensorValue_hPa, value_hPa); + PressureSensorUtils.AltitudeChange altitudeChange = PressureSensorUtils.computeChangesWithSmoothing_m(lastAcceptedSensorValue, lastSeenSensorValue, currentSensorValue); if (altitudeChange != null) { altitudeGain_m += altitudeChange.getAltitudeGain_m(); altitudeLoss_m += altitudeChange.getAltitudeLoss_m(); - lastAcceptedPressureValue_hPa = altitudeChange.getCurrentSensorValue_hPa(); + lastAcceptedSensorValue = altitudeChange.getCurrentSensorValue(); } - lastSeenSensorValue_hPa = value_hPa; + lastSeenSensorValue = currentSensorValue; Log.v(TAG, "altitude gain: " + altitudeGain_m + ", altitude loss: " + altitudeLoss_m); } diff --git a/src/main/java/de/dennisguse/opentracks/sensors/BluetoothUtils.java b/src/main/java/de/dennisguse/opentracks/sensors/BluetoothUtils.java index 23e099d81..028dff1e1 100644 --- a/src/main/java/de/dennisguse/opentracks/sensors/BluetoothUtils.java +++ b/src/main/java/de/dennisguse/opentracks/sensors/BluetoothUtils.java @@ -28,6 +28,7 @@ import java.util.Collections; import java.util.List; import java.util.UUID; +import de.dennisguse.opentracks.data.models.AtmosphericPressure; import de.dennisguse.opentracks.data.models.BatteryLevel; import de.dennisguse.opentracks.data.models.Cadence; import de.dennisguse.opentracks.data.models.Distance; @@ -69,8 +70,9 @@ public class BluetoothUtils { )) ); - public static final ServiceMeasurementUUID PRESSURE = new ServiceMeasurementUUID( - new UUID(0x181A00001000L, 0x800000805f9b34fbL), + private static final UUID ENVIRONMENTAL_SENSING_SERVICE = new UUID(0x181A00001000L, 0x800000805f9b34fbL); + public static final ServiceMeasurementUUID BAROMETRIC_PRESSURE = new ServiceMeasurementUUID( + ENVIRONMENTAL_SENSING_SERVICE, new UUID(0x2A6D00001000L, 0x800000805f9b34fbL) ); @@ -142,15 +144,15 @@ public class BluetoothUtils { return null; } - public static Integer parsePressure(BluetoothGattCharacteristic characteristic) { + public static AtmosphericPressure parseEnvironmentalSensing(BluetoothGattCharacteristic characteristic) { byte[] raw = characteristic.getValue(); if (raw.length < 4) { return null; } - Float fPressure = characteristic.getFloatValue(BluetoothGattCharacteristic.FORMAT_FLOAT, 0); - return Math.round(fPressure); + Integer pressure = characteristic.getIntValue(BluetoothGattCharacteristic.FORMAT_UINT32, 0); + return AtmosphericPressure.ofPA(pressure / 10f); } public static SensorDataCyclingPower.Data parseCyclingPower(String address, String sensorName, BluetoothGattCharacteristic characteristic) { diff --git a/src/main/java/de/dennisguse/opentracks/sensors/PressureSensorUtils.java b/src/main/java/de/dennisguse/opentracks/sensors/PressureSensorUtils.java index ed447a6f6..d86e88ca7 100644 --- a/src/main/java/de/dennisguse/opentracks/sensors/PressureSensorUtils.java +++ b/src/main/java/de/dennisguse/opentracks/sensors/PressureSensorUtils.java @@ -4,6 +4,8 @@ import android.hardware.SensorManager; import androidx.annotation.VisibleForTesting; +import de.dennisguse.opentracks.data.models.AtmosphericPressure; + public class PressureSensorUtils { //Everything above is considered a meaningful change in altitude. @@ -18,17 +20,17 @@ public class PressureSensorUtils { public static class AltitudeChange { - private final float currentSensorValue_hPa; + private final AtmosphericPressure currentSensorValue; private final float altitudeChange_m; - public AltitudeChange(float currentSensorValue_hPa, float altitudeChange_m) { - this.currentSensorValue_hPa = currentSensorValue_hPa; + public AltitudeChange(AtmosphericPressure currentSensorValue, float altitudeChange_m) { + this.currentSensorValue = currentSensorValue; this.altitudeChange_m = altitudeChange_m; } - public float getCurrentSensorValue_hPa() { - return currentSensorValue_hPa; + public AtmosphericPressure getCurrentSensorValue() { + return currentSensorValue; } public float getAltitudeChange_m() { @@ -47,16 +49,16 @@ public class PressureSensorUtils { /** * Applies exponential smoothing to sensor value before computation. */ - public static AltitudeChange computeChangesWithSmoothing_m(float lastAcceptedSensorValue_hPa, float lastSeenSensorValue_hPa, float currentSensorValue_hPa) { - float nextSensorValue_hPa = EXPONENTIAL_SMOOTHING * currentSensorValue_hPa + (1 - EXPONENTIAL_SMOOTHING) * lastSeenSensorValue_hPa; + public static AltitudeChange computeChangesWithSmoothing_m(AtmosphericPressure lastAcceptedSensorValue, AtmosphericPressure lastSeenSensorValue, AtmosphericPressure currentSensorValue) { + AtmosphericPressure nextSensorValue = AtmosphericPressure.ofHPA(EXPONENTIAL_SMOOTHING * currentSensorValue.getHPA() + (1 - EXPONENTIAL_SMOOTHING) * lastSeenSensorValue.getHPA()); - return computeChanges(lastAcceptedSensorValue_hPa, nextSensorValue_hPa); + return computeChanges(lastAcceptedSensorValue, nextSensorValue); } @VisibleForTesting - public static AltitudeChange computeChanges(float lastAcceptedSensorValue_hPa, float currentSensorValue_hPa) { - float lastSensorValue_m = SensorManager.getAltitude(p0, lastAcceptedSensorValue_hPa); - float currentSensorValue_m = SensorManager.getAltitude(p0, currentSensorValue_hPa); + public static AltitudeChange computeChanges(AtmosphericPressure lastAcceptedSensorValue, AtmosphericPressure currentSensorValue) { + float lastSensorValue_m = SensorManager.getAltitude(p0, lastAcceptedSensorValue.getHPA()); + float currentSensorValue_m = SensorManager.getAltitude(p0, currentSensorValue.getHPA()); float altitudeChange_m = currentSensorValue_m - lastSensorValue_m; if (Math.abs(altitudeChange_m) < ALTITUDE_CHANGE_DIFF_M) { @@ -64,7 +66,7 @@ public class PressureSensorUtils { } // Limit altitudeC change by ALTITUDE_CHANGE_DIFF and computes pressure value accordingly. - AltitudeChange altitudeChange = new AltitudeChange(currentSensorValue_hPa, altitudeChange_m); + AltitudeChange altitudeChange = new AltitudeChange(currentSensorValue, altitudeChange_m); if (altitudeChange.getAltitudeChange_m() > 0) { return new AltitudeChange(getBarometricPressure(lastSensorValue_m + ALTITUDE_CHANGE_DIFF_M), ALTITUDE_CHANGE_DIFF_M); } else { @@ -78,7 +80,7 @@ public class PressureSensorUtils { * {\color{White} p(h)} = p_0 \cdot \left( 1 - \frac{0{,}0065 \frac{\mathrm K}{\mathrm m} \cdot h}{T_0\ } \right)^{5{,}255} */ @VisibleForTesting - public static float getBarometricPressure(float altitude_m) { - return (float) (p0 * Math.pow(1.0 - 0.0065 * altitude_m / 288.15, 5.255f)); + public static AtmosphericPressure getBarometricPressure(float altitude_m) { + return AtmosphericPressure.ofHPA((float) (p0 * Math.pow(1.0 - 0.0065 * altitude_m / 288.15, 5.255f))); } } From 6c852091cefb489932901718eb42ec39a105e3c0 Mon Sep 17 00:00:00 2001 From: Dennis Guse Date: Sat, 27 May 2023 09:22:49 +0200 Subject: [PATCH 3/7] AltitudeSumManager: extracted Barometer driver. Part of #1424. --- .../sensors/AltitudeSumManagerTest.java | 24 +++--- .../sensors/AltitudeSumManager.java | 73 +++++-------------- .../sensors/driver/BarometerInternal.java | 67 +++++++++++++++++ 3 files changed, 99 insertions(+), 65 deletions(-) create mode 100644 src/main/java/de/dennisguse/opentracks/sensors/driver/BarometerInternal.java diff --git a/src/androidTest/java/de/dennisguse/opentracks/sensors/AltitudeSumManagerTest.java b/src/androidTest/java/de/dennisguse/opentracks/sensors/AltitudeSumManagerTest.java index fb8160c52..557d3ff71 100644 --- a/src/androidTest/java/de/dennisguse/opentracks/sensors/AltitudeSumManagerTest.java +++ b/src/androidTest/java/de/dennisguse/opentracks/sensors/AltitudeSumManagerTest.java @@ -1,33 +1,37 @@ package de.dennisguse.opentracks.sensors; +import static org.mockito.Mockito.when; + import org.junit.Assert; -import org.junit.Before; import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.Mock; +import org.mockito.junit.MockitoJUnitRunner; import de.dennisguse.opentracks.data.models.AtmosphericPressure; +import de.dennisguse.opentracks.sensors.driver.BarometerInternal; /** * NOTE: Test data is completely artificial. */ +@RunWith(MockitoJUnitRunner.class) public class AltitudeSumManagerTest { - private final AltitudeSumManager subject = new AltitudeSumManager(); - private static void addSensorValue(AltitudeSumManager altitudeSumManager, float[] values) { for (float f : values) { altitudeSumManager.onSensorValueChanged(AtmosphericPressure.ofHPA(f)); } } - @Before - public void setUp() { - subject.reset(); - } + @Mock + BarometerInternal driver; @Test public void getAltitudeGainLoss_downhill() { // given - subject.setConnected(true); + AltitudeSumManager subject = new AltitudeSumManager(driver); + + when(driver.isConnected()).thenReturn(true); // then addSensorValue(subject, new float[]{1015f, 1015.01f, 1015.02f, 1015.03f, 1015.04f, 1015.05f, 1015.06f, 1015.07f, 1015.08f, 1015.09f, 1015.10f, 1015.11f, 1015.12f, 1015.13f, 1018f, 1018.1f, 1018.1f, 1018.1f, 1018.1f}); @@ -40,7 +44,9 @@ public class AltitudeSumManagerTest { @Test public void sensorUnavailable() { // given - subject.setConnected(false); + AltitudeSumManager subject = new AltitudeSumManager(driver); + + when(driver.isConnected()).thenReturn(true); // then subject.onSensorValueChanged(AtmosphericPressure.ofHPA(999f)); diff --git a/src/main/java/de/dennisguse/opentracks/sensors/AltitudeSumManager.java b/src/main/java/de/dennisguse/opentracks/sensors/AltitudeSumManager.java index c44522405..c85ff9066 100644 --- a/src/main/java/de/dennisguse/opentracks/sensors/AltitudeSumManager.java +++ b/src/main/java/de/dennisguse/opentracks/sensors/AltitudeSumManager.java @@ -1,10 +1,6 @@ package de.dennisguse.opentracks.sensors; import android.content.Context; -import android.hardware.Sensor; -import android.hardware.SensorEvent; -import android.hardware.SensorEventListener; -import android.hardware.SensorManager; import android.os.Handler; import android.util.Log; @@ -12,19 +8,18 @@ import androidx.annotation.NonNull; import androidx.annotation.Nullable; import androidx.annotation.VisibleForTesting; -import java.util.concurrent.TimeUnit; - import de.dennisguse.opentracks.data.models.AtmosphericPressure; import de.dennisguse.opentracks.data.models.TrackPoint; +import de.dennisguse.opentracks.sensors.driver.BarometerInternal; /** * Estimates the altitude gain and altitude loss using the device's pressure sensor (i.e., barometer). */ -public class AltitudeSumManager implements SensorConnector, SensorEventListener { +public class AltitudeSumManager implements SensorConnector { private static final String TAG = AltitudeSumManager.class.getSimpleName(); - private boolean isConnected = false; + private final BarometerInternal driver; private AtmosphericPressure lastAcceptedSensorValue; @@ -33,16 +28,17 @@ public class AltitudeSumManager implements SensorConnector, SensorEventListener private Float altitudeGain_m; private Float altitudeLoss_m; - public void start(Context context, Handler handler) { - SensorManager sensorManager = (SensorManager) context.getSystemService(Context.SENSOR_SERVICE); + public AltitudeSumManager() { + driver = new BarometerInternal(); + } - Sensor pressureSensor = sensorManager.getDefaultSensor(Sensor.TYPE_PRESSURE); - if (pressureSensor == null) { - Log.w(TAG, "No pressure sensor available."); - isConnected = false; - } else { - isConnected = sensorManager.registerListener(this, pressureSensor, (int) TimeUnit.SECONDS.toMicros(5), handler); - } + @VisibleForTesting + public AltitudeSumManager(BarometerInternal mock) { + this.driver = mock; + } + + public void start(Context context, Handler handler) { + driver.connect(context, handler, this); lastAcceptedSensorValue = null; reset(); @@ -51,18 +47,10 @@ public class AltitudeSumManager implements SensorConnector, SensorEventListener public void stop(Context context) { Log.d(TAG, "Stop"); - SensorManager sensorManager = (SensorManager) context.getSystemService(Context.SENSOR_SERVICE); - sensorManager.unregisterListener(this); - - isConnected = false; + driver.disconnect(context); reset(); } - @VisibleForTesting - public void setConnected(boolean isConnected) { - this.isConnected = isConnected; - } - public void fill(@NonNull TrackPoint trackPoint) { trackPoint.setAltitudeGain(altitudeGain_m); trackPoint.setAltitudeLoss(altitudeLoss_m); @@ -70,7 +58,7 @@ public class AltitudeSumManager implements SensorConnector, SensorEventListener @Nullable public Float getAltitudeGain_m() { - return isConnected ? altitudeGain_m : null; + return driver.isConnected() ? altitudeGain_m : null; } @VisibleForTesting @@ -78,21 +66,9 @@ public class AltitudeSumManager implements SensorConnector, SensorEventListener this.altitudeGain_m = altitudeGain_m; } - @VisibleForTesting - public void addAltitudeGain_m(float altitudeGain_m) { - this.altitudeGain_m = this.altitudeGain_m == null ? 0f : this.altitudeGain_m; - this.altitudeGain_m += altitudeGain_m; - } - - @VisibleForTesting - public void addAltitudeLoss_m(Float altitudeLoss_m) { - this.altitudeLoss_m = this.altitudeLoss_m == null ? 0f : this.altitudeLoss_m; - this.altitudeLoss_m += altitudeLoss_m; - } - @Nullable public Float getAltitudeLoss_m() { - return isConnected ? altitudeLoss_m : null; + return driver.isConnected() ? altitudeLoss_m : null; } @VisibleForTesting @@ -106,22 +82,7 @@ public class AltitudeSumManager implements SensorConnector, SensorEventListener altitudeLoss_m = null; } - @Override - public void onAccuracyChanged(Sensor sensor, int accuracy) { - Log.w(TAG, "Sensor accuracy changes are (currently) ignored."); - } - - @Override - public void onSensorChanged(SensorEvent event) { - if (!isConnected) { - Log.w(TAG, "Not connected to sensor, cannot process data."); - return; - } - onSensorValueChanged(AtmosphericPressure.ofHPA(event.values[0])); - } - - @VisibleForTesting - void onSensorValueChanged(AtmosphericPressure currentSensorValue) { + public void onSensorValueChanged(AtmosphericPressure currentSensorValue) { if (lastAcceptedSensorValue == null) { lastAcceptedSensorValue = currentSensorValue; lastSeenSensorValue = currentSensorValue; diff --git a/src/main/java/de/dennisguse/opentracks/sensors/driver/BarometerInternal.java b/src/main/java/de/dennisguse/opentracks/sensors/driver/BarometerInternal.java new file mode 100644 index 000000000..8a405f720 --- /dev/null +++ b/src/main/java/de/dennisguse/opentracks/sensors/driver/BarometerInternal.java @@ -0,0 +1,67 @@ +package de.dennisguse.opentracks.sensors.driver; + +import android.content.Context; +import android.hardware.Sensor; +import android.hardware.SensorEvent; +import android.hardware.SensorEventListener; +import android.hardware.SensorManager; +import android.os.Handler; +import android.util.Log; + +import java.util.concurrent.TimeUnit; + +import de.dennisguse.opentracks.data.models.AtmosphericPressure; +import de.dennisguse.opentracks.sensors.AltitudeSumManager; + +public class BarometerInternal { + + private static final String TAG = BarometerInternal.class.getSimpleName(); + + private static final int SAMPLING_PERIOD = (int) TimeUnit.SECONDS.toMicros(5); + + private AltitudeSumManager observer; + + private final SensorEventListener listener = new SensorEventListener() { + @Override + public void onSensorChanged(SensorEvent event) { + if (!isConnected()) { + Log.w(TAG, "Not connected to sensor, cannot process data."); + return; + } + + observer.onSensorValueChanged(AtmosphericPressure.ofHPA(event.values[0])); + } + + @Override + public void onAccuracyChanged(Sensor sensor, int accuracy) { + Log.w(TAG, "Sensor accuracy changes are (currently) ignored."); + } + }; + + public void connect(Context context, Handler handler, AltitudeSumManager observer) { + this.observer = observer; + SensorManager sensorManager = (SensorManager) context.getSystemService(Context.SENSOR_SERVICE); + Sensor pressureSensor = sensorManager.getDefaultSensor(Sensor.TYPE_PRESSURE); + if (pressureSensor == null) { + Log.w(TAG, "No pressure sensor available."); + this.observer = null; + return; + } + + if (sensorManager.registerListener(listener, pressureSensor, SAMPLING_PERIOD, handler)) { + this.observer = observer; + return; + } + + disconnect(context); + } + + public void disconnect(Context context) { + SensorManager sensorManager = (SensorManager) context.getSystemService(Context.SENSOR_SERVICE); + sensorManager.unregisterListener(listener); + } + + public boolean isConnected() { + return observer != null; + } +} From d046ca1cd8ba702fce68d8ec71cc4997e5e6cc4a Mon Sep 17 00:00:00 2001 From: Dennis Guse Date: Sat, 27 May 2023 10:02:56 +0200 Subject: [PATCH 4/7] SensorManager handles SensorDataSet. Part of #1424. --- .../io/file/importer/ExportImportTest.java | 37 +++++------ .../AbstractBluetoothConnectionManager.java | 1 + .../sensors/BluetoothRemoteSensorManager.java | 64 ++----------------- .../opentracks/sensors/SensorManager.java | 43 ++++++++++--- .../sensors/sensorData/SensorDataSet.java | 44 +++++++++++-- .../services/handlers/TrackPointCreator.java | 5 ++ 6 files changed, 98 insertions(+), 96 deletions(-) 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 bdaf92f5c..c80d5129c 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 @@ -57,7 +57,6 @@ import de.dennisguse.opentracks.data.models.TrackPoint; import de.dennisguse.opentracks.io.file.TrackFileFormat; import de.dennisguse.opentracks.io.file.exporter.TrackExporter; import de.dennisguse.opentracks.sensors.AltitudeSumManager; -import de.dennisguse.opentracks.sensors.BluetoothRemoteSensorManager; import de.dennisguse.opentracks.sensors.sensorData.SensorDataCyclingCadence; import de.dennisguse.opentracks.sensors.sensorData.SensorDataCyclingDistanceSpeed; import de.dennisguse.opentracks.sensors.sensorData.SensorDataCyclingPower; @@ -165,8 +164,7 @@ public class ExportImportTest { service.insertMarker("Marker 2", "Marker 2 category", "Marker 2 desc", null); trackPointCreator.setClock("2020-02-02T02:02:18Z"); - BluetoothRemoteSensorManager mockRemoteSensorManager = Mockito.mock(BluetoothRemoteSensorManager.class); - trackPointCreator.getSensorManager().setBluetoothSensorManager(mockRemoteSensorManager); + trackPointCreator.getSensorManager().sensorDataSet = new SensorDataSet(); service.endCurrentTrack(); trackPointCreator.setClock("2020-02-02T02:03:20Z"); @@ -179,7 +177,6 @@ public class ExportImportTest { sendLocation(trackPointCreator, "2020-02-02T02:03:23Z", 3, 16.001, 10, 27, 15, 10, 0); trackPointCreator.setClock("2020-02-02T02:03:24Z"); - trackPointCreator.getSensorManager().setBluetoothSensorManager(mockRemoteSensorManager); service.endCurrentTrack(); Track track = contentProviderUtils.getTrack(trackId); @@ -495,9 +492,6 @@ public class ExportImportTest { } private void mockBLESensorData(TrackPointCreator trackPointCreator, Float speed, Distance distance, float heartRate, float cadence, Float power) { - BluetoothRemoteSensorManager remoteSensorManager = Mockito.mock(BluetoothRemoteSensorManager.class); - Mockito.when(remoteSensorManager.fill(Mockito.any())).thenAnswer(invocation -> { - TrackPoint trackPoint = invocation.getArgument(0); SensorDataSet sensorDataSet = new SensorDataSet(); sensorDataSet.set(new SensorDataCyclingPower("power", "power", Power.of(power))); @@ -508,21 +502,20 @@ public class ExportImportTest { Mockito.when(cyclingCadence.getValue()).thenReturn(Cadence.of(cadence)); sensorDataSet.set(cyclingCadence); - if (distance != null && speed != null) { - SensorDataCyclingDistanceSpeed.Data distanceSpeedData = Mockito.mock(SensorDataCyclingDistanceSpeed.Data.class); - Mockito.when(distanceSpeedData.getDistanceOverall()).thenReturn(distance); - Mockito.when(distanceSpeedData.getSpeed()).thenReturn(Speed.of(speed)); - SensorDataCyclingDistanceSpeed distanceSpeed = Mockito.mock(SensorDataCyclingDistanceSpeed.class); - Mockito.when(distanceSpeed.hasValue()).thenReturn(true); - Mockito.when(distanceSpeed.getValue()).thenReturn(distanceSpeedData); - sensorDataSet.set(distanceSpeed); - } + if (distance != null && speed != null) { + SensorDataCyclingDistanceSpeed.Data distanceSpeedData = Mockito.mock(SensorDataCyclingDistanceSpeed.Data.class); + Mockito.when(distanceSpeedData.getDistanceOverall()).thenReturn(distance); + Mockito.when(distanceSpeedData.getSpeed()).thenReturn(Speed.of(speed)); + SensorDataCyclingDistanceSpeed distanceSpeed = Mockito.mock(SensorDataCyclingDistanceSpeed.class); + Mockito.when(distanceSpeed.hasValue()).thenReturn(true); + Mockito.when(distanceSpeed.getValue()).thenReturn(distanceSpeedData); + sensorDataSet.set(distanceSpeed); + } - sensorDataSet.fillTrackPoint(trackPoint); - return sensorDataSet; - }); - trackPointCreator.getSensorManager().setBluetoothSensorManager(remoteSensorManager); - trackPointCreator.onChange(new SensorDataSet()); + trackPointCreator.getSensorManager().sensorDataSet = sensorDataSet; + trackPointCreator.onChange(sensorDataSet); + + //TODO Might require: getSensorManager().sensorDataSet = new SensorDataSet() } private void mockAltitudeChange(TrackPointCreator trackPointCreator, float altitudeGain) { @@ -545,6 +538,6 @@ public class ExportImportTest { mockAltitudeChange(trackPointCreator, altitudeGain); trackPointCreator.setClock(time); - trackPointCreator.getGpsHandler().onLocationChanged(location); + trackPointCreator.onChange(location); } } \ No newline at end of file diff --git a/src/main/java/de/dennisguse/opentracks/sensors/AbstractBluetoothConnectionManager.java b/src/main/java/de/dennisguse/opentracks/sensors/AbstractBluetoothConnectionManager.java index 7e0c19f8e..218fbd786 100644 --- a/src/main/java/de/dennisguse/opentracks/sensors/AbstractBluetoothConnectionManager.java +++ b/src/main/java/de/dennisguse/opentracks/sensors/AbstractBluetoothConnectionManager.java @@ -189,6 +189,7 @@ public abstract class AbstractBluetoothConnectionManager { */ protected abstract SensorData parsePayload(@NonNull ServiceMeasurementUUID serviceMeasurementUUID, String sensorName, String address, @NonNull BluetoothGattCharacteristic characteristic); + @Deprecated //TODO REMOVE interface SensorDataObserver { void onChanged(SensorData sensorData); diff --git a/src/main/java/de/dennisguse/opentracks/sensors/BluetoothRemoteSensorManager.java b/src/main/java/de/dennisguse/opentracks/sensors/BluetoothRemoteSensorManager.java index 2711aa1bf..53036f066 100644 --- a/src/main/java/de/dennisguse/opentracks/sensors/BluetoothRemoteSensorManager.java +++ b/src/main/java/de/dennisguse/opentracks/sensors/BluetoothRemoteSensorManager.java @@ -24,17 +24,12 @@ import android.os.Handler; import android.util.Log; import androidx.annotation.NonNull; +import androidx.annotation.Nullable; import java.time.Duration; import de.dennisguse.opentracks.R; -import de.dennisguse.opentracks.data.models.Distance; -import de.dennisguse.opentracks.data.models.TrackPoint; import de.dennisguse.opentracks.sensors.sensorData.SensorData; -import de.dennisguse.opentracks.sensors.sensorData.SensorDataCyclingCadence; -import de.dennisguse.opentracks.sensors.sensorData.SensorDataCyclingDistanceSpeed; -import de.dennisguse.opentracks.sensors.sensorData.SensorDataRunning; -import de.dennisguse.opentracks.sensors.sensorData.SensorDataSet; import de.dennisguse.opentracks.settings.PreferencesUtils; import de.dennisguse.opentracks.util.PermissionRequester; @@ -61,20 +56,15 @@ public class BluetoothRemoteSensorManager implements SensorConnector, AbstractBl private final BluetoothAdapter bluetoothAdapter; private final Context context; private final Handler handler; + private final SensorManager.SensorDataChangedObserver observer; private boolean started = false; - private Distance preferenceWheelCircumference; - private final BluetoothConnectionManagerHeartRate heartRate = new BluetoothConnectionManagerHeartRate(this); private final BluetoothConnectionManagerCyclingCadence cyclingCadence = new BluetoothConnectionManagerCyclingCadence(this); private final BluetoothConnectionManagerCyclingDistanceSpeed cyclingSpeed = new BluetoothConnectionManagerCyclingDistanceSpeed(this); private final BluetoothConnectionManagerCyclingPower cyclingPower = new BluetoothConnectionManagerCyclingPower(this); private final BluetoothConnectionRunningSpeedAndCadence runningSpeedAndCadence = new BluetoothConnectionRunningSpeedAndCadence(this); - private final SensorDataSet sensorDataSet = new SensorDataSet(); - - private final SensorManager.SensorDataSetChangeObserver observer; - private final SharedPreferences.OnSharedPreferenceChangeListener sharedPreferenceChangeListener = new SharedPreferences.OnSharedPreferenceChangeListener() { @Override public void onSharedPreferenceChanged(SharedPreferences sharedPreferences, String key) { @@ -95,9 +85,6 @@ public class BluetoothRemoteSensorManager implements SensorConnector, AbstractBl connect(cyclingSpeed, address); } - if (PreferencesUtils.isKey(R.string.settings_sensor_bluetooth_cycling_speed_wheel_circumference_key, key)) { - preferenceWheelCircumference = PreferencesUtils.getWheelCircumference(); - } if (PreferencesUtils.isKey(R.string.settings_sensor_bluetooth_cycling_power_key, key)) { String address = PreferencesUtils.getBluetoothCyclingPowerSensorAddress(); @@ -114,7 +101,7 @@ public class BluetoothRemoteSensorManager implements SensorConnector, AbstractBl } }; - public BluetoothRemoteSensorManager(@NonNull Context context, @NonNull Handler handler, @NonNull SensorManager.SensorDataSetChangeObserver observer) { + public BluetoothRemoteSensorManager(@NonNull Context context, @NonNull Handler handler, @Nullable SensorManager.SensorDataChangedObserver observer) { this.context = context; this.handler = handler; this.observer = observer; @@ -137,8 +124,6 @@ public class BluetoothRemoteSensorManager implements SensorConnector, AbstractBl cyclingPower.disconnect(); runningSpeedAndCadence.disconnect(); - sensorDataSet.clear(); - PreferencesUtils.unregisterOnSharedPreferenceChangeListener(sharedPreferenceChangeListener); started = false; } @@ -178,53 +163,14 @@ public class BluetoothRemoteSensorManager implements SensorConnector, AbstractBl } } - public SensorDataSet fill(@NonNull TrackPoint trackPoint) { - sensorDataSet.fillTrackPoint(trackPoint); - return new SensorDataSet(sensorDataSet); - } - - public void reset() { - sensorDataSet.reset(); - } - @Override public synchronized void onChanged(SensorData sensorData) { - if (sensorData instanceof SensorDataCyclingCadence) { - SensorDataCyclingCadence previous = sensorDataSet.getCyclingCadence(); - Log.d(TAG, "Previous: " + previous + "; current: " + sensorData); - - if (sensorData.equals(previous)) { - Log.d(TAG, "onChanged: cadence data repeated."); - return; - } - ((SensorDataCyclingCadence) sensorData).compute(previous); - } - if (sensorData instanceof SensorDataCyclingDistanceSpeed) { - SensorDataCyclingDistanceSpeed previous = sensorDataSet.getCyclingDistanceSpeed(); - Log.d(TAG, "Previous: " + previous + "; Current" + sensorData); - if (sensorData.equals(previous)) { - Log.d(TAG, "onChanged: cycling speed data repeated."); - return; - } - ((SensorDataCyclingDistanceSpeed) sensorData).compute(previous, preferenceWheelCircumference); - } - if (sensorData instanceof SensorDataRunning) { - SensorDataRunning previous = sensorDataSet.getRunningDistanceSpeedCadence(); - Log.d(TAG, "Previous: " + previous + "; Current" + sensorData); - if (sensorData.equals(previous)) { - Log.d(TAG, "onChanged: running speed data repeated."); - return; - } - ((SensorDataRunning) sensorData).compute(previous); - } - - sensorDataSet.set(sensorData); - observer.onChange(new SensorDataSet(sensorDataSet)); + observer.onChange(sensorData); } @Override public void onDisconnecting(SensorData sensorData) { - sensorDataSet.remove(sensorData); + observer.onDisconnect(sensorData); } @NonNull diff --git a/src/main/java/de/dennisguse/opentracks/sensors/SensorManager.java b/src/main/java/de/dennisguse/opentracks/sensors/SensorManager.java index bf8e82d08..d2e02fdf6 100644 --- a/src/main/java/de/dennisguse/opentracks/sensors/SensorManager.java +++ b/src/main/java/de/dennisguse/opentracks/sensors/SensorManager.java @@ -14,12 +14,34 @@ public class SensorManager { private static final String TAG = SensorManager.class.getSimpleName(); + //TODO Should be final and not be visible for testing + @VisibleForTesting + public SensorDataSet sensorDataSet = new SensorDataSet(); + + private final SensorManager.SensorDataSetChangeObserver observer; + + private final SensorDataChangedObserver listener = new SensorDataChangedObserver() { + + @Override + public void onChange(SensorData sensorData) { + sensorDataSet.set(sensorData); + observer.onChange(new SensorDataSet(sensorDataSet)); + } + + @Override + public void onDisconnect(SensorData sensorData) { + sensorDataSet.remove(sensorData); + observer.onChange(new SensorDataSet(sensorDataSet)); + } + }; + private BluetoothRemoteSensorManager bluetoothSensorManager; private AltitudeSumManager altitudeSumManager; public SensorManager(Context context, Handler handler, SensorDataSetChangeObserver observer) { - bluetoothSensorManager = new BluetoothRemoteSensorManager(context, handler, observer); + this.observer = observer; + bluetoothSensorManager = new BluetoothRemoteSensorManager(context, handler, listener); altitudeSumManager = new AltitudeSumManager(); } @@ -38,11 +60,14 @@ public class SensorManager { altitudeSumManager.stop(context); altitudeSumManager = null; } + + sensorDataSet.clear(); } public SensorDataSet fill(TrackPoint trackPoint) { altitudeSumManager.fill(trackPoint); - return bluetoothSensorManager.fill(trackPoint); + sensorDataSet.fillTrackPoint(trackPoint); + return new SensorDataSet(sensorDataSet); } public void reset() { @@ -50,7 +75,7 @@ public class SensorManager { Log.d(TAG, "No recording running and no reset necessary."); return; } - bluetoothSensorManager.reset(); + sensorDataSet.reset(); altitudeSumManager.reset(); } @@ -60,12 +85,6 @@ public class SensorManager { return bluetoothSensorManager; } - @Deprecated - @VisibleForTesting - public void setBluetoothSensorManager(BluetoothRemoteSensorManager remoteSensorManager) { - this.bluetoothSensorManager = remoteSensorManager; - } - @Deprecated @VisibleForTesting public AltitudeSumManager getAltitudeSumManager() { @@ -78,6 +97,12 @@ public class SensorManager { this.altitudeSumManager = altitudeSumManager; } + public interface SensorDataChangedObserver { + void onChange(SensorData sensorData); + + void onDisconnect(SensorData sensorData); + } + @Deprecated public interface SensorDataSetChangeObserver { void onChange(SensorDataSet sensorDataSet); diff --git a/src/main/java/de/dennisguse/opentracks/sensors/sensorData/SensorDataSet.java b/src/main/java/de/dennisguse/opentracks/sensors/sensorData/SensorDataSet.java index 07cb2535f..3ae7b3141 100644 --- a/src/main/java/de/dennisguse/opentracks/sensors/sensorData/SensorDataSet.java +++ b/src/main/java/de/dennisguse/opentracks/sensors/sensorData/SensorDataSet.java @@ -1,16 +1,21 @@ package de.dennisguse.opentracks.sensors.sensorData; +import android.util.Log; import android.util.Pair; import androidx.annotation.NonNull; import de.dennisguse.opentracks.data.models.Cadence; +import de.dennisguse.opentracks.data.models.Distance; import de.dennisguse.opentracks.data.models.HeartRate; import de.dennisguse.opentracks.data.models.Speed; import de.dennisguse.opentracks.data.models.TrackPoint; +import de.dennisguse.opentracks.settings.PreferencesUtils; public final class SensorDataSet { + private static final String TAG = SensorDataSet.class.getSimpleName(); + private SensorDataHeartRate heartRate; private SensorDataCyclingCadence cyclingCadence; @@ -140,28 +145,55 @@ public final class SensorDataSet { + (runningDistanceSpeedCadence != null ? " " + runningDistanceSpeedCadence : ""); } - private void set(@NonNull SensorData type, SensorData data) { + private void set(@NonNull SensorData type, SensorData sensorData) { if (type instanceof SensorDataHeartRate) { - this.heartRate = (SensorDataHeartRate) data; + this.heartRate = (SensorDataHeartRate) sensorData; return; } if (type instanceof SensorDataCyclingCadence) { - this.cyclingCadence = (SensorDataCyclingCadence) data; + SensorDataCyclingCadence previous = getCyclingCadence(); + Log.d(TAG, "Previous: " + previous + "; current: " + sensorData); + + if (sensorData.equals(previous)) { + Log.d(TAG, "onChanged: cadence data repeated."); + return; + } + + this.cyclingCadence = (SensorDataCyclingCadence) sensorData; + this.cyclingCadence.compute(previous); return; } + if (type instanceof SensorDataCyclingDistanceSpeed) { - this.cyclingDistanceSpeed = (SensorDataCyclingDistanceSpeed) data; + SensorDataCyclingDistanceSpeed previous = getCyclingDistanceSpeed(); + Log.d(TAG, "Previous: " + previous + "; Current" + sensorData); + if (sensorData.equals(previous)) { + Log.d(TAG, "onChanged: cycling speed data repeated."); + return; + } + Distance preferenceWheelCircumference = PreferencesUtils.getWheelCircumference(); //TODO Fetch once and then listen for changes. + + this.cyclingDistanceSpeed = (SensorDataCyclingDistanceSpeed) sensorData; + this.cyclingDistanceSpeed.compute(previous, preferenceWheelCircumference); return; } if (type instanceof SensorDataCyclingPower) { - this.cyclingPower = (SensorDataCyclingPower) data; + this.cyclingPower = (SensorDataCyclingPower) sensorData; return; } if (type instanceof SensorDataRunning) { - this.runningDistanceSpeedCadence = (SensorDataRunning) data; + SensorDataRunning previous = getRunningDistanceSpeedCadence(); + Log.d(TAG, "Previous: " + previous + "; Current" + sensorData); + if (sensorData.equals(previous)) { + Log.d(TAG, "onChanged: running speed data repeated."); + return; + } + + this.runningDistanceSpeedCadence = (SensorDataRunning) sensorData; + this.runningDistanceSpeedCadence.compute(previous); return; } 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 744760401..aab81ac6b 100644 --- a/src/main/java/de/dennisguse/opentracks/services/handlers/TrackPointCreator.java +++ b/src/main/java/de/dennisguse/opentracks/services/handlers/TrackPointCreator.java @@ -142,6 +142,11 @@ public class TrackPointCreator implements SensorManager.SensorDataSetChangeObser return sensorManager; } + @VisibleForTesting + public void setSensorManager(SensorManager sensorManager) { + this.sensorManager = sensorManager; + } + @VisibleForTesting public void setClock(@NonNull String time) { this.clock = Clock.fixed(Instant.parse(time), ZoneId.of("CET")); From 1657bf5ec1e8545478b9d1f23aa21de0804a559a Mon Sep 17 00:00:00 2001 From: Dennis Guse Date: Sat, 27 May 2023 11:12:58 +0200 Subject: [PATCH 5/7] Cleanup: removed SensorDataSetChangeObserver interface. Part of #1424. --- .../dennisguse/opentracks/sensors/SensorManager.java | 10 +++------- .../services/handlers/TrackPointCreator.java | 3 +-- 2 files changed, 4 insertions(+), 9 deletions(-) diff --git a/src/main/java/de/dennisguse/opentracks/sensors/SensorManager.java b/src/main/java/de/dennisguse/opentracks/sensors/SensorManager.java index d2e02fdf6..2e2743c9d 100644 --- a/src/main/java/de/dennisguse/opentracks/sensors/SensorManager.java +++ b/src/main/java/de/dennisguse/opentracks/sensors/SensorManager.java @@ -9,6 +9,7 @@ import androidx.annotation.VisibleForTesting; import de.dennisguse.opentracks.data.models.TrackPoint; import de.dennisguse.opentracks.sensors.sensorData.SensorData; import de.dennisguse.opentracks.sensors.sensorData.SensorDataSet; +import de.dennisguse.opentracks.services.handlers.TrackPointCreator; public class SensorManager { @@ -18,7 +19,7 @@ public class SensorManager { @VisibleForTesting public SensorDataSet sensorDataSet = new SensorDataSet(); - private final SensorManager.SensorDataSetChangeObserver observer; + private final TrackPointCreator observer; private final SensorDataChangedObserver listener = new SensorDataChangedObserver() { @@ -39,7 +40,7 @@ public class SensorManager { private AltitudeSumManager altitudeSumManager; - public SensorManager(Context context, Handler handler, SensorDataSetChangeObserver observer) { + public SensorManager(Context context, Handler handler, TrackPointCreator observer) { this.observer = observer; bluetoothSensorManager = new BluetoothRemoteSensorManager(context, handler, listener); altitudeSumManager = new AltitudeSumManager(); @@ -102,9 +103,4 @@ public class SensorManager { void onDisconnect(SensorData sensorData); } - - @Deprecated - public interface SensorDataSetChangeObserver { - void onChange(SensorDataSet sensorDataSet); - } } 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 aab81ac6b..df8b3f120 100644 --- a/src/main/java/de/dennisguse/opentracks/services/handlers/TrackPointCreator.java +++ b/src/main/java/de/dennisguse/opentracks/services/handlers/TrackPointCreator.java @@ -22,7 +22,7 @@ import de.dennisguse.opentracks.sensors.sensorData.SensorDataSet; /** * Creates TrackPoints while recording by fusing data from different sensors (e.g., GNSS, barometer, BLE sensors). */ -public class TrackPointCreator implements SensorManager.SensorDataSetChangeObserver { +public class TrackPointCreator { private static final String TAG = TrackPointCreator.class.getSimpleName(); @@ -85,7 +85,6 @@ public class TrackPointCreator implements SensorManager.SensorDataSetChangeObser /** * Got a new TrackPoint from Bluetooth only; contains no GPS location. */ - @Override public synchronized void onChange(@NonNull SensorDataSet unused) { onNewTrackPoint(new TrackPoint(TrackPoint.Type.SENSORPOINT, createNow())); } From f336a1da12dd2eb575112293e1e44cd36a6ecfb5 Mon Sep 17 00:00:00 2001 From: Dennis Guse Date: Sat, 27 May 2023 13:22:33 +0200 Subject: [PATCH 6/7] Move GPSManager into SensorManager. Includes Bugfix: SensorManager might not have been stopped. Part of #1424. --- .../TrackRecordingServiceTestUtils.java | 2 +- .../handlers/TrackPointCreatorTest.java | 57 ------------------- .../opentracks/sensors/SensorManager.java | 22 ++++++- .../services/handlers/GPSManager.java | 2 +- .../services/handlers/TrackPointCreator.java | 25 ++------ 5 files changed, 26 insertions(+), 82 deletions(-) delete mode 100644 src/androidTest/java/de/dennisguse/opentracks/services/handlers/TrackPointCreatorTest.java diff --git a/src/androidTest/java/de/dennisguse/opentracks/services/TrackRecordingServiceTestUtils.java b/src/androidTest/java/de/dennisguse/opentracks/services/TrackRecordingServiceTestUtils.java index 214f152db..07441fd9d 100644 --- a/src/androidTest/java/de/dennisguse/opentracks/services/TrackRecordingServiceTestUtils.java +++ b/src/androidTest/java/de/dennisguse/opentracks/services/TrackRecordingServiceTestUtils.java @@ -30,6 +30,6 @@ public class TrackRecordingServiceTestUtils { location.setSpeed(speed); trackPointCreator.setClock(time); - trackPointCreator.getGpsHandler().onLocationChanged(location); + trackPointCreator.getSensorManager().getGpsManager().onLocationChanged(location); } } diff --git a/src/androidTest/java/de/dennisguse/opentracks/services/handlers/TrackPointCreatorTest.java b/src/androidTest/java/de/dennisguse/opentracks/services/handlers/TrackPointCreatorTest.java deleted file mode 100644 index 0b6519fe5..000000000 --- a/src/androidTest/java/de/dennisguse/opentracks/services/handlers/TrackPointCreatorTest.java +++ /dev/null @@ -1,57 +0,0 @@ -package de.dennisguse.opentracks.services.handlers; - -import static org.mockito.Mockito.verify; - -import android.content.Context; - -import org.junit.After; -import org.junit.Before; -import org.junit.Ignore; -import org.junit.Test; -import org.junit.runner.RunWith; -import org.mockito.Mock; -import org.mockito.junit.MockitoJUnitRunner; - -import de.dennisguse.opentracks.data.models.Distance; -import de.dennisguse.opentracks.data.models.TrackPoint; - -@RunWith(MockitoJUnitRunner.class) -public class TrackPointCreatorTest { - - @Mock - private Context context; - - @Mock - private TrackPointCreator.Callback server; - - @Mock - private GPSManager locationHandler; - - private TrackPointCreator subject; - - @Before - public void setUp() { - subject = new TrackPointCreator(locationHandler, server); - subject.start(context, null); - } - - @After - public void tearDown() { - subject.stop(); - } - - @Ignore("ServiceExecutor disabled for #822") - @Test - public void sendTrackPoint() throws InterruptedException { - // given - TrackPoint trackPoint = new TrackPoint(TrackPoint.Type.TRACKPOINT, null); - Distance horizontalAccuracyThreshold = Distance.of(50); - - // when - subject.onNewTrackPoint(trackPoint); - - // then - Thread.sleep(10); // Wait for executor service - verify(server).newTrackPoint(trackPoint, horizontalAccuracyThreshold); - } -} \ No newline at end of file diff --git a/src/main/java/de/dennisguse/opentracks/sensors/SensorManager.java b/src/main/java/de/dennisguse/opentracks/sensors/SensorManager.java index 2e2743c9d..520af52e2 100644 --- a/src/main/java/de/dennisguse/opentracks/sensors/SensorManager.java +++ b/src/main/java/de/dennisguse/opentracks/sensors/SensorManager.java @@ -9,6 +9,7 @@ import androidx.annotation.VisibleForTesting; import de.dennisguse.opentracks.data.models.TrackPoint; import de.dennisguse.opentracks.sensors.sensorData.SensorData; import de.dennisguse.opentracks.sensors.sensorData.SensorDataSet; +import de.dennisguse.opentracks.services.handlers.GPSManager; import de.dennisguse.opentracks.services.handlers.TrackPointCreator; public class SensorManager { @@ -40,14 +41,20 @@ public class SensorManager { private AltitudeSumManager altitudeSumManager; - public SensorManager(Context context, Handler handler, TrackPointCreator observer) { + private GPSManager gpsManager; + + public SensorManager(TrackPointCreator observer) { this.observer = observer; - bluetoothSensorManager = new BluetoothRemoteSensorManager(context, handler, listener); - altitudeSumManager = new AltitudeSumManager(); } public void start(Context context, Handler handler) { + gpsManager = new GPSManager(observer); //TODO Pass listener + gpsManager.start(context, handler); + + bluetoothSensorManager = new BluetoothRemoteSensorManager(context, handler, listener); bluetoothSensorManager.start(context, handler); + + altitudeSumManager = new AltitudeSumManager(); altitudeSumManager.start(context, handler); } @@ -62,6 +69,11 @@ public class SensorManager { altitudeSumManager = null; } + if (gpsManager != null) { + gpsManager.stop(context); + gpsManager = null; + } + sensorDataSet.clear(); } @@ -86,6 +98,10 @@ public class SensorManager { return bluetoothSensorManager; } + public GPSManager getGpsManager() { + return gpsManager; + } + @Deprecated @VisibleForTesting public AltitudeSumManager getAltitudeSumManager() { diff --git a/src/main/java/de/dennisguse/opentracks/services/handlers/GPSManager.java b/src/main/java/de/dennisguse/opentracks/services/handlers/GPSManager.java index 2174b0a54..81611f3ee 100644 --- a/src/main/java/de/dennisguse/opentracks/services/handlers/GPSManager.java +++ b/src/main/java/de/dennisguse/opentracks/services/handlers/GPSManager.java @@ -30,7 +30,7 @@ public class GPSManager implements SensorConnector, LocationListenerCompat, GpsS private final String TAG = GPSManager.class.getSimpleName(); - public static final String LOCATION_PROVIDER = LocationManager.GPS_PROVIDER; + private static final String LOCATION_PROVIDER = LocationManager.GPS_PROVIDER; private final TrackPointCreator trackPointCreator; private Context context; 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 df8b3f120..635313055 100644 --- a/src/main/java/de/dennisguse/opentracks/services/handlers/TrackPointCreator.java +++ b/src/main/java/de/dennisguse/opentracks/services/handlers/TrackPointCreator.java @@ -18,6 +18,7 @@ import de.dennisguse.opentracks.data.models.Distance; import de.dennisguse.opentracks.data.models.TrackPoint; import de.dennisguse.opentracks.sensors.SensorManager; import de.dennisguse.opentracks.sensors.sensorData.SensorDataSet; +import de.dennisguse.opentracks.settings.PreferencesUtils; /** * Creates TrackPoints while recording by fusing data from different sensors (e.g., GNSS, barometer, BLE sensors). @@ -32,26 +33,21 @@ public class TrackPointCreator { @NonNull private Clock clock = new MonotonicClock(); - - private final GPSManager gpsManager; private SensorManager sensorManager; public TrackPointCreator(Callback service, Context context, Handler handler) { this.service = service; - this.gpsManager = new GPSManager(this); - this.sensorManager = new SensorManager(context, handler, this); + this.sensorManager = new SensorManager(this); } @VisibleForTesting - TrackPointCreator(GPSManager gpsManager, Callback service) { + TrackPointCreator(Callback service) { this.service = service; - this.gpsManager = gpsManager; } public synchronized void start(@NonNull Context context, @NonNull Handler handler) { this.context = context; - gpsManager.start(context, handler); sensorManager.start(context, handler); } @@ -73,8 +69,7 @@ public class TrackPointCreator { } public synchronized void stop() { - gpsManager.stop(context); - + sensorManager.stop(context); this.context = null; } @@ -93,7 +88,7 @@ public class TrackPointCreator { public void onNewTrackPoint(@NonNull TrackPoint trackPoint) { addSensorData(trackPoint); - boolean stored = service.newTrackPoint(trackPoint, gpsManager.getThresholdHorizontalAccuracy()); + boolean stored = service.newTrackPoint(trackPoint, PreferencesUtils.getThresholdHorizontalAccuracy()); //TODO Cache preference for performance if (stored) { reset(); } @@ -151,16 +146,6 @@ public class TrackPointCreator { this.clock = Clock.fixed(Instant.parse(time), ZoneId.of("CET")); } - @VisibleForTesting - public void setClock(@NonNull Clock clock) { - this.clock = clock; - } - - @VisibleForTesting - public GPSManager getGpsHandler() { - return gpsManager; - } - void sendGpsStatus(GpsStatusValue gpsStatusValue) { service.newGpsStatus(gpsStatusValue); } From 80d968dd0fded9b9d8154f6c7508b028c67a4191 Mon Sep 17 00:00:00 2001 From: Dennis Guse Date: Sun, 4 Jun 2023 09:35:54 +0200 Subject: [PATCH 7/7] Cleanup. --- ...ckRecordingServiceNotificationManager.java | 25 +------------------ 1 file changed, 1 insertion(+), 24 deletions(-) diff --git a/src/main/java/de/dennisguse/opentracks/services/TrackRecordingServiceNotificationManager.java b/src/main/java/de/dennisguse/opentracks/services/TrackRecordingServiceNotificationManager.java index 4d31df235..7ed36d078 100644 --- a/src/main/java/de/dennisguse/opentracks/services/TrackRecordingServiceNotificationManager.java +++ b/src/main/java/de/dennisguse/opentracks/services/TrackRecordingServiceNotificationManager.java @@ -9,19 +9,15 @@ import android.content.Intent; import android.content.SharedPreferences; import android.os.Build; -import androidx.annotation.NonNull; import androidx.annotation.VisibleForTesting; import androidx.core.app.NotificationCompat; import androidx.core.app.TaskStackBuilder; import de.dennisguse.opentracks.R; import de.dennisguse.opentracks.TrackListActivity; -import de.dennisguse.opentracks.TrackRecordedActivity; -import de.dennisguse.opentracks.TrackRecordingActivity; import de.dennisguse.opentracks.data.models.Distance; import de.dennisguse.opentracks.data.models.DistanceFormatter; import de.dennisguse.opentracks.data.models.SpeedFormatter; -import de.dennisguse.opentracks.data.models.Track; import de.dennisguse.opentracks.data.models.TrackPoint; import de.dennisguse.opentracks.settings.PreferencesUtils; import de.dennisguse.opentracks.settings.UnitSystem; @@ -89,6 +85,7 @@ class TrackRecordingServiceNotificationManager implements SharedPreferences.OnSh DistanceFormatter formatter = DistanceFormatter.Builder() .setUnit(unitSystem) .build(context); + // The decision if a trackpoint is not accurate enough should happen in the sensor subsystem. if (trackPoint.hasHorizontalAccuracy()) { formattedAccuracy = formatter.formatDistance(trackPoint.getHorizontalAccuracy()); @@ -107,26 +104,6 @@ class TrackRecordingServiceNotificationManager implements SharedPreferences.OnSh notificationBuilder.setOnlyAlertOnce(true); } - Notification setRecording(Context context, @NonNull Track.Id trackId) { - Intent intent = IntentUtils.newIntent(context, TrackRecordingActivity.class) - .putExtra(TrackRecordedActivity.EXTRA_TRACK_ID, trackId); - int pendingIntentFlags = PendingIntent.FLAG_UPDATE_CURRENT; - if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.M) { - pendingIntentFlags |= PendingIntent.FLAG_IMMUTABLE; - } - - PendingIntent pendingIntent = TaskStackBuilder.create(context) - .addNextIntentWithParentStack(intent) - .getPendingIntent(0, pendingIntentFlags); - - updateContent(context.getString(R.string.gps_starting)); - - notificationBuilder.setContentIntent(pendingIntent); - updateNotification(); - - return getNotification(); - } - Notification setGPSonlyStarted(Context context) { Intent intent = IntentUtils.newIntent(context, TrackListActivity.class);