diff --git a/src/main/java/de/dennisguse/opentracks/Startup.java b/src/main/java/de/dennisguse/opentracks/Startup.java index a1c1b623d3..45d61e4922 100644 --- a/src/main/java/de/dennisguse/opentracks/Startup.java +++ b/src/main/java/de/dennisguse/opentracks/Startup.java @@ -4,7 +4,6 @@ import android.app.Application; import android.content.Context; import android.os.Build; -import android.os.StrictMode; import android.util.Log; import com.google.android.material.color.DynamicColors; @@ -33,7 +32,7 @@ public void onCreate() { // In debug builds: show thread and VM warnings. if (BuildConfig.DEBUG) { Log.d(TAG, "Enabling strict mode"); - StrictMode.enableDefaults(); +// StrictMode.enableDefaults(); } PreferencesUtils.initPreferences(this, getResources()); diff --git a/src/main/java/de/dennisguse/opentracks/sensors/BluetoothConnectionManager.java b/src/main/java/de/dennisguse/opentracks/sensors/BluetoothConnectionManager.java index c8660f1116..c3aabdc954 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 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 void onCharacteristicChanged(BluetoothGatt gatt, @NonNull BluetoothGattCh } }; - 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 @@ private synchronized void clearData() { 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 @@ synchronized void disconnect() { 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 1875b5a694..299fa4a8b2 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 List getServices() { 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 2ff1434c7b..8f919fca28 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 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 synchronized void stop(Context context) { 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 df7fed010a..6dca516d46 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.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 void stop(Context context) { 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 9032010a57..74ef061f8c 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.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 Context context; - private final SensorEventListener listener = new SensorEventListener() { + private final SensorEventListener sensorEventListener = new SensorEventListener() { @Override public void onSensorChanged(SensorEvent event) { if (!isConnected()) { @@ -29,7 +34,7 @@ public void onSensorChanged(SensorEvent event) { return; } - observer.onSensorValueChanged(AtmosphericPressure.ofHPA(event.values[0])); + listener.onChange(new Raw<>(AtmosphericPressure.ofHPA(event.values[0]))); } @Override @@ -38,30 +43,40 @@ public void onAccuracyChanged(Sensor sensor, int accuracy) { } }; - 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); + disconnect(); } - public void disconnect(Context context) { - SensorManager sensorManager = (SensorManager) context.getSystemService(Context.SENSOR_SERVICE); - sensorManager.unregisterListener(listener); - observer = null; + public boolean isConnected() { + return context != null; } - public boolean isConnected() { - return observer != 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 0000000000..98157de609 --- /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 bcd21a256b..910375a180 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 void update(@NonNull Raw data) { 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/bluetooth/BluetoothLeAdapter.java b/src/main/java/de/dennisguse/opentracks/settings/bluetooth/BluetoothLeAdapter.java index c60a3f4a96..5a934e0a58 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; @@ -11,6 +12,7 @@ import java.util.List; import java.util.Objects; +@SuppressLint("MissingPermission") public class BluetoothLeAdapter extends BaseAdapter { /** 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 8ffb1365ae..ee3e495a99 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 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 PreferenceDialogFragmentCompat createInstance() { 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 2b7bf38162..554daebc7b 100644 --- a/src/main/java/de/dennisguse/opentracks/settings/bluetooth/BluetoothLeSensorPreference.java +++ b/src/main/java/de/dennisguse/opentracks/settings/bluetooth/BluetoothLeSensorPreference.java @@ -205,24 +205,21 @@ private void startBluetoothScan() { 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(getContext().getString(DEVICE_NONE_RESOURCEID), SensorType.NONE.getPreferenceValue()); 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(getString(SENSOR_INTERNAL_RESOURCEID), SensorType.INTERNAL.getPreferenceValue()); + if (SensorType.INTERNAL.getPreferenceValue().equals(deviceSelected)) { selectedEntryIndex = 1; } } - if (deviceSelected != null && !deviceNone.equals(deviceSelected) && !sensorInternal.equals(deviceSelected)) { + if (SensorType.REMOTE.equals(PreferencesUtils.getSensorType(deviceSelected))) { listAdapter.add(preference.value, preference.value); - selectedEntryIndex++; + selectedEntryIndex = !includeInternalSensor ? 1 : 2; } List scanFilter = null;