Commit b26fa61

derdilla <82763757+NobodyForNothing@users.noreply.github.com>
2024-05-10 21:28:39
cleanup tech debt and bugs
Signed-off-by: derdilla <82763757+NobodyForNothing@users.noreply.github.com>
1 parent 89759ad
Changed files (5)
app/lib/bluetooth/bluetooth_cubit.dart
@@ -45,7 +45,14 @@ class BluetoothCubit extends Cubit<BluetoothState> {
         emit(BluetoothUnauthorized());
         await requestPermission();
       case BluetoothAdapterState.on:
-        emit(BluetoothReady());
+        if (await Permission.bluetoothConnect.isGranted) {
+          emit(BluetoothReady());
+          Permission.bluetoothConnect
+            .onGrantedCallback(() => _onAdapterStateChanged(state));
+        } else {
+          emit(BluetoothUnauthorized());
+        }
+
       case BluetoothAdapterState.off:
       case BluetoothAdapterState.turningOff:
       case BluetoothAdapterState.turningOn:
@@ -84,4 +91,14 @@ class BluetoothCubit extends Cubit<BluetoothState> {
       return false;
     }
   }
+
+  /// Reevaluate the current state.
+  ///
+  /// When the user is in another app like the device settings, sometimes
+  /// the app won't get notified about permission changes and such. In those
+  /// instances the user should have the option to manually recheck the state to
+  /// avoid getting stuck on a unauthorized state.
+  Future<void> forceRefresh() async {
+    _onAdapterStateChanged(_flutterBluePlus.adapterStateNow);
+  }
 }
app/lib/bluetooth/device_scan_cubit.dart
@@ -27,7 +27,7 @@ class DeviceScanCubit extends Cubit<DeviceScanState> {
     required this.settings,
   }) : _flutterBluePlus = flutterBluePlus ?? FlutterBluePlusMockable(),
         super(DeviceListLoading()) {
-    assert(!_flutterBluePlus.isScanningNow, '');
+    assert(!_flutterBluePlus.isScanningNow);
     _startScanning();
   }
 
@@ -44,6 +44,12 @@ class DeviceScanCubit extends Cubit<DeviceScanState> {
   @override
   Future<void> close() async {
     await _scanResultsSubscription.cancel();
+    try {
+      await _flutterBluePlus.stopScan();
+    } catch (e) {
+      Log.err('Failed to stop scanning', [e]);
+      return;
+    }
     await super.close();
   }
 
app/lib/components/bluetooth_input.dart
@@ -14,6 +14,7 @@ class BluetoothInput extends StatefulWidget {
   /// Create a measurement input through bluetooth.
   const BluetoothInput({super.key, required this.settings});
 
+  /// Settings to store known devices.
   final Settings settings;
 
   @override
@@ -24,12 +25,16 @@ class _BluetoothInputState extends State<BluetoothInput> {
   /// Whether the user expanded bluetooth input
   bool _isActive = false;
 
+  // TODO: return values
+
   final BluetoothCubit _bluetoothCubit =  BluetoothCubit();
   StreamSubscription<BluetoothState>? _bluetoothSubscription;
+  DeviceScanCubit? _deviceScanCubit;
 
   void _returnToIdle() {
     _bluetoothSubscription?.cancel();
     _bluetoothSubscription = null;
+    _deviceScanCubit?.close().then((_) => _deviceScanCubit = null);
     if (_isActive) {
       setState(() {
         _isActive = false;
@@ -45,31 +50,28 @@ class _BluetoothInputState extends State<BluetoothInput> {
         child: CircularProgressIndicator(),
       ),
       BluetoothUnfeasible() => const SizedBox.shrink(),
-      BluetoothUnauthorized() => Align(
-        alignment: Alignment.topRight,
-        child: Row(
-          mainAxisSize: MainAxisSize.min,
-          children: [
-            Text(AppLocalizations.of(context)!.errBleNoPerms),
-            const Icon(Icons.bluetooth_disabled),
-            // TODO: tapable
-          ],
-        ),
+      BluetoothUnauthorized() => ListTile(
+        title: Text(AppLocalizations.of(context)!.errBleNoPerms),
+        leading: const Icon(Icons.bluetooth_disabled),
+        onTap: () async {
+          // TODO: test
+          await _bluetoothCubit.requestPermission();
+          await _bluetoothCubit.forceRefresh();
+        },
+        // TODO: add information icon
       ),
-      BluetoothDisabled() => Align(
-        alignment: Alignment.topRight,
-        child: Row(
-          mainAxisSize: MainAxisSize.min,
-          children: [
-            Text(AppLocalizations.of(context)!.bluetoothDisabled),
-            const Icon(Icons.bluetooth_disabled),
-            // TODO: tapable
-          ],
-        ),
+      BluetoothDisabled() => ListTile(
+        title: Text(AppLocalizations.of(context)!.bluetoothDisabled),
+        leading: const Icon(Icons.bluetooth_disabled),
+        onTap: () async {
+          await _bluetoothCubit.enableBluetooth();
+          await _bluetoothCubit.forceRefresh();
+        },
       ),
-      BluetoothReady() => IconButton(
-        icon: const Icon(Icons.bluetooth),
-        onPressed: () => setState(() => _isActive = true),
+      BluetoothReady() => ListTile(
+        leading: const Icon(Icons.bluetooth),
+        title: Text(AppLocalizations.of(context)!.bluetoothDisabled),
+        onTap: () => setState(() => _isActive = true),
       ),
     },
   );
@@ -78,12 +80,12 @@ class _BluetoothInputState extends State<BluetoothInput> {
     _bluetoothSubscription = _bluetoothCubit.stream.listen((state) {
       if (state is! BluetoothReady) _returnToIdle();
     });
-    final deviceScanBloc = DeviceScanCubit(
+    _deviceScanCubit = DeviceScanCubit(
       service: Guid('1810'),
       settings: widget.settings,
     );
     return BlocBuilder<DeviceScanCubit, DeviceScanState>(
-      bloc: deviceScanBloc,
+      bloc: _deviceScanCubit,
       builder: (context, DeviceScanState state) => switch(state) {
         DeviceListLoading() => _buildMainCard(context,
           child: const CircularProgressIndicator()),
@@ -93,9 +95,8 @@ class _BluetoothInputState extends State<BluetoothInput> {
             children: [
               for (final d in state.devices)
                 ListTile(
-                  // TODO: consider only passing the string
                   title: Text(d.device.platformName),
-                  onTap: () => deviceScanBloc.acceptDevice(d.device),
+                  onTap: () => _deviceScanCubit!.acceptDevice(d.device),
                 ),
             ],
           ),
@@ -105,13 +106,14 @@ class _BluetoothInputState extends State<BluetoothInput> {
               .connectTo(state.device.device.platformName)),
           child: FilledButton(
             child: Text(AppLocalizations.of(context)!.connect),
-            onPressed: () => deviceScanBloc.acceptDevice(state.device.device),
+            onPressed: () => _deviceScanCubit!.acceptDevice(state.device.device),
           ),
         ),
         DeviceSelected() => BlocBuilder<BleReadCubit, BleReadState>(
           bloc: BleReadCubit(state.device),
           builder: (BuildContext context, BleReadState state) => switch (state) {
-            BleReadInProgress() => _buildMainCard(context, child: CircularProgressIndicator()),
+            BleReadInProgress() => _buildMainCard(context,
+              child: const CircularProgressIndicator()),
             BleReadFailure() => _buildMainCard(context,
               child: Center(
                 child: Column(
@@ -136,7 +138,6 @@ class _BluetoothInputState extends State<BluetoothInput> {
                 ),
               ),
             ),
-
           },
         ),
       },
@@ -153,7 +154,7 @@ class _BluetoothInputState extends State<BluetoothInput> {
   Widget _buildMainCard(BuildContext context, {
     required Widget child,
     Widget? title,
-  }) => Card.outlined(
+  }) => Card(
     color: Theme.of(context).cardColor,
     // borderRadius: BorderRadius.circular(24),
     // width: MediaQuery.of(context).size.width,
@@ -164,7 +165,9 @@ class _BluetoothInputState extends State<BluetoothInput> {
       children: [
         Padding( // content
           padding: const EdgeInsets.all(24),
-          child: child,
+          child: Center(
+            child: child,
+          ),
         ),
         if (title != null)
           Align(
@@ -175,18 +178,10 @@ class _BluetoothInputState extends State<BluetoothInput> {
           alignment: Alignment.topRight,
           child: IconButton(
             icon: const Icon(Icons.close),
-            onPressed: () => null, // TODO
+            onPressed: _returnToIdle,
           ),
         ),
       ],
     ),
   );
 }
-
-enum _State {
-  /// Default state of the widget shown when no interaction happened.
-  idle,
-  /// Scanning and selecting devices.
-  deviceScan,
-  measurementRead,
-}
app/lib/l10n/app_en.arb
@@ -529,5 +529,7 @@
   "measurementSuccess": "Measurement taken successfully",
   "@measurementSuccess": {},
   "connect": "Connect",
-  "@connect": {}
+  "@connect": {},
+  "bluetoothInput": "Bluetooth input",
+  "@bluetoothInput": {}
 }
app/lib/screens/home_screen.dart
@@ -1,3 +1,4 @@
+import 'package:blood_pressure_app/components/bluetooth_input.dart';
 import 'package:blood_pressure_app/components/dialoges/add_measurement_dialoge.dart';
 import 'package:blood_pressure_app/components/measurement_list/measurement_list.dart';
 import 'package:blood_pressure_app/model/blood_pressure/medicine/intake_history.dart';
@@ -65,6 +66,7 @@ class AppHome extends StatelessWidget {
               Consumer<IntervallStoreManager>(builder: (context, intervalls, child) =>
               Consumer<Settings>(builder: (context, settings, child) =>
                 Column(children: [
+                  BluetoothInput(settings: Settings(),),
                   const MeasurementGraph(),
                   Expanded(
                     child: (settings.useLegacyList) ?