Commit 4723a55

derdilla <82763757+NobodyForNothing@users.noreply.github.com>
2024-05-18 16:49:39
cleanup tech debt
Signed-off-by: derdilla <82763757+NobodyForNothing@users.noreply.github.com>
1 parent 28d9b54
app/lib/bluetooth/device_scan_cubit.dart
@@ -62,8 +62,9 @@ class DeviceScanCubit extends Cubit<DeviceScanState> {
       await _flutterBluePlus.startScan(
         // no timeout, the user knows best how long scanning is needed
         withServices: [service],
-        // Might not find the device (https://pub.dev/packages/flutter_blue_plus#scanning-does-not-find-my-device)
-        // TODO: Make decision on whether to support these devices
+        // Not all devices are found using this configuration (https://pub.dev/packages/flutter_blue_plus#scanning-does-not-find-my-device).
+        // As long as no significant issues arise from this these devices are
+        // considered unsupported.
 
       );
     } catch (e) {
app/lib/components/ble_input/ble_input._dart
@@ -1,157 +0,0 @@
-import 'package:blood_pressure_app/components/ble_input/ble_input_bloc.dart';
-import 'package:blood_pressure_app/components/ble_input/ble_input_events.dart';
-import 'package:blood_pressure_app/components/ble_input/ble_input_state.dart';
-import 'package:blood_pressure_app/main.dart';
-import 'package:flutter/material.dart';
-import 'package:flutter_bloc/flutter_bloc.dart';
-import 'package:flutter_gen/gen_l10n/app_localizations.dart';
-import 'package:flutter_reactive_ble/flutter_reactive_ble.dart';
-
-/// An interactive way to add measurements over bluetooth.
-class BleInput extends StatefulWidget {
-  /// Create an interactive bluetooth measurement adder.
-  const BleInput({super.key, this.bloc});
-
-  /// Logic implementation of this input form.
-  final BleInputBloc? bloc; // Configurable for testing.
-
-  @override
-  State<BleInput> createState() => _BleInputState();
-}
-
-class _BleInputState extends State<BleInput> {
-
-  late final BleInputBloc bloc;
-
-  @override
-  void initState() {
-    super.initState();
-    bloc = widget.bloc ?? BleInputBloc();
-  }
-
-  @override
-  Widget build(BuildContext context) => SizeChangedLayoutNotifier(
-    child: BlocBuilder<BleInputBloc, BleInputState>(
-      bloc: bloc,
-      builder: (BuildContext context, BleInputState state) {
-        debugLog.add('${DateTime.now()} - STATE:${state.runtimeType}');
-        final localizations = AppLocalizations.of(context)!;
-        return switch (state) {
-          BleInputClosed() => IconButton(
-            icon: const Icon(Icons.bluetooth),
-            onPressed: () => bloc.add(OpenBleInput()),
-          ),
-          BleInputLoadInProgress() => _buildTwoElementCard(context,
-            const CircularProgressIndicator(),
-            Text(localizations.scanningDevices),
-          ),
-          BleInputLoadFailure() => _buildTwoElementCard(context,
-            const Icon(Icons.bluetooth_disabled),
-            Text(localizations.errBleCantOpen),
-            onTap: () => bloc.add(OpenBleInput()),
-          ),
-          BleInputLoadSuccess() => _buildLoadSuccess(state),
-          BleInputPermissionFailure() => _buildTwoElementCard(context,
-            const Icon(Icons.bluetooth_disabled),
-            Text(localizations.errBleNoPerms),
-            onTap: () => bloc.add(OpenBleInput()),
-          ),
-          BleConnectInProgress() => _buildTwoElementCard(context,
-            const CircularProgressIndicator(),
-            Text(localizations.bleConnecting),
-          ),
-          BleConnectFailed() => _buildTwoElementCard(context,
-            const Icon(Icons.bluetooth_disabled),
-            Text(localizations.errBleCouldNotConnect),
-            onTap: () => bloc.add(OpenBleInput()),
-          ),
-          BleConnectSuccess() => _buildTwoElementCard(context,
-            const Icon(Icons.bluetooth_connected),
-            Text(localizations.bleConnected),
-          ),
-          BleMeasurementInProgress() => _buildTwoElementCard(context,
-            const CircularProgressIndicator(),
-            Text(localizations.bleProcessing),
-          ),
-          BleMeasurementSuccess() => _buildTwoElementCard(context,
-            const Icon(Icons.done, color: Colors.lightGreen,),
-            Text('Received measurement:' // TODO: rework this process
-                '\n${state.record}'
-                '\nCuff loose: ${state.cuffLoose}'
-                '\nIrregular pulse: ${state.irregularPulse}'
-                '\nBody moved: ${state.bodyMoved}'
-                '\nWrong measurement position: ${state.improperMeasurementPosition}'
-                '\nMeasurement status: ${state.measurementStatus}'
-            ),
-          ),
-        };
-      },
-    ),
-  );
-
-  Widget _buildLoadSuccess(BleInputLoadSuccess state) {
-    debugLog.add('BleInputLoadSuccess:${state.availableDevices}');
-    // List of available ble devices
-    final localizations = AppLocalizations.of(context)!;
-    if (state.availableDevices.isEmpty) {
-      return _buildTwoElementCard(context,
-        const Icon(Icons.info),
-        Text(localizations.errBleNoDev),
-        onTap: () => bloc.add(OpenBleInput()),
-      );
-    }
-    return SizedBox(
-      height: 250,
-      child: _buildMainCard(context, ListView.builder(
-        itemCount: state.availableDevices.length,
-        itemBuilder: (context, idx) => ListTile(
-          title: Text(state.availableDevices[idx].name),
-          trailing: state.availableDevices[idx].connectable == Connectable.available
-              ? const Icon(Icons.bluetooth_audio)
-              : const Icon(Icons.bluetooth_disabled),
-          onTap: () => bloc.add(BleInputDeviceSelected(state.availableDevices[idx])),
-        ),
-      ),),
-    );
-  }
-
-  Widget _buildMainCard(BuildContext context, Widget child) => Card.outlined(
-    color: Theme.of(context).cardColor,
-    // borderRadius: BorderRadius.circular(24),
-    // width: MediaQuery.of(context).size.width,
-    // height: MediaQuery.of(context).size.width,
-    // padding: const EdgeInsets.all(24),
-    margin: const EdgeInsets.all(8),
-    child: Stack(
-      children: [
-        Padding( // content
-          padding: const EdgeInsets.all(24),
-          child: child,
-        ),
-        Align(
-          alignment: Alignment.topRight,
-          child: IconButton(
-            icon: const Icon(Icons.close),
-            onPressed: () => bloc.add(CloseBleInput()),
-          ),
-        ),
-      ],
-    ),
-  );
-
-  /// Builds the full card but with two centered elements.
-  Widget _buildTwoElementCard(
-      BuildContext context,
-      Widget top,
-      Widget bottom, {
-        void Function()? onTap,
-      }) => InkWell(
-    onTap: onTap,
-    child: _buildMainCard(context, Center(
-      child: Column(
-        mainAxisAlignment: MainAxisAlignment.center,
-        children: [top, const SizedBox(height: 8,), bottom,],
-      ),
-    ),),
-  );
-}
app/lib/components/ble_input/ble_input_bloc._dart
@@ -1,146 +0,0 @@
-import 'dart:async';
-
-import 'package:blood_pressure_app/components/ble_input/ble_input_events.dart';
-import 'package:blood_pressure_app/components/ble_input/ble_input_state.dart';
-import 'package:blood_pressure_app/components/ble_input/measurement_characteristic.dart';
-import 'package:blood_pressure_app/model/blood_pressure/record.dart';
-import 'package:flutter_bloc/flutter_bloc.dart';
-import 'package:flutter_reactive_ble/flutter_reactive_ble.dart';
-import 'package:permission_handler/permission_handler.dart';
-
-import '../../main.dart';
-
-/// Logic for bluetooth measurement input.
-class BleInputBloc extends Bloc<BleInputEvent, BleInputState> {
-  /// Create logic component for bluetooth measurement input.
-  BleInputBloc(): super(BleInputClosed()) {
-    on<OpenBleInput>(_onOpenBleInput);
-    on<CloseBleInput>(_onCloseBleInput);
-    on<BleInputDeviceSelected>(_onBleInputDeviceSelected);
-    // TODO: figure out exhaustive approach
-
-    // TODO: show capabilities during testing:
-    // _ble.getDiscoveredServices()
-
-    // Interesting available characteristics:
-    // - Battery Health Information 0x2BEB (and other battery ...)
-    // - Blood Pressure Feature 0x2A49
-    // - Device Name 0x2A00
-    // - Enhanced Blood Pressure Measurement 0x2B34
-    // - Live Health Observations 0x2B8B
-
-  }
-
-  final _ble = FlutterReactiveBle();
-
-  final Set<DiscoveredDevice> _availableDevices = {};
-
-  StreamSubscription<DiscoveredDevice>? _deviceStreamSubscribtion;
-  StreamSubscription<ConnectionStateUpdate>? _connectionUpdateStreamSubscribtion;
-
-  final _requiredServices = [
-    Uuid.parse('1810'),
-  ];
-  
-  
-  Future<void> _onOpenBleInput(OpenBleInput event, Emitter<BleInputState> emit) async {
-    emit(BleInputLoadInProgress());
-    if (await Permission.bluetoothConnect.isDenied) {
-      emit(BleInputPermissionFailure());
-      await Permission.bluetoothConnect.request();
-      return;
-    }
-    emit(BleInputLoadInProgress());
-    if (await Permission.bluetoothScan.isDenied) {
-      emit(BleInputPermissionFailure());
-      await Permission.bluetoothScan.request();
-      return;
-    }
-    emit(BleInputLoadInProgress());
-
-    try {
-      await _ble.initialize();
-      final deviceStream = _ble.scanForDevices(withServices: _requiredServices,);
-      await _deviceStreamSubscribtion?.cancel();
-      _deviceStreamSubscribtion = deviceStream.listen((device) {
-        if (!_availableDevices.any((e) => e.name == device.name)) {
-          _availableDevices.add(device);
-          emit(BleInputLoadSuccess(_availableDevices.toList()));
-        }
-      });
-      await _deviceStreamSubscribtion!.asFuture();
-    } catch (e) {
-      emit(BleInputLoadFailure());
-    }
-  }
-
-  Future<void> _onCloseBleInput(CloseBleInput event, Emitter<BleInputState> emit) async {
-    await _deviceStreamSubscribtion?.cancel();
-    await _connectionUpdateStreamSubscribtion?.cancel();
-    await _ble.deinitialize();
-    emit(BleInputClosed());
-    // TODO: cleanup
-  }
-
-  Future<void> _onBleInputDeviceSelected(BleInputDeviceSelected event, Emitter<BleInputState> emit) async {
-    await _deviceStreamSubscribtion?.cancel();
-    emit(BleConnectInProgress());
-    try {
-      await _connectionUpdateStreamSubscribtion?.cancel();
-      _connectionUpdateStreamSubscribtion = _ble.connectToAdvertisingDevice(
-        id: event.device.id,
-        prescanDuration: const Duration(seconds: 5),
-        withServices: _requiredServices,
-        connectionTimeout: const Duration(minutes: 2),
-      ).listen((update) {
-        if (update.failure != null) {
-          emit(BleConnectFailed());
-        } else if (update.connectionState == DeviceConnectionState.connected) {
-          // characteristics IDs (https://www.bluetooth.com/wp-content/uploads/Files/Specification/HTML/Assigned_Numbers/out/en/Assigned_Numbers.pdf?v=1711151578821):
-          // - Blood Pressure Measurement: 0x2A35 (https://bitbucket.org/bluetooth-SIG/public/src/main/gss/org.bluetooth.characteristic.blood_pressure_measurement.yaml)
-          // - Blood Pressure Records: 0x2A36 (https://bitbucket.org/bluetooth-SIG/public/src/main/gss/org.bluetooth.characteristic.blood_pressure_record.yaml)
-          //
-          // A record represents a stored measurement, so in theory we should
-          // search for a measurement.
-          // Definition: https://www.bluetooth.com/specifications/bls-1-1-1/
-          final characteristic = QualifiedCharacteristic(
-            characteristicId: Uuid.parse('2A35'),
-            serviceId: Uuid.parse('1810'),
-            deviceId: event.device.id,
-          );
-          // TODO: extract subscription
-          _ble.subscribeToCharacteristic(characteristic).listen((List<int> data) async {
-            await _deviceStreamSubscribtion?.cancel();
-            await _connectionUpdateStreamSubscribtion?.cancel();
-            emit(BleMeasurementInProgress());
-            debugLog.add('BLE MESSAGE: $data');
-            final decoded = BPMeasurementCharacteristic.parse(data);
-            final record = BloodPressureRecord(
-              decoded.time ?? DateTime.now(),
-              // TODO: unit conversions
-              decoded.sys.toInt(),
-              decoded.dia.toInt(),
-              decoded.pul?.toInt(),
-              '',
-            );
-            emit(BleMeasurementSuccess(record,
-              bodyMoved: decoded.bodyMoved,
-              cuffLoose: decoded.cuffLoose,
-              irregularPulse: decoded.irregularPulse,
-              improperMeasurementPosition: decoded.improperMeasurementPosition,
-              measurementStatus: decoded.measurementStatus,
-            ),);
-          });
-          emit(BleConnectSuccess());
-        } else if (update.connectionState == DeviceConnectionState.connecting) {
-          emit(BleConnectInProgress());
-        } else {
-          emit(BleConnectFailed());
-        }
-      });
-      await _connectionUpdateStreamSubscribtion!.asFuture();
-    } on TimeoutException {
-      emit(BleConnectFailed());
-    }
-  }
-}
app/lib/components/ble_input/ble_input_events._dart
@@ -1,19 +0,0 @@
-import 'package:flutter_reactive_ble/flutter_reactive_ble.dart';
-
-/// Bluetooth measurement event.
-sealed class BleInputEvent {}
-
-/// Request expanding the input field.
-class OpenBleInput extends BleInputEvent {}
-
-/// Request closing the input field.
-class CloseBleInput extends BleInputEvent {}
-
-/// Connection with a device has been requested.
-class BleInputDeviceSelected extends BleInputEvent {
-  /// Request connection with a device.
-  BleInputDeviceSelected(this.device);
-
-  /// The device to connect with.
-  final DiscoveredDevice device;
-}
app/lib/components/ble_input/ble_input_state._dart
@@ -1,77 +0,0 @@
-import 'package:blood_pressure_app/components/ble_input/measurement_characteristic.dart';
-import 'package:blood_pressure_app/model/blood_pressure/record.dart';
-import 'package:flutter_reactive_ble/flutter_reactive_ble.dart';
-
-/// State of a component for inputting measurements through ble devices
-sealed class BleInputState {}
-
-/// The ble input field is inactive (not opened).
-class BleInputClosed extends BleInputState {}
-
-/// Doesn't have permission for bluetooth access.
-///
-/// The UI should show a warning to allow bluetooth and potentially location
-/// permissions.
-class BleInputPermissionFailure extends BleInputState {}
-
-/// Scanning for devices.
-class BleInputLoadInProgress extends BleInputState {}
-/// Could not start bluetooth search.
-///
-/// Most permissions errors should be covered by [BleInputPermissionFailure] so
-/// this might not be actionable by the user.
-class BleInputLoadFailure extends BleInputState {}
-/// Devices have been found and need selecting.
-class BleInputLoadSuccess extends BleInputState {
-  /// Devices have been found and need selecting.
-  BleInputLoadSuccess(this.availableDevices);
-
-  /// List of all unique devices reported by the ble lib.
-  final List<DiscoveredDevice> availableDevices;
-
-  @override
-  String toString() => 'BleInputLoadSuccess{availableDevices: $availableDevices}';
-}
-
-/// Connecting to device.
-class BleConnectInProgress extends BleInputState {}
-/// Couldn't connect to device or closed connection.
-class BleConnectFailed extends BleInputState {}
-/// Is connected with device.
-class BleConnectSuccess extends BleInputState {}
-
-/// Received information about an blood pressure measurement.
-class BleMeasurementInProgress extends BleInputState {}
-
-/// A measurement was taken through the bluetooth device.
-class BleMeasurementSuccess extends BleInputState {
-  /// A measurement that was taken through the bluetooth device.
-  BleMeasurementSuccess(this.record, {
-    this.bodyMoved,
-    this.cuffLoose,
-    this.irregularPulse,
-    this.measurementStatus,
-    this.improperMeasurementPosition,
-  });
-
-  /// Measured blood pressure data.
-  final BloodPressureRecord record;
-
-  /// Whether body movement was detected during measurement.
-  bool? bodyMoved;
-
-  /// Whether the cuff was too loose during measurement.
-  bool? cuffLoose;
-
-  /// Whether irregular pulse was detected.
-  bool? irregularPulse;
-
-  /// The range the pulse rate was in.
-  MeasurementStatus? measurementStatus;
-
-  /// Whether the measurement was taken at an improper position.
-  bool? improperMeasurementPosition;
-
-  @override
-  String toString() => 'BleMeasurementSuccess{record: $record, bodyMoved: $bodyMoved, cuffLoose: $cuffLoose, irregularPulse: $irregularPulse, measurementStatus: $measurementStatus, improperMeasurementPosition: $improperMeasurementPosition}';
-}
app/lib/components/ble_input/tmp
@@ -1,107 +0,0 @@
-import 'dart:async';
-
-import 'package:flutter_blue_plus/flutter_blue_plus.dart';
-
-class BeforeScanning {
-  late StreamSubscription<BluetoothAdapterState> _adapterStateStateSubscription;
-
-  // Must be: `on`
-  BluetoothAdapterState _adapterState = BluetoothAdapterState.unknown;
-
-  init() {
-    _adapterStateStateSubscription = FlutterBluePlus.adapterState.listen((state) {
-      setState(() {
-        _adapterState = state;
-      });
-    });
-  }
-
-  dispose() {
-    _adapterStateStateSubscription.cancel();
-  }
-
-  enableBluetooth() {
-    try {
-      if (Platform.isAndroid) {
-        await FlutterBluePlus.turnOn();
-      }
-    } catch (e) {
-      Snackbar.show(ABC.a, prettyException("Error Turning On:", e), success: false);
-    }
-  }
-}
-
-class ScanningPhase {
-  List<BluetoothDevice> _systemDevices = [];
-  List<ScanResult> _scanResults = [];
-  bool _isScanning = false;
-  late StreamSubscription<List<ScanResult>> _scanResultsSubscription;
-  late StreamSubscription<bool> _isScanningSubscription;
-
-  initState() {
-    super.initState();
-
-    _scanResultsSubscription = FlutterBluePlus.scanResults.listen((results) {
-      _scanResults = results;
-    }, onError: (e) {
-      Snackbar.show(ABC.b, prettyException("Scan Error:", e), success: false);
-    });
-
-    _isScanningSubscription = FlutterBluePlus.isScanning.listen((state) {
-      _isScanning = state;
-    });
-  }
-
-  dispose() {
-    _scanResultsSubscription.cancel();
-    _isScanningSubscription.cancel();
-    super.dispose();
-  }
-
-  onScanPressed() async {
-    try {
-      _systemDevices = await FlutterBluePlus.systemDevices;
-    } catch (e) {
-      Snackbar.show(ABC.b, prettyException("System Devices Error:", e), success: false);
-    }
-    try {
-      await FlutterBluePlus.startScan(timeout: const Duration(seconds: 15));
-    } catch (e) {
-      Snackbar.show(ABC.b, prettyException("Start Scan Error:", e), success: false);
-    }
-  }
-
-  onStopPressed() async {
-    try {
-      FlutterBluePlus.stopScan();
-    } catch (e) {
-      Snackbar.show(ABC.b, prettyException("Stop Scan Error:", e), success: false);
-    }
-  }
-}
-
-class DeviceInfo {
-
-  userInfo() {
-    widget.result._device.platformName.isNotEmpty
-        ? widget.result._device.platformName
-        : widget.result._device.remoteId.str
-  }
-
-  bool get isConnectable => widget.result.advertisementData.connectable;
-
-  connect() {
-    _device.connectAndUpdateStream().catchError((e) {
-      Snackbar.show(ABC.c, prettyException("Connect Error:", e), success: false);
-    });
-  }
-}
-
-class DeviceConnection {
-  late StreamSubscription<BluetoothConnectionState> _connectionStateSubscription;
-  late StreamSubscription<bool> _isConnectingSubscription;
-  late StreamSubscription<bool> _isDisconnectingSubscription;
-  late StreamSubscription<int> _mtuSubscription;
-
-  // https://github.com/boskokg/flutter_blue_plus/blob/master/example/lib/screens/device_screen.dart
-}
\ No newline at end of file
app/lib/components/bluetooth_input.dart
@@ -42,17 +42,18 @@ class _BluetoothInputState extends State<BluetoothInput> {
   DeviceScanCubit? _deviceScanCubit;
 
   @override
-  void dispose() {
-    _bluetoothSubscription?.cancel();
-    _bluetoothCubit.close();
-    _deviceScanCubit?.close();
+  void dispose() async {
+    await _bluetoothSubscription?.cancel();
+    await _bluetoothCubit.close();
+    await _deviceScanCubit?.close();
     super.dispose();
   }
 
-  void _returnToIdle() {
-    _bluetoothSubscription?.cancel();
+  void _returnToIdle() async {
+    await  _bluetoothSubscription?.cancel();
     _bluetoothSubscription = null;
-    _deviceScanCubit?.close().then((_) => _deviceScanCubit = null);
+    await _deviceScanCubit?.close();
+    _deviceScanCubit = null;
     if (_isActive) {
       setState(() {
         _isActive = false;
@@ -60,8 +61,6 @@ class _BluetoothInputState extends State<BluetoothInput> {
     }
   }
 
-  // TODO: bloc dispose
-
   Widget _buildActive(BuildContext context) {
     _bluetoothSubscription = _bluetoothCubit.stream.listen((state) {
       if (state is! BluetoothReady) _returnToIdle();
@@ -130,8 +129,6 @@ class _BluetoothInputState extends State<BluetoothInput> {
       },
     );
   }
-  // TODO: scanning devices info
-
 
   Widget _buildMainCard(BuildContext context, {
     required Widget child,