Extract SensorHandler from GainManager for re-use.

This commit is contained in:
Dennis Guse
2026-01-29 18:17:15 +01:00
parent 167d90332e
commit f8aa456549
8 changed files with 178 additions and 116 deletions
@@ -1,132 +1,69 @@
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.R;
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.driver.DriverObserver;
import de.dennisguse.opentracks.sensors.driver.DriverObserver0;
import de.dennisguse.opentracks.sensors.sensorData.Aggregator;
import de.dennisguse.opentracks.sensors.sensorData.AggregatorBarometer;
import de.dennisguse.opentracks.settings.PreferencesUtils;
/**
* Estimates the altitude gain and altitude loss using the device's pressure sensor (i.e., barometer).
*/
//TODO Rename class
public class GainManager implements DriverObserver<AtmosphericPressure> {
private static final String TAG = GainManager.class.getSimpleName();
private final SensorManager sensorManager;
private final SharedPreferences.OnSharedPreferenceChangeListener sharedPreferenceChangeListener = (sharedPreferences, key) -> connect();
private Context context;
private Handler handler;
private Driver driver;
private AggregatorBarometer aggregatorBarometer;
public class GainManager extends SensorHandler<AtmosphericPressure> {
public GainManager(SensorManager sensorManager) {
this.sensorManager = sensorManager;
}
public void start(Context context, Handler handler) {
this.context = context;
this.handler = handler;
PreferencesUtils.registerOnSharedPreferenceChangeListener(sharedPreferenceChangeListener);
}
public void stop() {
Log.d(TAG, "Stop");
this.context = null;
this.handler = null;
PreferencesUtils.unregisterOnSharedPreferenceChangeListener(sharedPreferenceChangeListener);
disconnect();
}
@Deprecated
public AggregatorBarometer getAggregatorBarometer() {
return aggregatorBarometer;
}
@Deprecated
public void setAggregatorBarometer(AggregatorBarometer aggregatorBarometer) {
this.aggregatorBarometer = aggregatorBarometer;
}
private void connect() {
disconnect();
String address = PreferencesUtils.getBarometerSensorAddress();
switch (PreferencesUtils.getSensorType(address)) {
case NONE -> {
return;
}
case INTERNAL -> driver = new BarometerInternal(this);
case REMOTE -> driver =
new BluetoothConnectionManager(
BluetoothUtils.getAdapter(context),
new DriverObserver0() {
@Override
public void onConnect(Aggregator<?, ?> emptySensorData) {
GainManager.this.onConnect();
}
@Override
public void onDisconnect(Aggregator<?, ?> emptySensorData) {
GainManager.this.onDisconnect();
}
@Override
public void onRemove(Aggregator<?, ?> emptySensorData) {
GainManager.this.onRemove();
}
@Override
public void onChange(Record value) {
onData((AtmosphericPressure) value);
}
},
new BluetoothHandlerBarometricPressure()
);
default -> throw new RuntimeException("Not implemented");
}
driver.connect(context, handler, address);
}
private void disconnect() {
if (driver == null) return;
driver.disconnect();
driver = null;
super(sensorManager);
}
@Override
public void onConnect() {
this.aggregatorBarometer = new AggregatorBarometer("", null);
protected int getSensorPreferenceKey() {
return R.string.settings_sensor_bluetooth_pressure_key;
}
@NonNull
@Override
public void onDisconnect() {
this.aggregatorBarometer.resetAggregated();
protected Driver createDriverInternal() {
return new BarometerInternal(driverObserver);
}
@NonNull
@Override
public void onData(AtmosphericPressure value) {
this.aggregatorBarometer.add(sensorManager.getNow(), value);
protected Driver createDriverBluetooth(String address) {
return new BluetoothConnectionManager(
BluetoothUtils.getAdapter(getContext()),
new DriverObserver0() {
@Override
public void onConnect(Aggregator<?, ?> emptySensorData) {
driverObserver.onConnect();
}
@Override
public void onDisconnect(Aggregator<?, ?> emptySensorData) {
driverObserver.onDisconnect();
}
@Override
public void onRemove(Aggregator<?, ?> emptySensorData) {
driverObserver.onRemove();
}
@Override
public void onChange(Record value) {
driverObserver.onData((AtmosphericPressure) value);
}
},
new BluetoothHandlerBarometricPressure()
);
}
@NonNull
@Override
public void onRemove() {
this.aggregatorBarometer = null;
public Aggregator<AtmosphericPressure, ?> createAggregator() {
return new AggregatorBarometer("", null);
}
}
@@ -0,0 +1,126 @@
package de.dennisguse.opentracks.sensors;
import android.content.Context;
import android.content.SharedPreferences;
import android.os.Handler;
import androidx.annotation.NonNull;
import androidx.annotation.VisibleForTesting;
import de.dennisguse.opentracks.sensors.driver.Driver;
import de.dennisguse.opentracks.sensors.driver.DriverObserver;
import de.dennisguse.opentracks.sensors.sensorData.Aggregator;
import de.dennisguse.opentracks.settings.PreferencesUtils;
public abstract class SensorHandler<T> {
private final SensorManager sensorManager;
private final SharedPreferences.OnSharedPreferenceChangeListener sharedPreferenceChangeListener = (sharedPreferences, key) -> connect();
private Context context;
private Handler handler;
private Driver driver;
private Aggregator<T, ?> aggregator;
protected final DriverObserver<T> driverObserver = new DriverObserver<T>() {
@Override
public void onConnect() {
SensorHandler.this.aggregator = createAggregator();
}
@Override
public void onDisconnect() {
SensorHandler.this.aggregator.resetAggregated();
}
@Override
public void onData(T value) {
SensorHandler.this.aggregator.add(sensorManager.getNow(), value);
sensorManager.onChange();
}
@Override
public void onRemove() {
SensorHandler.this.aggregator = null;
}
};
protected SensorHandler(SensorManager sensorManager) {
this.sensorManager = sensorManager;
}
//TODO Do we really need start and stop?
void start(Context context, Handler handler) {
this.context = context;
this.handler = handler;
PreferencesUtils.registerOnSharedPreferenceChangeListener(sharedPreferenceChangeListener);
}
void stop() {
this.context = null;
this.handler = null;
PreferencesUtils.unregisterOnSharedPreferenceChangeListener(sharedPreferenceChangeListener);
disconnect();
}
void connect() {
disconnect();
String address = PreferencesUtils.getString(getSensorPreferenceKey(), PreferencesUtils.getBluetoothSensorAddressNone());
switch (PreferencesUtils.getSensorType(address)) {
case NONE -> {
return;
}
case INTERNAL -> {
driver = createDriverInternal();
}
case REMOTE -> {
driver = createDriverBluetooth(address);
}
default -> throw new UnsupportedOperationException();
}
driver.connect(context, handler, address);
}
void disconnect() {
if (driver == null) return;
driver.disconnect();
driver = null;
}
@NonNull
protected Driver createDriverInternal() {
throw new RuntimeException("Not implemented");
}
@NonNull
protected Driver createDriverBluetooth(String address) {
throw new RuntimeException("Not implemented");
}
@NonNull
protected abstract Aggregator<T, ?> createAggregator();
protected Context getContext() {
return context;
}
//TODO Try to keep aggregator private.
Aggregator<T, ?> getAggregator() {
return aggregator;
}
//TODO REMOVE
@VisibleForTesting
public void setAggregator(Aggregator<T, ?> aggregator) {
this.aggregator = aggregator;
}
}
@@ -84,10 +84,11 @@ public class SensorManager implements SharedPreferences.OnSharedPreferenceChange
this.cyclingPowerManager = new BluetoothConnectionManager(bluetoothAdapter, this, new BluetoothHandlerCyclingPower());
this.runningSpeedAndCadenceManager = new BluetoothConnectionManager(bluetoothAdapter, this, new BluetoothHandlerRunningSpeedAndCadence());
altitudeSumManager.start(context, handler);
onSharedPreferenceChanged(null, null);
gpsManager.start(context, handler);
altitudeSumManager.start(context, handler);
}
public void stop() {
@@ -137,7 +138,7 @@ public class SensorManager implements SharedPreferences.OnSharedPreferenceChange
if (cyclingDistanceSpeed != null) cyclingDistanceSpeed.resetAggregated();
if (cyclingPower != null) cyclingPower.resetAggregated();
if (runningDistanceSpeedCadence != null) runningDistanceSpeedCadence.resetAggregated();
if (altitudeSumManager.getAggregatorBarometer() != null) altitudeSumManager.getAggregatorBarometer().resetAggregated();
if (altitudeSumManager.getAggregator() != null) altitudeSumManager.getAggregator().resetAggregated();
if (gpsManager.getAggregatorGPS() != null) gpsManager.getAggregatorGPS().resetAggregated();
}
@@ -355,7 +356,7 @@ public class SensorManager implements SharedPreferences.OnSharedPreferenceChange
//TODO This is to ExportImportTest alive; refactor test.
if (this.altitudeSumManager != null) {
this.altitudeSumManager.setAggregatorBarometer(null);
this.altitudeSumManager.setAggregator(null);
}
if (this.gpsManager != null) {
this.gpsManager.setAggregatorGPS();
@@ -364,7 +365,7 @@ public class SensorManager implements SharedPreferences.OnSharedPreferenceChange
public SensorDataSet getSensorDataSet(Instant now) {
AggregatorGPS gps = gpsManager.getAggregatorGPS();
AggregatorBarometer barometer = altitudeSumManager.getAggregatorBarometer();
AggregatorBarometer barometer = (AggregatorBarometer) altitudeSumManager.getAggregator();
return new SensorDataSet(
gps != null && gps.hasReceivedData() ? gps.getAggregatedValueWithSensorName(now) : new SensorData<>(Position.of(now), ""),
@@ -1,8 +1,8 @@
package de.dennisguse.opentracks.sensors;
public enum SensorType {
NONE("NONE"),
INTERNAL("INTERNAL"),
NONE("NONE"), //TODO Use R.string.sensor_type_value_none
INTERNAL("INTERNAL"), //TODO Use R.string.sensor_type_value_none
//NOTE: preferenceValue of REMOTE should not be used anywhere.
REMOTE("*");
@@ -4,6 +4,8 @@ import android.content.Context;
import android.os.Handler;
public interface Driver {
//TODO Address is optional for internal drivers. Move address to driver instantiation.
void connect(Context context, Handler handler, String address);
//TODO Remove from interface?
@@ -253,7 +253,7 @@ public class PreferencesUtils {
return isReportSpeed(track.activityTypeLocalized());
}
private static String getBluetoothSensorAddressNone() {
public static String getBluetoothSensorAddressNone() {
return SensorType.NONE.getPreferenceValue();
}
@@ -264,10 +264,6 @@ public class PreferencesUtils {
.orElse(SensorType.REMOTE);
}
public static String getBarometerSensorAddress() {
return getString(R.string.settings_sensor_bluetooth_pressure_key, getBluetoothSensorAddressNone());
}
public static String getBluetoothHeartRateSensorAddress() {
return getString(R.string.settings_sensor_bluetooth_heart_rate_key, getBluetoothSensorAddressNone());
}