From b7462e47e70c50ced0da47076872e4424d288ff5 Mon Sep 17 00:00:00 2001
From: Marvin <marvin@weiler.rocks>
Date: Fri, 14 Mar 2025 22:58:57 +0100
Subject: [PATCH] Added option to specify in which interval a measurement must
 be. Refactored the settings to a own class.

---
 lib/foreground_task/task_handler.dart | 24 ++++---
 lib/l10n/app_de.arb                   |  3 +
 lib/l10n/app_en.arb                   |  3 +
 lib/main.dart                         | 12 ++--
 lib/manager/settings_manager.dart     | 74 ++++++++++++++++++++
 lib/manager/upload_manger.dart        | 30 +++++----
 lib/obs_list_upload_page.dart         | 11 ++-
 lib/obs_settings_page.dart            | 97 +++++++++++++++++----------
 lib/widgets/measurements.dart         |  7 +-
 9 files changed, 182 insertions(+), 79 deletions(-)
 create mode 100644 lib/manager/settings_manager.dart

diff --git a/lib/foreground_task/task_handler.dart b/lib/foreground_task/task_handler.dart
index 0113d94..ac56681 100644
--- a/lib/foreground_task/task_handler.dart
+++ b/lib/foreground_task/task_handler.dart
@@ -2,6 +2,7 @@ import 'dart:async';
 import 'dart:convert';
 import 'package:app/foreground_task/bluetooth.dart';
 import 'package:app/foreground_task/circular_buffer.dart';
+import 'package:app/manager/settings_manager.dart';
 import 'package:flutter_blue_plus/flutter_blue_plus.dart';
 import 'package:flutter_foreground_task/flutter_foreground_task.dart';
 import 'package:isar/isar.dart';
@@ -29,7 +30,7 @@ void startBackgroundTask() {
 class ForegroundTaskHandler extends TaskHandler {
   late Isar _isar;
   late Bluetooth _bluetooth;
-  late SharedPreferencesAsync _prefs;
+  final SettingsManager _settingsManager = SettingsManager();
   late Uuid _uuid;
   final LocationSettings locationSettings = LocationSettings(
     accuracy: LocationAccuracy.best,
@@ -108,9 +109,6 @@ class ForegroundTaskHandler extends TaskHandler {
     // add callback for the disconnect event
     _bluetooth.subscribeToDisconnectEvent(_onBleDeviceDisconnected);
 
-    //Initialize access to the sharedPreferenzes
-    _prefs = SharedPreferencesAsync();
-
     // Initialize the location
     _positionStream =
         Geolocator.getPositionStream(locationSettings: locationSettings)
@@ -128,11 +126,8 @@ class ForegroundTaskHandler extends TaskHandler {
     );
 
     // Initialize Sentry to report errors from isolate
-    final sentryEnabled = await _prefs.getBool('sentryEnabled') ?? false;
-    //final sentryEnabled = true;
-
-    final sentryDSN = await _prefs.getString('sentryDSN') ??
-        'https://d36af7603bf2434889b0bc7ffcaeeda7@glitchtip.weiler.rocks/1';
+    final sentryEnabled = await _settingsManager.readSentryEnabled();
+    final sentryDSN = await _settingsManager.readSentryDSN();
 
     if (sentryEnabled && sentryDSN.isNotEmpty) {
       await SentryFlutter.init((options) {
@@ -375,22 +370,29 @@ class ForegroundTaskHandler extends TaskHandler {
     }
 
     // Substract the handlebar length from the sensor readings
-    final handlebarDistance = await _prefs.getInt("handlebarDistance") ?? 0;
-    final invertSensors = await _prefs.getBool("invertSensors") ?? false;
+    final handlebarDistance = await _settingsManager.readHandlebarOffset();
+    final invertSensors = await _settingsManager.readInvertSensors();
+    final minMeassurement = await _settingsManager.readMinMeasurement();
+    final maxMeassurement = await _settingsManager.readMaxMeasurement();
 
     // find the index in the list of the confirmed event
     final confirmedIdx = sampleData.indexWhere((element) => element.confirmed);
 
     // Extract the sensor data, invert the sensors if set in settings
     // if the value is smaller then the handlebar distance set it to 65000
+    // also check if the value is inside of the min and max meassurement range
     // 65535 is the max value a obs reports and will be ignored by the portal
     final s1Data = sampleData
         .map((e) => invertSensors ? e.sensor2 : e.sensor1)
         .map((val) => (val - handlebarDistance) > 0.0 ? val : 65535.0)
+        .map((val) =>
+            val < minMeassurement || val > maxMeassurement ? 65535.0 : val)
         .toList();
     final s2Data = sampleData
         .map((e) => invertSensors ? e.sensor1 : e.sensor2)
         .map((val) => (val - handlebarDistance) > 0.0 ? val : 65535.0)
+        .map((val) =>
+            val < minMeassurement || val > maxMeassurement ? 65535.0 : val)
         .toList();
 
     final TripSegment event = TripSegment()
diff --git a/lib/l10n/app_de.arb b/lib/l10n/app_de.arb
index 6fe9e0d..86ce87a 100644
--- a/lib/l10n/app_de.arb
+++ b/lib/l10n/app_de.arb
@@ -28,6 +28,9 @@
   "settingsPageTitle": "OBS APP Einstellungen",
   "settingsPageHandlebarDistanceDescription": "Miss den horizontalen Abstand deines linken und rechten Lenker-Endes bis zur Radmitte. Hat dein Lenker zum Beispiel eine gesamtbreite von 60 cm, dann gibts du hier 30cm ein.",
   "settingsPageHandlebarDistanceLabel": "Lenkerabstand in cm",
+  "settingsPageDistanceFilterDescription": "Einige OpenBikeSensors melden ungültige Entfernungsdaten. Oft liegen diese Werte in der Nähe der Min/Max-Werte. Hier kannst du den  Bereich angeben, in dem du die Werte aufzeichnen möchten. Entfernungen, die nicht zwischen diesen beiden Werten liegen, werden nicht aufgezeichnet",
+  "settingsPageMinMeasurementLabel": "Min Messung in cm",
+  "settingsPageMaxMeasurementLabel": "Max Messung in cm",
   "settingsPageInvertSensorsDescription": ",Wenn die Abstandswerte links und rechts vertauscht angezeigt werden, kannst du hier die Sensoren vertauschen. Dies ist z.B. notwendig, wenn der OBS kopfüber montiert wird.",
   "settingsPageInvertSensorsLabel": "Vertausche den linken und rechten sensor",
   "settingsPageHandlebarDistanceError": "Bitte gib eine Zahl zwischen 0 und 200 ein",
diff --git a/lib/l10n/app_en.arb b/lib/l10n/app_en.arb
index f9b0f85..f891b5b 100644
--- a/lib/l10n/app_en.arb
+++ b/lib/l10n/app_en.arb
@@ -28,6 +28,9 @@
   "settingsPageTitle": "OBS APP Settings",
   "settingsPageHandlebarDistanceDescription": "Measure the horizontal distance from your left and right handlebar ends to the centre of the wheel. For example, if your handlebars are  at all 60 cm wide, enter 30 cm here.",
   "settingsPageHandlebarDistanceLabel": "Handlebar distance in cm",
+  "settingsPageDistanceFilterDescription": "Some OpenBikeSensors report invalid distance data. Often these values are close to the min/max values. You can specify the range in which you want to record the values. Distances not between these two values will not be recorded.",
+  "settingsPageMinMeasurementLabel": "Min Measurement in cm",
+  "settingsPageMaxMeasurementLabel": "Max Measurement in cm",
   "settingsPageInvertSensorsDescription": "If the distance values are displayed reversed on the left and right, you can swap the sensors here. This is necessary, for example, if the OBS is mounted upside down.",
   "settingsPageInvertSensorsLabel": "Swap left and right sensor",
   "settingsPageHandlebarDistanceError": "Please enter a number between 0 and 200",
diff --git a/lib/main.dart b/lib/main.dart
index 8fe0061..38496eb 100644
--- a/lib/main.dart
+++ b/lib/main.dart
@@ -3,6 +3,7 @@ import 'dart:io';
 
 import 'package:app/generated/i18n/app_localizations.dart';
 import 'package:app/manager/background_manger.dart';
+import 'package:app/manager/settings_manager.dart';
 import 'package:app/providers/battery_provider.dart';
 import 'package:app/providers/bluetooth_provider.dart';
 import 'package:app/providers/button_provider.dart';
@@ -36,16 +37,11 @@ Future<void> main() async {
     ],
     child: const MyApp(),
   );
-
-  // Initialize the shared preferences
-  SharedPreferencesAsync prefs = SharedPreferencesAsync();
+  final settingsManager = SettingsManager();
 
   // Initialize Sentry to report errors from isolate
-  final sentryEnabled = await prefs.getBool('sentryEnabled') ?? false;
-  //final sentryEnabled = true;
-  final sentryDSN = await prefs.getString('sentryDSN') ??
-      'https://d36af7603bf2434889b0bc7ffcaeeda7@glitchtip.weiler.rocks/1';
-
+  final sentryEnabled = await settingsManager.readSentryEnabled();
+  final sentryDSN = await settingsManager.readSentryDSN();
   if (sentryEnabled && sentryDSN.isNotEmpty) {
     await SentryFlutter.init((options) {
       options.dsn = sentryDSN;
diff --git a/lib/manager/settings_manager.dart b/lib/manager/settings_manager.dart
new file mode 100644
index 0000000..341fb70
--- /dev/null
+++ b/lib/manager/settings_manager.dart
@@ -0,0 +1,74 @@
+import 'dart:core';
+import 'package:shared_preferences/shared_preferences.dart';
+
+class SettingsManager {
+  SharedPreferencesAsync prefs = SharedPreferencesAsync();
+
+  Future<void> _saveStringValue(String key, String value) async {
+    await prefs.setString(key, value);
+  }
+
+  Future<void> _saveIntValue(String key, int value) async {
+    await prefs.setInt(key, value);
+  }
+
+  Future<void> _saveBoolValue(String key, bool value) async {
+    await prefs.setBool(key, value);
+  }
+
+  Future<int> readHandlebarOffset() async =>
+      await prefs.getInt('handlebarOffset') ?? 0;
+
+  Future<void> writeHandlebarOffset(int value) async {
+    await _saveIntValue('handlebarOffset', value);
+  }
+
+  Future<String> readPortalServer() async =>
+      await prefs.getString('portalServer') ?? "";
+
+  Future<void> writePortalServer(String value) async {
+    await _saveStringValue('portalServer', value);
+  }
+
+  Future<String> readPortalKey() async =>
+      await prefs.getString('portalKey') ?? "";
+
+  Future<void> writePortalKey(String value) async {
+    await _saveStringValue('portalKey', value);
+  }
+
+  Future<String> readSentryDSN() async =>
+      await prefs.getString('sentryDSN') ?? "";
+
+  Future<void> writeSentryDSN(String value) async {
+    await _saveStringValue('sentryDSN', value);
+  }
+
+  Future<bool> readSentryEnabled() async =>
+      await prefs.getBool('sentryEnabled') ?? false;
+
+  Future<void> writeSentryEnabled(bool value) async {
+    await _saveBoolValue('sentryEnabled', value);
+  }
+
+  Future<bool> readInvertSensors() async =>
+      await prefs.getBool('invertSensors') ?? false;
+
+  Future<void> writeInvertSensors(bool value) async {
+    await _saveBoolValue('invertSensors', value);
+  }
+
+  Future<int> readMinMeasurement() async =>
+      await prefs.getInt('minMeasurement') ?? 0;
+
+  Future<void> writeMinMeasurement(int value) async {
+    await _saveIntValue('minMeasurement', value);
+  }
+
+  Future<int> readMaxMeasurement() async =>
+      await prefs.getInt('maxMeasurement') ?? 1000;
+
+  Future<void> writeMaxMeasurement(int value) async {
+    await _saveIntValue('maxMeasurement', value);
+  }
+}
diff --git a/lib/manager/upload_manger.dart b/lib/manager/upload_manger.dart
index eff1c18..9fa1f93 100644
--- a/lib/manager/upload_manger.dart
+++ b/lib/manager/upload_manger.dart
@@ -1,5 +1,6 @@
 import 'package:app/database/trip.dart';
 import 'package:app/manager/database_manger.dart';
+import 'package:app/manager/settings_manager.dart';
 import 'package:http/http.dart' as http;
 import 'package:http_parser/http_parser.dart';
 import 'package:isar/isar.dart';
@@ -7,6 +8,8 @@ import 'package:sentry_flutter/sentry_flutter.dart';
 import 'package:shared_preferences/shared_preferences.dart';
 
 class UploadManager {
+  final MICRO_SEC_TO_CM_DIVIDER = 58.0;
+
   Future<(int, int)> upload() async {
     final database = await IsarService().isar;
 
@@ -78,8 +81,8 @@ class UploadManager {
     return (minLeft, minRight);
   }
 
-  String _genMetadataHeader(
-      Trip trip, int maxMeasurements, int handlebarDistance) {
+  String _genMetadataHeader(Trip trip, int maxMeasurements,
+      int handlebarDistance, int maxFlightTimeinMicroSec) {
     // https://github.com/openbikesensor/OpenBikeSensorFirmware/blob/main/docs/software/firmware/csv_format.md#metadata
     final fields = [
       "OBSFirmwareVersion=${trip.obsFirmwareRevision}",
@@ -90,7 +93,7 @@ class UploadManager {
       "OffsetRight=${handlebarDistance / 2}",
       "NumberOfDefinedPrivacyAreas=0", // can be 0 as we do not record in privacy areas
       "PrivacyLevelApplied=AbsolutePrivacy",
-      "MaximumValidFlightTimeMicroseconds=18560",
+      "MaximumValidFlightTimeMicroseconds=$maxFlightTimeinMicroSec",
       "DistanceSensorsUsed=HC-SR04/JSN-SR04T", // TODO read from db
       "DeviceId=obs-app"
     ];
@@ -141,8 +144,6 @@ class UploadManager {
     final double minLeftDistance = minDistances.$1 - event.offsetLeft;
     final double minRightDistance = minDistances.$2 - event.offsetRight;
 
-    final MICRO_SEC_TO_CM_DIVIDER = 58.0;
-
     final List<String> fields = [
       "${event.timestamp.day}.${event.timestamp.month}.${event.timestamp.year}",
       "${event.timestamp.hour}:${event.timestamp.minute}:${event.timestamp.second}",
@@ -192,10 +193,11 @@ class UploadManager {
     return fields.join(";");
   }
 
-  String _genCSVFile(Trip trip, int handlebarDistance) {
+  String _genCSVFile(
+      Trip trip, int handlebarDistance, int maxFlightTimeinMicroSec) {
     final maxMeasurements = _maxDistanceForTrip(trip);
-    final metadata =
-        _genMetadataHeader(trip, maxMeasurements, handlebarDistance);
+    final metadata = _genMetadataHeader(
+        trip, maxMeasurements, handlebarDistance, maxFlightTimeinMicroSec);
     final header = _genCSVHeader(trip, maxMeasurements);
 
     final List<String> rows = [];
@@ -207,23 +209,25 @@ class UploadManager {
   }
 
   Future<bool> uploadTrip(Trip trip) async {
-    final SharedPreferencesAsync prefs = SharedPreferencesAsync();
+    final SettingsManager settingsManager = SettingsManager();
 
-    int handlebarDistance = await prefs.getInt('handlebarDistance') ?? 0;
+    int handlebarDistance = await settingsManager.readHandlebarOffset();
+    int maxMeasurements = await settingsManager.readMaxMeasurement();
 
-    String portalServer = await prefs.getString('portalServer') ?? "";
+    String portalServer = await settingsManager.readPortalServer();
     if (portalServer.isEmpty) {
       throw Exception("No portal server set");
     }
     if (!portalServer.startsWith("https://")) {
       portalServer = "https://$portalServer";
     }
-    final portalKey = await prefs.getString('portalKey') ?? "";
+    final portalKey = await settingsManager.readPortalKey();
     if (portalKey.isEmpty) {
       throw Exception("No portal key set");
     }
 
-    String csvContent = _genCSVFile(trip, handlebarDistance);
+    String csvContent = _genCSVFile(trip, handlebarDistance,
+        maxMeasurements * MICRO_SEC_TO_CM_DIVIDER.toInt());
 
     String filename = "${trip.trackID}_${trip.createdAt.toString()}.csv";
 
diff --git a/lib/obs_list_upload_page.dart b/lib/obs_list_upload_page.dart
index 9c6e4d8..a914c49 100644
--- a/lib/obs_list_upload_page.dart
+++ b/lib/obs_list_upload_page.dart
@@ -1,5 +1,6 @@
 import 'dart:io';
 
+import 'package:app/manager/settings_manager.dart';
 import 'package:app/manager/upload_manger.dart';
 import 'package:app/providers/trip_provider.dart';
 import 'package:app/providers/upload_provider.dart';
@@ -44,13 +45,11 @@ class _ObsListUploadPageState extends State<ObsListUploadPage> {
                 onPressed: uploadStateProvider.uploading
                     ? null
                     : () async {
-                        final SharedPreferencesAsync prefs =
-                            SharedPreferencesAsync();
+                        final settingsManager = SettingsManager();
 
-                        if (((await prefs.getString('portalServer'))?.isEmpty ??
-                                true) ||
-                            ((await prefs.getString('portalKey'))?.isEmpty ??
-                                true)) {
+                        if (((await settingsManager.readPortalServer())
+                                .isEmpty) ||
+                            (await settingsManager.readPortalKey()).isEmpty) {
                           toastification.show(
                             context: context,
                             type: ToastificationType.error,
diff --git a/lib/obs_settings_page.dart b/lib/obs_settings_page.dart
index acc737e..05e6de2 100644
--- a/lib/obs_settings_page.dart
+++ b/lib/obs_settings_page.dart
@@ -1,7 +1,9 @@
 import 'package:app/database/privacy_zone.dart';
+import 'package:app/manager/settings_manager.dart';
 import 'package:app/widgets/map_page.dart';
 import 'package:app/providers/privacy_zone_provider.dart';
 import 'package:flutter/material.dart';
+import 'package:flutter/services.dart';
 import 'package:package_info_plus/package_info_plus.dart';
 import 'package:provider/provider.dart';
 import 'package:shared_preferences/shared_preferences.dart';
@@ -25,9 +27,14 @@ class _OBSSettingsPageState extends State<OBSSettingsPage> {
   final TextEditingController _portalServerController = TextEditingController();
   final TextEditingController _portalKeyController = TextEditingController();
   final TextEditingController _sentryDSNController = TextEditingController();
+  final TextEditingController _minMeasurementController =
+      TextEditingController();
+  final TextEditingController _maxMeasurementController =
+      TextEditingController();
   bool _sentryEnabled = false;
   bool _invertSensors = false;
   String? _lenkerabstandError; // Variable to hold error message
+  final SettingsManager _settingsManager = SettingsManager();
 
   @override
   void initState() {
@@ -45,8 +52,7 @@ class _OBSSettingsPageState extends State<OBSSettingsPage> {
         } else {
           setState(() {
             _lenkerabstandError = null; // Clear the error if valid
-            _saveIntValue(
-                'handlebarDistance', numberValue); // Save only if valid
+            _settingsManager.writeHandlebarOffset(numberValue);
           });
         }
       } else {
@@ -58,17 +64,18 @@ class _OBSSettingsPageState extends State<OBSSettingsPage> {
   }
 
   Future<void> _loadSavedValues() async {
-    SharedPreferencesAsync prefs = SharedPreferencesAsync();
-
-    final handlebarDistance = await prefs.getInt('handlebarDistance') ?? 0;
-    final portalServer = await prefs.getString('portalServer') ?? "";
-    final portalKey = await prefs.getString('portalKey') ?? "";
-    final sentryDSN = await prefs.getString('sentryDSN') ?? "";
-    final sentryEnabled = await prefs.getBool('sentryEnabled') ?? false;
-    final invertSensors = await prefs.getBool('invertSensors') ?? false;
-
+    final handlebarDistance = await _settingsManager.readHandlebarOffset();
+    final portalServer = await _settingsManager.readPortalServer();
+    final portalKey = await _settingsManager.readPortalKey();
+    final sentryDSN = await _settingsManager.readSentryDSN();
+    final sentryEnabled = await _settingsManager.readSentryEnabled();
+    final invertSensors = await _settingsManager.readInvertSensors();
+    final minMeasurement = await _settingsManager.readMinMeasurement();
+    final maxMeasurement = await _settingsManager.readMaxMeasurement();
     setState(() {
       _lenkerabstandController.text = handlebarDistance.toString();
+      _minMeasurementController.text = minMeasurement.toString();
+      _maxMeasurementController.text = maxMeasurement.toString();
       _portalServerController.text = portalServer;
       _portalKeyController.text = portalKey;
       _sentryDSNController.text = sentryDSN;
@@ -82,21 +89,6 @@ class _OBSSettingsPageState extends State<OBSSettingsPage> {
     return packageInfo.version;
   }
 
-  Future<void> _saveStringValue(String key, String value) async {
-    SharedPreferencesAsync prefs = SharedPreferencesAsync();
-    await prefs.setString(key, value);
-  }
-
-  Future<void> _saveIntValue(String key, int value) async {
-    SharedPreferencesAsync prefs = SharedPreferencesAsync();
-    await prefs.setInt(key, value);
-  }
-
-  Future<void> _saveBoolValue(String key, bool value) async {
-    SharedPreferencesAsync prefs = SharedPreferencesAsync();
-    await prefs.setBool(key, value);
-  }
-
   @override
   Widget build(BuildContext context) {
     final privacyZonesProvider = context.watch<PrivacyZoneProvider>();
@@ -123,16 +115,49 @@ class _OBSSettingsPageState extends State<OBSSettingsPage> {
               keyboardType: TextInputType.number,
             ),
             const SizedBox(height: 20),
+            SelectableText(localizations.settingsPageDistanceFilterDescription),
+            Row(children: [
+              Expanded(
+                child: TextField(
+                  controller: _minMeasurementController,
+                  decoration: InputDecoration(
+                    labelText: localizations.settingsPageMinMeasurementLabel,
+                  ),
+                  keyboardType: TextInputType.number,
+                  inputFormatters: [
+                    FilteringTextInputFormatter.digitsOnly,
+                  ],
+                  onChanged: (value) {
+                    _settingsManager.writeMinMeasurement(int.parse(value));
+                  },
+                ),
+              ),
+              const SizedBox(width: 5),
+              Expanded(
+                child: TextField(
+                  controller: _maxMeasurementController,
+                  decoration: InputDecoration(
+                    labelText: localizations.settingsPageMaxMeasurementLabel,
+                  ),
+                  keyboardType: TextInputType.number,
+                  inputFormatters: [
+                    FilteringTextInputFormatter.digitsOnly,
+                  ],
+                  onChanged: (value) {
+                    _settingsManager.writeMaxMeasurement(int.parse(value));
+                  },
+                ),
+              ),
+            ]),
+            const SizedBox(height: 20),
             SelectableText(localizations.settingsPageInvertSensorsDescription),
             SwitchListTile(
               title: Text(localizations.settingsPageInvertSensorsLabel),
               value: _invertSensors,
               onChanged: (value) {
+                _settingsManager.writeInvertSensors(value);
                 setState(() {
-                  _saveBoolValue('invertSensors', value);
-                  setState(() {
-                    _invertSensors = value;
-                  });
+                  _invertSensors = value;
                 });
               },
             ),
@@ -142,7 +167,7 @@ class _OBSSettingsPageState extends State<OBSSettingsPage> {
               controller: _portalServerController,
               decoration: InputDecoration(
                   labelText: localizations.settingsPagePortalServerLabel),
-              onChanged: (value) => _saveStringValue('portalServer', value),
+              onChanged: (value) => _settingsManager.writePortalServer(value),
             ),
             const SizedBox(height: 20),
             SelectableText(localizations.settingsPagePortalKeyDescription),
@@ -150,7 +175,7 @@ class _OBSSettingsPageState extends State<OBSSettingsPage> {
               controller: _portalKeyController,
               decoration: InputDecoration(
                   labelText: localizations.settingsPagePortalKeyLabel),
-              onChanged: (value) => _saveStringValue('portalKey', value),
+              onChanged: (value) => _settingsManager.writePortalKey(value),
             ),
             const SizedBox(height: 40),
             Text(localizations.settingsPageErrorReportingTitle,
@@ -160,11 +185,9 @@ class _OBSSettingsPageState extends State<OBSSettingsPage> {
               title: Text(localizations.settingsPageErrorReportingSwitchLabel),
               value: _sentryEnabled,
               onChanged: (value) {
+                _settingsManager.writeSentryEnabled(value);
                 setState(() {
-                  _saveBoolValue('sentryEnabled', value);
-                  setState(() {
-                    _sentryEnabled = value;
-                  });
+                  _sentryEnabled = value;
                 });
               },
             ),
@@ -173,7 +196,7 @@ class _OBSSettingsPageState extends State<OBSSettingsPage> {
               controller: _sentryDSNController,
               decoration: InputDecoration(
                   labelText: localizations.settingsPageSentryDSNLabel),
-              onChanged: (value) => _saveStringValue('sentryDSN', value),
+              onChanged: (value) => _settingsManager.writeSentryDSN(value),
             ),
             Divider(),
             const SizedBox(height: 20),
diff --git a/lib/widgets/measurements.dart b/lib/widgets/measurements.dart
index 88275f1..2006774 100644
--- a/lib/widgets/measurements.dart
+++ b/lib/widgets/measurements.dart
@@ -1,11 +1,11 @@
 import 'package:app/events.dart';
 import 'package:app/generated/i18n/app_localizations.dart';
 import 'package:app/manager/background_manger.dart';
+import 'package:app/manager/settings_manager.dart';
 import 'package:app/providers/distance_provider.dart';
 import 'package:flutter/material.dart';
 import 'package:app/providers/bluetooth_provider.dart';
 import 'package:provider/provider.dart';
-import 'package:shared_preferences/shared_preferences.dart';
 
 class Measurements extends StatefulWidget {
   const Measurements({super.key});
@@ -16,19 +16,18 @@ class Measurements extends StatefulWidget {
 
 class _MeasurementsState extends State<Measurements> {
   late BackgroundManager backgroundManger;
-  late SharedPreferencesAsync _prefs;
+  final _settingsManager = SettingsManager();
   bool invertSensors = false;
 
   @override
   void initState() {
     super.initState();
     backgroundManger = BackgroundManager.instance;
-    _prefs = SharedPreferencesAsync();
     getInvertSesnors();
   }
 
   Future<void> getInvertSesnors() async {
-    final val = await _prefs.getBool('invertSensors') ?? false;
+    final val = await _settingsManager.readInvertSensors();
     setState(() {
       invertSensors = val;
     });
-- 
GitLab