From 490141db2a24b71d4e4674f383a82c95850e6dfb Mon Sep 17 00:00:00 2001 From: Dennis Guse Date: Wed, 22 Nov 2023 20:24:12 +0100 Subject: [PATCH 1/4] Barometer: added settings for sensor selection. Part of #1424. --- .../BluetoothHandlerBarometricPressure.java | 8 ++- .../opentracks/sensors/GainManager.java | 4 +- .../sensorData/AggregatorBarometer.java | 4 +- .../opentracks/settings/PreferencesUtils.java | 12 +++++ .../BluetoothLeCyclingPowerPreference.java | 4 +- .../BluetoothLeCyclingSpeedPreference.java | 4 +- .../BluetoothLePressurePreference.java | 41 ++++++++++++++++ ...othLeRunningSpeedAndCadencePreference.java | 4 +- .../BluetoothLeSensorPreference.java | 49 ++++++++++++------- src/main/res/values/settings.xml | 2 + src/main/res/values/strings.xml | 2 + src/main/res/xml/settings_sensors.xml | 4 ++ 12 files changed, 111 insertions(+), 27 deletions(-) create mode 100644 src/main/java/de/dennisguse/opentracks/settings/bluetooth/BluetoothLePressurePreference.java diff --git a/src/main/java/de/dennisguse/opentracks/sensors/BluetoothHandlerBarometricPressure.java b/src/main/java/de/dennisguse/opentracks/sensors/BluetoothHandlerBarometricPressure.java index ef7aeba37..1875b5a69 100644 --- a/src/main/java/de/dennisguse/opentracks/sensors/BluetoothHandlerBarometricPressure.java +++ b/src/main/java/de/dennisguse/opentracks/sensors/BluetoothHandlerBarometricPressure.java @@ -7,6 +7,8 @@ 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; public class BluetoothHandlerBarometricPressure implements SensorHandlerInterface { @@ -23,12 +25,14 @@ public class BluetoothHandlerBarometricPressure implements SensorHandlerInterfac @Override public Aggregator createEmptySensorData(String address, String name) { - return null; //TODO + return new AggregatorBarometer(address, name); } @Override public void handlePayload(SensorManager.SensorDataChangedObserver observer, ServiceMeasurementUUID serviceMeasurementUUID, String sensorName, String address, BluetoothGattCharacteristic characteristic) { - //TODO + AtmosphericPressure value = parseEnvironmentalSensing(characteristic); + if (value == null) return; + observer.onChange(new Raw<>(value)); } public static AtmosphericPressure parseEnvironmentalSensing(BluetoothGattCharacteristic characteristic) { diff --git a/src/main/java/de/dennisguse/opentracks/sensors/GainManager.java b/src/main/java/de/dennisguse/opentracks/sensors/GainManager.java index 40e1749f9..613502832 100644 --- a/src/main/java/de/dennisguse/opentracks/sensors/GainManager.java +++ b/src/main/java/de/dennisguse/opentracks/sensors/GainManager.java @@ -29,7 +29,7 @@ public class GainManager implements SensorConnector { driver.connect(context, handler, this); if (driver.isConnected()) { - listener.onConnect(new AggregatorBarometer("internal")); + listener.onConnect(new AggregatorBarometer("internal", null)); } } @@ -37,7 +37,7 @@ public class GainManager implements SensorConnector { Log.d(TAG, "Stop"); driver.disconnect(context); - listener.onDisconnect(new AggregatorBarometer("internal")); + listener.onDisconnect(new AggregatorBarometer("internal", null)); } public void onSensorValueChanged(AtmosphericPressure currentSensorValue) { 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 7e5ed7c57..06f9fbb7e 100644 --- a/src/main/java/de/dennisguse/opentracks/sensors/sensorData/AggregatorBarometer.java +++ b/src/main/java/de/dennisguse/opentracks/sensors/sensorData/AggregatorBarometer.java @@ -11,8 +11,8 @@ public class AggregatorBarometer extends Aggregator sensorUUIDs) { + return newInstance(preferenceKey, sensorUUIDs, false); } - public static BluetoothLeSensorPreferenceDialog newInstance(String preferenceKey, List sensorUUIDs) { + public static BluetoothLeSensorPreferenceDialog newInstance(String preferenceKey, List sensorUUIDs, boolean includeInternalSensor) { final BluetoothLeSensorPreferenceDialog fragment = new BluetoothLeSensorPreferenceDialog(); - final Bundle b = new Bundle(1); + final Bundle b = new Bundle(3); b.putString(ARG_KEY, preferenceKey); b.putParcelableArrayList(ARG_BLE_SERVICE_UUIDS, new ArrayList<>(sensorUUIDs.stream() .map(ServiceMeasurementUUID::serviceUUID) .map(ParcelUuid::new) .collect(Collectors.toList()))); + b.putBoolean(ARG_INCLUDE_INTERNAL, includeInternalSensor); + fragment.setArguments(b); return fragment; } @@ -179,6 +182,7 @@ public abstract class BluetoothLeSensorPreference extends DialogPreference { private void startBluetoothScan() { List serviceUUIDs = getArguments().getParcelableArrayList(ARG_BLE_SERVICE_UUIDS); + boolean includeInternalSensor = getArguments().getBoolean(ARG_INCLUDE_INTERNAL); BluetoothAdapter bluetoothAdapter = BluetoothUtils.getAdapter(getContext()); if (bluetoothAdapter == null || !bluetoothAdapter.isEnabled()) { @@ -201,14 +205,23 @@ public abstract class BluetoothLeSensorPreference extends DialogPreference { } String deviceNone = getContext().getString(R.string.sensor_type_value_none); + String sensorInternal = getString(R.string.sensor_type_value_internal); + listAdapter.add(getContext().getString(DEVICE_NONE_RESOURCEID), deviceNone); selectedEntryIndex = 0; BluetoothLeSensorPreference preference = (BluetoothLeSensorPreference) getPreference(); - String deviceSelected = preference.getValue(); - if (deviceSelected != null && !deviceNone.equals(deviceSelected)) { - listAdapter.add(preference.getValue(), preference.getValue()); - selectedEntryIndex = 1; + String deviceSelected = preference.value; + if (includeInternalSensor) { + listAdapter.add(getString(SENSOR_INTERNAL_RESOURCEID), sensorInternal); + if (sensorInternal.equals(deviceSelected)) { + selectedEntryIndex = 1; + } + } + + if (deviceSelected != null && !deviceNone.equals(deviceSelected) && !sensorInternal.equals(deviceSelected)) { + listAdapter.add(preference.value, preference.value); + selectedEntryIndex++; } List scanFilter = null; diff --git a/src/main/res/values/settings.xml b/src/main/res/values/settings.xml index f66ad622a..97e18a979 100644 --- a/src/main/res/values/settings.xml +++ b/src/main/res/values/settings.xml @@ -32,7 +32,9 @@ bluetoothCyclingSpeedSensor bluetoothCyclingPowerSensor bluetoothRunningSpeedAndCadenceSensor + bluetoothPressureSensor NONE + INTERNAL bluetoothCyclingSpeedWheelCircumference 2135 diff --git a/src/main/res/values/strings.xml b/src/main/res/values/strings.xml index 02bdf8955..44ffd5fc6 100644 --- a/src/main/res/values/strings.xml +++ b/src/main/res/values/strings.xml @@ -331,6 +331,7 @@ limitations under the License. bpm %1$d bpm Power + Barometer Avg Power Max Power W @@ -559,6 +560,7 @@ limitations under the License. %1$d s None Off + Internal sensor Smallest (recommended) %1$d s (recommended) Becoming idle. diff --git a/src/main/res/xml/settings_sensors.xml b/src/main/res/xml/settings_sensors.xml index 564a3e3f3..f72adcdaa 100644 --- a/src/main/res/xml/settings_sensors.xml +++ b/src/main/res/xml/settings_sensors.xml @@ -15,6 +15,10 @@ android:key="@string/settings_sensor_heart_rate_max_key" android:title="@string/settings_sensor_heart_rate_max" app:useSimpleSummaryProvider="true" /> + From 94540606ef3fc58244c07eecfb4153ad0aed725c Mon Sep 17 00:00:00 2001 From: Dennis Guse Date: Wed, 27 Dec 2023 18:43:47 +0100 Subject: [PATCH 2/4] Barometer: select INTERNAL or NONE (also while recording). Part of #1424. --- .../io/file/importer/ExportImportTest.java | 2 +- .../sensorData/AggregatorBarometerTest.java | 2 +- .../sensors/BluetoothConnectionManager.java | 9 ++-- .../sensors/BluetoothRemoteSensorManager.java | 2 +- .../opentracks/sensors/GainManager.java | 54 ++++++++++++++++--- .../opentracks/sensors/SensorType.java | 17 ++++++ .../opentracks/settings/PreferencesUtils.java | 16 +++--- .../BluetoothLePressurePreference.java | 7 ++- .../BluetoothLeSensorPreference.java | 5 +- 9 files changed, 86 insertions(+), 28 deletions(-) create mode 100644 src/main/java/de/dennisguse/opentracks/sensors/SensorType.java diff --git a/src/androidTest/java/de/dennisguse/opentracks/io/file/importer/ExportImportTest.java b/src/androidTest/java/de/dennisguse/opentracks/io/file/importer/ExportImportTest.java index 97b61dda0..9669a2cea 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 @@ -586,7 +586,7 @@ public class ExportImportTest { Mockito.when(barometer.getValue()).thenReturn(new AltitudeGainLoss(altitudeGain, altitudeGain)); sensorDataSet.add(barometer); } else { - sensorDataSet.add(new AggregatorBarometer("")); + sensorDataSet.add(new AggregatorBarometer("test", null)); } } 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 5ef2b51c6..88db170fc 100644 --- a/src/androidTest/java/de/dennisguse/opentracks/sensors/sensorData/AggregatorBarometerTest.java +++ b/src/androidTest/java/de/dennisguse/opentracks/sensors/sensorData/AggregatorBarometerTest.java @@ -16,7 +16,7 @@ public class AggregatorBarometerTest { @Test public void getAltitudeGainLoss_downhill() { // given - AggregatorBarometer subject = new AggregatorBarometer(""); + AggregatorBarometer subject = new AggregatorBarometer("test", null); // 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}); diff --git a/src/main/java/de/dennisguse/opentracks/sensors/BluetoothConnectionManager.java b/src/main/java/de/dennisguse/opentracks/sensors/BluetoothConnectionManager.java index 2c0cf74f0..c8660f111 100644 --- a/src/main/java/de/dennisguse/opentracks/sensors/BluetoothConnectionManager.java +++ b/src/main/java/de/dennisguse/opentracks/sensors/BluetoothConnectionManager.java @@ -101,9 +101,12 @@ public class BluetoothConnectionManager { return; } - descriptor.setValue(BluetoothGattDescriptor.ENABLE_NOTIFICATION_VALUE); - gatt.writeDescriptor(descriptor); - + if (!descriptor.setValue(BluetoothGattDescriptor.ENABLE_NOTIFICATION_VALUE)) { + Log.e(TAG, "CLIENT_CHARACTERISTIC_CONFIG_UUID could not be set to ENABLE_NOTIFICATION_VALUE"); + } + if (!gatt.writeDescriptor(descriptor)) { + Log.e(TAG, "CLIENT_CHARACTERISTIC_CONFIG_UUID descriptor could not be written"); + } } @Override diff --git a/src/main/java/de/dennisguse/opentracks/sensors/BluetoothRemoteSensorManager.java b/src/main/java/de/dennisguse/opentracks/sensors/BluetoothRemoteSensorManager.java index 28f65f304..9a4bc0848 100644 --- a/src/main/java/de/dennisguse/opentracks/sensors/BluetoothRemoteSensorManager.java +++ b/src/main/java/de/dennisguse/opentracks/sensors/BluetoothRemoteSensorManager.java @@ -105,7 +105,7 @@ public class BluetoothRemoteSensorManager implements SensorConnector, SharedPref return; } - if (PreferencesUtils.isBluetoothSensorAddressNone(address)) { + if (SensorType.NONE.getPreferenceValue().equals(address)) { Log.w(TAG, "No Bluetooth address."); connectionManager.disconnect(); return; diff --git a/src/main/java/de/dennisguse/opentracks/sensors/GainManager.java b/src/main/java/de/dennisguse/opentracks/sensors/GainManager.java index 613502832..df7fed010 100644 --- a/src/main/java/de/dennisguse/opentracks/sensors/GainManager.java +++ b/src/main/java/de/dennisguse/opentracks/sensors/GainManager.java @@ -1,13 +1,17 @@ package de.dennisguse.opentracks.sensors; import android.content.Context; +import android.content.SharedPreferences; import android.os.Handler; import android.util.Log; +import androidx.annotation.NonNull; + import de.dennisguse.opentracks.data.models.AtmosphericPressure; import de.dennisguse.opentracks.sensors.driver.BarometerInternal; import de.dennisguse.opentracks.sensors.sensorData.AggregatorBarometer; import de.dennisguse.opentracks.sensors.sensorData.Raw; +import de.dennisguse.opentracks.settings.PreferencesUtils; /** * Estimates the altitude gain and altitude loss using the device's pressure sensor (i.e., barometer). @@ -16,31 +20,65 @@ public class GainManager implements SensorConnector { private static final String TAG = GainManager.class.getSimpleName(); - private final BarometerInternal driver; + private BarometerInternal driver; private final SensorManager.SensorDataChangedObserver listener; + private final SharedPreferences.OnSharedPreferenceChangeListener sharedPreferenceChangeListener = (sharedPreferences, key) -> { + connect(); + }; + + private Context context; + private Handler handler; + public GainManager(SensorManager.SensorDataChangedObserver listener) { this.listener = listener; driver = new BarometerInternal(); } public void start(Context context, Handler handler) { - driver.connect(context, handler, this); - - if (driver.isConnected()) { - listener.onConnect(new AggregatorBarometer("internal", null)); - } + 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); - driver.disconnect(context); - listener.onDisconnect(new AggregatorBarometer("internal", null)); + onDisconnect(context); } public void onSensorValueChanged(AtmosphericPressure currentSensorValue) { listener.onChange(new Raw<>(currentSensorValue)); } + + private void connect() { + onDisconnect(context); + + String address = PreferencesUtils.getBarometerSensorAddress(); + switch (PreferencesUtils.getSensorType(address)) { + case NONE -> driver = null; + case INTERNAL -> driver = new BarometerInternal(); + case REMOTE -> throw new RuntimeException("Not implemented"); //TODO #1424 + default -> throw new RuntimeException("Not implemented"); + } + + if (driver != null) { + driver.connect(context, handler, this); + + if (driver.isConnected()) { + listener.onConnect(new AggregatorBarometer("internal", null)); + } + } + } + + private void onDisconnect(@NonNull Context context) { + if (driver == null) return; + + driver.disconnect(context); + listener.onDisconnect(new AggregatorBarometer("internal", null)); + } } diff --git a/src/main/java/de/dennisguse/opentracks/sensors/SensorType.java b/src/main/java/de/dennisguse/opentracks/sensors/SensorType.java new file mode 100644 index 000000000..ad66d7fce --- /dev/null +++ b/src/main/java/de/dennisguse/opentracks/sensors/SensorType.java @@ -0,0 +1,17 @@ +package de.dennisguse.opentracks.sensors; + +public enum SensorType { + NONE("NONE"), + INTERNAL("INTERNAL"), + REMOTE("*"); + + private final String preferenceValue; + + SensorType(String preferenceValue) { + this.preferenceValue = preferenceValue; + } + + public String getPreferenceValue() { + return preferenceValue; + } +} diff --git a/src/main/java/de/dennisguse/opentracks/settings/PreferencesUtils.java b/src/main/java/de/dennisguse/opentracks/settings/PreferencesUtils.java index 5dd10043e..fe474b4bc 100644 --- a/src/main/java/de/dennisguse/opentracks/settings/PreferencesUtils.java +++ b/src/main/java/de/dennisguse/opentracks/settings/PreferencesUtils.java @@ -48,6 +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.ui.customRecordingLayout.CsvLayoutUtils; import de.dennisguse.opentracks.ui.customRecordingLayout.RecordingLayout; import de.dennisguse.opentracks.ui.customRecordingLayout.RecordingLayoutIO; @@ -249,22 +250,21 @@ public class PreferencesUtils { } private static String getBluetoothSensorAddressNone() { - return resources.getString(R.string.sensor_type_value_none); + return SensorType.NONE.getPreferenceValue(); } public static boolean isBluetoothSensorAddressNone(String currentValue) { return getBluetoothSensorAddressNone().equals(currentValue); } - private static String getBluetoothSensorAddressInternal() { - return resources.getString(R.string.sensor_type_value_internal); + public static SensorType getSensorType(String address) { + return Arrays.stream(SensorType.values()) + .filter(it -> it.getPreferenceValue().equals(address)) + .findFirst() + .orElse(SensorType.REMOTE); } - public static boolean isBluetoothSensorAddressInternal(String currentValue) { - return getBluetoothSensorAddressInternal().equals(currentValue); - } - - public static String getBluetoothPressureAddress() { + public static String getBarometerSensorAddress() { return getString(R.string.settings_sensor_bluetooth_pressure_key, getBluetoothSensorAddressNone()); } 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..8ffb1365a 100644 --- a/src/main/java/de/dennisguse/opentracks/settings/bluetooth/BluetoothLePressurePreference.java +++ b/src/main/java/de/dennisguse/opentracks/settings/bluetooth/BluetoothLePressurePreference.java @@ -7,9 +7,7 @@ import android.util.AttributeSet; import androidx.preference.PreferenceDialogFragmentCompat; -import java.util.List; - -import de.dennisguse.opentracks.sensors.BluetoothHandlerBarometricPressure; +import java.util.Collections; public class BluetoothLePressurePreference extends BluetoothLeSensorPreference { @@ -36,6 +34,7 @@ public class BluetoothLePressurePreference extends BluetoothLeSensorPreference { boolean hasInternalSensor = pressureSensor != null; return BluetoothLeSensorPreference.BluetoothLeSensorPreferenceDialog - .newInstance(getKey(), List.of(BluetoothHandlerBarometricPressure.BAROMETRIC_PRESSURE), hasInternalSensor); +// .newInstance(getKey(), List.of(BluetoothHandlerBarometricPressure.BAROMETRIC_PRESSURE), hasInternalSensor); //TODO #1424 + .newInstance(getKey(), Collections.emptyList(), hasInternalSensor); } } \ No newline at end of file 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 ebf49ef9a..2b7bf3816 100644 --- a/src/main/java/de/dennisguse/opentracks/settings/bluetooth/BluetoothLeSensorPreference.java +++ b/src/main/java/de/dennisguse/opentracks/settings/bluetooth/BluetoothLeSensorPreference.java @@ -28,6 +28,7 @@ import java.util.stream.Collectors; 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.settings.PreferencesUtils; import de.dennisguse.opentracks.util.PermissionRequester; @@ -86,10 +87,10 @@ public abstract class BluetoothLeSensorPreference extends DialogPreference { @Override public CharSequence getSummary() { - if (value == null || PreferencesUtils.isBluetoothSensorAddressNone(value)) { + if (value == null || SensorType.NONE.getPreferenceValue().equals(value)) { return getContext().getString(DEVICE_NONE_RESOURCEID); } - if (PreferencesUtils.isBluetoothSensorAddressInternal(value)) { + if (SensorType.INTERNAL.getPreferenceValue().equals(value)) { return getContext().getString(SENSOR_INTERNAL_RESOURCEID); } From 8eed8e9d8d24c9d2084ab71775c8e74a72d21c05 Mon Sep 17 00:00:00 2001 From: Dennis Guse Date: Thu, 28 Dec 2023 14:30:28 +0100 Subject: [PATCH 3/4] Bugfix: Bluetooth sensor data TLS is 5s instead of 50s. Introduced in 58d79380cf18074827d5783daeaca0b019eb7ba5 --- .../opentracks/sensors/BluetoothRemoteSensorManager.java | 9 +-------- 1 file changed, 1 insertion(+), 8 deletions(-) diff --git a/src/main/java/de/dennisguse/opentracks/sensors/BluetoothRemoteSensorManager.java b/src/main/java/de/dennisguse/opentracks/sensors/BluetoothRemoteSensorManager.java index 9a4bc0848..2ff1434c7 100644 --- a/src/main/java/de/dennisguse/opentracks/sensors/BluetoothRemoteSensorManager.java +++ b/src/main/java/de/dennisguse/opentracks/sensors/BluetoothRemoteSensorManager.java @@ -36,21 +36,14 @@ import de.dennisguse.opentracks.util.PermissionRequester; * Bluetooth LE sensor manager: manages connections to Bluetooth LE sensors. *

* Note: should only be instantiated once. - *

* TODO: listen for Bluetooth enabled/disabled events. - *

- * TODO: In case, a cycling (Cadence and Speed) sensor reports both values, testing is required. - * We establish two GATT separate GATT connections (as if two different sensors were used). - * However, it is not clear if this is allowed. - * Even if this works, it is not clear what happens if a user (while recording) changes one of the sensors in the settings as this will trigger a disconnect of one GATT. - * * @author Sandor Dornbush */ public class BluetoothRemoteSensorManager implements SensorConnector, SharedPreferences.OnSharedPreferenceChangeListener { private static final String TAG = BluetoothRemoteSensorManager.class.getSimpleName(); - public static final Duration MAX_SENSOR_DATE_SET_AGE = Duration.ofSeconds(50); //TODO + public static final Duration MAX_SENSOR_DATE_SET_AGE = Duration.ofSeconds(5); private final BluetoothAdapter bluetoothAdapter; private final Context context; From 03dd0c2308be495e4b9d4f722d61e4540ed8c566 Mon Sep 17 00:00:00 2001 From: Dennis Guse Date: Thu, 28 Dec 2023 15:10:44 +0100 Subject: [PATCH 4/4] Implement gain/loss via Bluetooth LE Environmental Sensing Sensor. Fixes #1424. --- .../sensors/BluetoothConnectionManager.java | 59 ++++++++++++++++--- .../BluetoothHandlerBarometricPressure.java | 7 +++ .../sensors/BluetoothRemoteSensorManager.java | 50 +++------------- .../opentracks/sensors/GainManager.java | 37 +++++------- .../sensors/driver/BarometerInternal.java | 53 +++++++++++------ .../opentracks/sensors/driver/Driver.java | 12 ++++ .../sensors/sensorData/SensorDataSet.java | 4 -- .../opentracks/settings/PreferencesUtils.java | 4 -- .../bluetooth/BluetoothLeAdapter.java | 50 ++++++++++------ .../BluetoothLePressurePreference.java | 7 ++- .../BluetoothLeSensorPreference.java | 21 +++---- 11 files changed, 170 insertions(+), 134 deletions(-) create mode 100644 src/main/java/de/dennisguse/opentracks/sensors/driver/Driver.java diff --git a/src/main/java/de/dennisguse/opentracks/sensors/BluetoothConnectionManager.java b/src/main/java/de/dennisguse/opentracks/sensors/BluetoothConnectionManager.java index c8660f111..c3aabdc95 100644 --- a/src/main/java/de/dennisguse/opentracks/sensors/BluetoothConnectionManager.java +++ b/src/main/java/de/dennisguse/opentracks/sensors/BluetoothConnectionManager.java @@ -17,6 +17,7 @@ package de.dennisguse.opentracks.sensors; import android.annotation.SuppressLint; +import android.bluetooth.BluetoothAdapter; import android.bluetooth.BluetoothDevice; import android.bluetooth.BluetoothGatt; import android.bluetooth.BluetoothGattCallback; @@ -33,19 +34,22 @@ import androidx.annotation.NonNull; import java.util.Optional; import java.util.UUID; +import de.dennisguse.opentracks.sensors.driver.Driver; import de.dennisguse.opentracks.sensors.sensorData.SensorHandlerInterface; /** * Manages connection to a Bluetooth LE sensor and subscribes for onChange-notifications. */ @SuppressLint("MissingPermission") -public class BluetoothConnectionManager { +public class BluetoothConnectionManager implements Driver { private static final String TAG = BluetoothConnectionManager.class.getSimpleName(); private final SensorManager.SensorDataChangedObserver observer; private final SensorHandlerInterface sensorHandler; + + private final BluetoothAdapter bluetoothAdapter; private BluetoothGatt bluetoothGatt; private final BluetoothGattCallback connectCallback = new BluetoothGattCallback() { @Override @@ -127,17 +131,46 @@ public class BluetoothConnectionManager { } }; - BluetoothConnectionManager(SensorManager.SensorDataChangedObserver observer, SensorHandlerInterface sensorHandler) { + BluetoothConnectionManager(BluetoothAdapter bluetoothAdapter, SensorManager.SensorDataChangedObserver observer, SensorHandlerInterface sensorHandler) { + this.bluetoothAdapter = bluetoothAdapter; this.observer = observer; this.sensorHandler = sensorHandler; } - synchronized void connect(Context context, Handler handler, @NonNull BluetoothDevice device) { - if (bluetoothGatt != null) { + @Override + public synchronized void connect(Context context, Handler handler, @NonNull String address) { + if (!isBluetoothEnabled()) { + Log.w(TAG, "Bluetooth not enabled."); + return; + } + + if (SensorType.NONE.getPreferenceValue().equals(address)) { + Log.w(TAG, "NONE: going to disconnect"); + if (isConnected()) { + disconnect(); + } + return; + } + + if (isConnected()) { Log.w(TAG, "Already connected; ignoring."); return; } + if (isSameBluetoothDevice(address)) { + return; + } else { + disconnect(); + } + + BluetoothDevice device; + try { + device = bluetoothAdapter.getRemoteDevice(address); + } catch (IllegalArgumentException e) { + Log.e(TAG, address + ": Unable to get remote device for", e); + return; + } + Log.d(TAG, device + ": trying to connect"); bluetoothGatt = device.connectGatt(context, false, connectCallback, BluetoothDevice.TRANSPORT_AUTO, 0, handler); @@ -149,8 +182,10 @@ public class BluetoothConnectionManager { observer.onDisconnect(sensorHandler.createEmptySensorData(bluetoothGatt.getDevice().getAddress(), bluetoothGatt.getDevice().getName())); } - synchronized void disconnect() { - if (bluetoothGatt == null) { + @Override + public synchronized void disconnect() { + if (!isConnected()) { + Log.w(TAG, "Not connected; no need to re-connect."); return; } Log.i(TAG, bluetoothGatt.getDevice() + ": start disconnect"); @@ -161,11 +196,21 @@ public class BluetoothConnectionManager { bluetoothGatt = null; } - synchronized boolean isSameBluetoothDevice(String address) { + 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(); + } + + @Override + public boolean isConnected() { + return bluetoothGatt != null; + } } diff --git a/src/main/java/de/dennisguse/opentracks/sensors/BluetoothHandlerBarometricPressure.java b/src/main/java/de/dennisguse/opentracks/sensors/BluetoothHandlerBarometricPressure.java index 1875b5a69..299fa4a8b 100644 --- a/src/main/java/de/dennisguse/opentracks/sensors/BluetoothHandlerBarometricPressure.java +++ b/src/main/java/de/dennisguse/opentracks/sensors/BluetoothHandlerBarometricPressure.java @@ -1,6 +1,7 @@ package de.dennisguse.opentracks.sensors; import android.bluetooth.BluetoothGattCharacteristic; +import android.util.Log; import java.util.List; import java.util.UUID; @@ -32,9 +33,15 @@ public class BluetoothHandlerBarometricPressure implements SensorHandlerInterfac public void handlePayload(SensorManager.SensorDataChangedObserver observer, ServiceMeasurementUUID serviceMeasurementUUID, String sensorName, String address, BluetoothGattCharacteristic characteristic) { AtmosphericPressure value = parseEnvironmentalSensing(characteristic); if (value == null) return; + Log.i("ZXCV", "VALUE: " + value); observer.onChange(new Raw<>(value)); } + /** + * Decoding: + * org.bluetooth.service.environmental_sensing.xml + * org.bluetooth.characteristic.pressure.xml + */ public static AtmosphericPressure parseEnvironmentalSensing(BluetoothGattCharacteristic characteristic) { byte[] raw = characteristic.getValue(); diff --git a/src/main/java/de/dennisguse/opentracks/sensors/BluetoothRemoteSensorManager.java b/src/main/java/de/dennisguse/opentracks/sensors/BluetoothRemoteSensorManager.java index 2ff1434c7..8f919fca2 100644 --- a/src/main/java/de/dennisguse/opentracks/sensors/BluetoothRemoteSensorManager.java +++ b/src/main/java/de/dennisguse/opentracks/sensors/BluetoothRemoteSensorManager.java @@ -17,11 +17,9 @@ package de.dennisguse.opentracks.sensors; import android.bluetooth.BluetoothAdapter; -import android.bluetooth.BluetoothDevice; import android.content.Context; import android.content.SharedPreferences; import android.os.Handler; -import android.util.Log; import androidx.annotation.NonNull; import androidx.annotation.Nullable; @@ -30,7 +28,6 @@ import java.time.Duration; import de.dennisguse.opentracks.R; import de.dennisguse.opentracks.settings.PreferencesUtils; -import de.dennisguse.opentracks.util.PermissionRequester; /** * Bluetooth LE sensor manager: manages connections to Bluetooth LE sensors. @@ -45,7 +42,6 @@ public class BluetoothRemoteSensorManager implements SensorConnector, SharedPref public static final Duration MAX_SENSOR_DATE_SET_AGE = Duration.ofSeconds(5); - private final BluetoothAdapter bluetoothAdapter; private final Context context; private final Handler handler; private boolean started = false; @@ -59,14 +55,13 @@ public class BluetoothRemoteSensorManager implements SensorConnector, SharedPref public BluetoothRemoteSensorManager(@NonNull Context context, @NonNull Handler handler, @Nullable SensorManager.SensorDataChangedObserver observer) { this.context = context; this.handler = handler; - bluetoothAdapter = BluetoothUtils.getAdapter(context); - - this.heartRate = new BluetoothConnectionManager(observer, new BluetoothHandlerManagerHeartRate()); - this.cyclingCadence = new BluetoothConnectionManager(observer, new BluetoothHandlerCyclingCadence()); - this.cyclingSpeed = new BluetoothConnectionManager(observer, new BluetoothHandlerCyclingDistanceSpeed()); - this.cyclingPower = new BluetoothConnectionManager(observer, new BluetoothHandlerManagerCyclingPower()); - this.runningSpeedAndCadence = new BluetoothConnectionManager(observer, new BluetoothHandlerRunningSpeedAndCadence()); + BluetoothAdapter bluetoothAdapter = BluetoothUtils.getAdapter(context); + this.heartRate = new BluetoothConnectionManager(bluetoothAdapter, observer, new BluetoothHandlerManagerHeartRate()); + this.cyclingCadence = new BluetoothConnectionManager(bluetoothAdapter, observer, new BluetoothHandlerCyclingCadence()); + this.cyclingSpeed = new BluetoothConnectionManager(bluetoothAdapter, observer, new BluetoothHandlerCyclingDistanceSpeed()); + this.cyclingPower = new BluetoothConnectionManager(bluetoothAdapter, observer, new BluetoothHandlerManagerCyclingPower()); + this.runningSpeedAndCadence = new BluetoothConnectionManager(bluetoothAdapter, observer, new BluetoothHandlerRunningSpeedAndCadence()); } @Override @@ -88,39 +83,8 @@ public class BluetoothRemoteSensorManager implements SensorConnector, SharedPref started = false; } - public boolean isEnabled() { - return bluetoothAdapter != null && bluetoothAdapter.isEnabled(); - } - private synchronized void connect(BluetoothConnectionManager connectionManager, String address) { - if (!isEnabled()) { - Log.w(TAG, "Bluetooth not enabled."); - return; - } - - if (SensorType.NONE.getPreferenceValue().equals(address)) { - Log.w(TAG, "No Bluetooth address."); - connectionManager.disconnect(); - return; - } - - // Check if there is an ongoing connection; if yes, check if the address changed. - if (connectionManager.isSameBluetoothDevice(address)) { - return; - } else { - connectionManager.disconnect(); - } - if (!PermissionRequester.BLUETOOTH.hasPermission(context)) { - Log.w(TAG, "BLUETOOTH_SCAN and/or BLUETOOTH_CONNECT not granted; not connecting."); - } - - Log.i(TAG, "Connecting to bluetooth address: " + address); - try { - BluetoothDevice device = bluetoothAdapter.getRemoteDevice(address); - connectionManager.connect(context, handler, device); - } catch (IllegalArgumentException e) { - Log.e(TAG, "Unable to get remote device for: " + address, e); - } + connectionManager.connect(context, handler, address); } @Override diff --git a/src/main/java/de/dennisguse/opentracks/sensors/GainManager.java b/src/main/java/de/dennisguse/opentracks/sensors/GainManager.java index df7fed010..6dca516d4 100644 --- a/src/main/java/de/dennisguse/opentracks/sensors/GainManager.java +++ b/src/main/java/de/dennisguse/opentracks/sensors/GainManager.java @@ -5,12 +5,9 @@ import android.content.SharedPreferences; import android.os.Handler; import android.util.Log; -import androidx.annotation.NonNull; - -import de.dennisguse.opentracks.data.models.AtmosphericPressure; 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.sensors.sensorData.Raw; import de.dennisguse.opentracks.settings.PreferencesUtils; /** @@ -20,7 +17,6 @@ public class GainManager implements SensorConnector { private static final String TAG = GainManager.class.getSimpleName(); - private BarometerInternal driver; private final SensorManager.SensorDataChangedObserver listener; @@ -30,10 +26,10 @@ public class GainManager implements SensorConnector { private Context context; private Handler handler; + private Driver driver; public GainManager(SensorManager.SensorDataChangedObserver listener) { this.listener = listener; - driver = new BarometerInternal(); } public void start(Context context, Handler handler) { @@ -48,37 +44,34 @@ public class GainManager implements SensorConnector { this.handler = null; PreferencesUtils.unregisterOnSharedPreferenceChangeListener(sharedPreferenceChangeListener); - onDisconnect(context); - } - - public void onSensorValueChanged(AtmosphericPressure currentSensorValue) { - listener.onChange(new Raw<>(currentSensorValue)); + onDisconnect(); } private void connect() { - onDisconnect(context); + onDisconnect(); String address = PreferencesUtils.getBarometerSensorAddress(); switch (PreferencesUtils.getSensorType(address)) { case NONE -> driver = null; - case INTERNAL -> driver = new BarometerInternal(); - case REMOTE -> throw new RuntimeException("Not implemented"); //TODO #1424 + case INTERNAL -> driver = new BarometerInternal(listener); + case REMOTE -> driver = + new BluetoothConnectionManager( + BluetoothUtils.getAdapter(context), + listener, + new BluetoothHandlerBarometricPressure() + ); default -> throw new RuntimeException("Not implemented"); } if (driver != null) { - driver.connect(context, handler, this); - - if (driver.isConnected()) { - listener.onConnect(new AggregatorBarometer("internal", null)); - } + driver.connect(context, handler, address); } } - private void onDisconnect(@NonNull Context context) { + private void onDisconnect() { if (driver == null) return; - driver.disconnect(context); - listener.onDisconnect(new AggregatorBarometer("internal", null)); + driver.disconnect(); + listener.onDisconnect(new AggregatorBarometer("GainManager", null)); } } 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 9032010a5..74ef061f8 100644 --- a/src/main/java/de/dennisguse/opentracks/sensors/driver/BarometerInternal.java +++ b/src/main/java/de/dennisguse/opentracks/sensors/driver/BarometerInternal.java @@ -4,24 +4,29 @@ 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 androidx.annotation.NonNull; + import java.util.concurrent.TimeUnit; import de.dennisguse.opentracks.data.models.AtmosphericPressure; -import de.dennisguse.opentracks.sensors.GainManager; +import de.dennisguse.opentracks.sensors.SensorManager; +import de.dennisguse.opentracks.sensors.sensorData.AggregatorBarometer; +import de.dennisguse.opentracks.sensors.sensorData.Raw; -public class BarometerInternal { +public class BarometerInternal implements Driver { private static final String TAG = BarometerInternal.class.getSimpleName(); private static final int SAMPLING_PERIOD = (int) TimeUnit.SECONDS.toMicros(5); - private GainManager observer; + private final SensorManager.SensorDataChangedObserver listener; - private final SensorEventListener listener = new SensorEventListener() { + private Context context; + + private final SensorEventListener sensorEventListener = new SensorEventListener() { @Override public void onSensorChanged(SensorEvent event) { if (!isConnected()) { @@ -29,7 +34,7 @@ public class BarometerInternal { return; } - observer.onSensorValueChanged(AtmosphericPressure.ofHPA(event.values[0])); + listener.onChange(new Raw<>(AtmosphericPressure.ofHPA(event.values[0]))); } @Override @@ -38,30 +43,40 @@ public class BarometerInternal { } }; - public void connect(Context context, Handler handler, GainManager observer) { - SensorManager sensorManager = (SensorManager) context.getSystemService(Context.SENSOR_SERVICE); + public BarometerInternal(@NonNull SensorManager.SensorDataChangedObserver listener) { + this.listener = listener; + + } + + @Override + public void connect(Context context, Handler handler, String addressIgnored) { + + 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."); - this.observer = null; return; } - if (sensorManager.registerListener(listener, pressureSensor, SAMPLING_PERIOD, handler)) { - this.observer = observer; + if (sensorManager.registerListener(sensorEventListener, pressureSensor, SAMPLING_PERIOD, handler)) { + this.context = context; + listener.onConnect(new AggregatorBarometer("internal", null)); return; } - disconnect(context); - } - - public void disconnect(Context context) { - SensorManager sensorManager = (SensorManager) context.getSystemService(Context.SENSOR_SERVICE); - sensorManager.unregisterListener(listener); - observer = null; + disconnect(); } public boolean isConnected() { - return observer != null; + return context != 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; } } diff --git a/src/main/java/de/dennisguse/opentracks/sensors/driver/Driver.java b/src/main/java/de/dennisguse/opentracks/sensors/driver/Driver.java new file mode 100644 index 000000000..98157de60 --- /dev/null +++ b/src/main/java/de/dennisguse/opentracks/sensors/driver/Driver.java @@ -0,0 +1,12 @@ +package de.dennisguse.opentracks.sensors.driver; + +import android.content.Context; +import android.os.Handler; + +public interface Driver { + void connect(Context context, Handler handler, String address); + + boolean isConnected(); + + void disconnect(); +} 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 bcd21a256..910375a18 100644 --- a/src/main/java/de/dennisguse/opentracks/sensors/sensorData/SensorDataSet.java +++ b/src/main/java/de/dennisguse/opentracks/sensors/sensorData/SensorDataSet.java @@ -120,10 +120,6 @@ public final class SensorDataSet { return; } if (value instanceof AtmosphericPressure) { - if (this.barometer == null) { - //TODO Quick fix. This shouldn't happen, but it does. May be a race condition? - return; - } this.barometer.add((Raw) data); return; } diff --git a/src/main/java/de/dennisguse/opentracks/settings/PreferencesUtils.java b/src/main/java/de/dennisguse/opentracks/settings/PreferencesUtils.java index fe474b4bc..531aa4fbf 100644 --- a/src/main/java/de/dennisguse/opentracks/settings/PreferencesUtils.java +++ b/src/main/java/de/dennisguse/opentracks/settings/PreferencesUtils.java @@ -253,10 +253,6 @@ public class PreferencesUtils { return SensorType.NONE.getPreferenceValue(); } - public static boolean isBluetoothSensorAddressNone(String currentValue) { - return getBluetoothSensorAddressNone().equals(currentValue); - } - public static SensorType getSensorType(String address) { return Arrays.stream(SensorType.values()) .filter(it -> it.getPreferenceValue().equals(address)) diff --git a/src/main/java/de/dennisguse/opentracks/settings/bluetooth/BluetoothLeAdapter.java b/src/main/java/de/dennisguse/opentracks/settings/bluetooth/BluetoothLeAdapter.java index c60a3f4a9..328f9b152 100644 --- a/src/main/java/de/dennisguse/opentracks/settings/bluetooth/BluetoothLeAdapter.java +++ b/src/main/java/de/dennisguse/opentracks/settings/bluetooth/BluetoothLeAdapter.java @@ -1,5 +1,6 @@ package de.dennisguse.opentracks.settings.bluetooth; +import android.annotation.SuppressLint; import android.bluetooth.BluetoothDevice; import android.view.LayoutInflater; import android.view.View; @@ -7,10 +8,13 @@ import android.view.ViewGroup; import android.widget.BaseAdapter; import android.widget.TextView; +import androidx.annotation.NonNull; + import java.util.ArrayList; import java.util.List; import java.util.Objects; +@SuppressLint("MissingPermission") public class BluetoothLeAdapter extends BaseAdapter { /** @@ -50,22 +54,30 @@ public class BluetoothLeAdapter extends BaseAdapter { return currentView; } - public void add(String name, String address) { - Device device = new Device(name, address); + /** + * @return Data changed? + */ + public boolean add(String address, String name) { + Device device = new Device(address, name); if (!devices.contains(device)) { - devices.add(new Device(name, address)); + devices.add(device); + return true; } else { for (Device currentDevice : devices) { - if (currentDevice.getAddress().equals(address)) { - currentDevice.setName(name); + if (currentDevice.address.equals(address)) { + currentDevice.name = name; + return true; } } } - notifyDataSetChanged(); + return false; } - public void add(BluetoothDevice bluetoothDevice) { - add(bluetoothDevice.getName(), bluetoothDevice.getAddress()); + public void addAll(List bluetoothDevices) { + boolean dataSetChanged = bluetoothDevices.stream() + .anyMatch(bluetoothDevice -> add(bluetoothDevice.getAddress(), bluetoothDevice.getName())); + + if (dataSetChanged) notifyDataSetChanged(); } public Device get(int index) { @@ -73,19 +85,14 @@ public class BluetoothLeAdapter extends BaseAdapter { } public static class Device { - private String name; + + @NonNull private final String address; + private String name; - public Device(String name, String address) { - this.name = name; + Device(@NonNull String address, String name) { + Objects.requireNonNull(address); this.address = address; - } - - public String getNameOrAddress() { - return name != null ? name : getAddress(); - } - - public void setName(String name) { this.name = name; } @@ -93,16 +100,21 @@ public class BluetoothLeAdapter extends BaseAdapter { return address; } + public String getNameOrAddress() { + return name != null ? name : address; + } + @Override public boolean equals(Object o) { if (this == o) return true; if (!(o instanceof Device device)) return false; + return address.equals(device.address); } @Override public int hashCode() { - return Objects.hash(name, address); + return Objects.hash(address); } } } 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 8ffb1365a..ee3e495a9 100644 --- a/src/main/java/de/dennisguse/opentracks/settings/bluetooth/BluetoothLePressurePreference.java +++ b/src/main/java/de/dennisguse/opentracks/settings/bluetooth/BluetoothLePressurePreference.java @@ -7,7 +7,9 @@ import android.util.AttributeSet; import androidx.preference.PreferenceDialogFragmentCompat; -import java.util.Collections; +import java.util.List; + +import de.dennisguse.opentracks.sensors.BluetoothHandlerBarometricPressure; public class BluetoothLePressurePreference extends BluetoothLeSensorPreference { @@ -34,7 +36,6 @@ public class BluetoothLePressurePreference extends BluetoothLeSensorPreference { boolean hasInternalSensor = pressureSensor != null; return BluetoothLeSensorPreference.BluetoothLeSensorPreferenceDialog -// .newInstance(getKey(), List.of(BluetoothHandlerBarometricPressure.BAROMETRIC_PRESSURE), hasInternalSensor); //TODO #1424 - .newInstance(getKey(), Collections.emptyList(), hasInternalSensor); + .newInstance(getKey(), List.of(BluetoothHandlerBarometricPressure.BAROMETRIC_PRESSURE), hasInternalSensor); } } \ No newline at end of file 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 2b7bf3816..013c2be98 100644 --- a/src/main/java/de/dennisguse/opentracks/settings/bluetooth/BluetoothLeSensorPreference.java +++ b/src/main/java/de/dennisguse/opentracks/settings/bluetooth/BluetoothLeSensorPreference.java @@ -102,7 +102,7 @@ public abstract class BluetoothLeSensorPreference extends DialogPreference { BluetoothDevice device = bluetoothAdapter.getRemoteDevice(value); if (device != null && device.getName() != null) { - return getContext().getString(R.string.bluetooth_sensor_summary, device.getAddress(), device.getName()); + return getContext().getString(R.string.bluetooth_sensor_summary, device.getAddress(), device.getName()); } return value; } @@ -121,14 +121,12 @@ public abstract class BluetoothLeSensorPreference extends DialogPreference { @Override public void onScanResult(int callbackType, ScanResult result) { Log.d(TAG, "Found device " + result.getDevice().getName() + " " + result); - listAdapter.add(result.getDevice()); + onBatchScanResults(List.of(result)); } @Override public void onBatchScanResults(List results) { - for (ScanResult result : results) { - onScanResult(-1, result); - } + listAdapter.addAll(results.stream().map(ScanResult::getDevice).collect(Collectors.toList())); } @Override @@ -205,24 +203,21 @@ public abstract class BluetoothLeSensorPreference extends DialogPreference { return; } - String deviceNone = getContext().getString(R.string.sensor_type_value_none); - String sensorInternal = getString(R.string.sensor_type_value_internal); - - listAdapter.add(getContext().getString(DEVICE_NONE_RESOURCEID), deviceNone); + listAdapter.add(SensorType.NONE.getPreferenceValue(), getContext().getString(DEVICE_NONE_RESOURCEID)); selectedEntryIndex = 0; BluetoothLeSensorPreference preference = (BluetoothLeSensorPreference) getPreference(); String deviceSelected = preference.value; if (includeInternalSensor) { - listAdapter.add(getString(SENSOR_INTERNAL_RESOURCEID), sensorInternal); - if (sensorInternal.equals(deviceSelected)) { + listAdapter.add(SensorType.INTERNAL.getPreferenceValue(), getString(SENSOR_INTERNAL_RESOURCEID)); + if (SensorType.INTERNAL.getPreferenceValue().equals(deviceSelected)) { selectedEntryIndex = 1; } } - if (deviceSelected != null && !deviceNone.equals(deviceSelected) && !sensorInternal.equals(deviceSelected)) { + if (deviceSelected != null && SensorType.REMOTE.equals(PreferencesUtils.getSensorType(deviceSelected))) { listAdapter.add(preference.value, preference.value); - selectedEntryIndex++; + selectedEntryIndex = !includeInternalSensor ? 1 : 2; } List scanFilter = null;