@@ -11,6 +11,7 @@ import 'package:flutter_foreground_task/flutter_foreground_task.dart';
1111import 'package:hive_ce_flutter/adapters.dart' ;
1212import 'package:logging/logging.dart' as log;
1313import 'package:permission_handler/permission_handler.dart' ;
14+ import 'package:riverpod/src/framework.dart' ;
1415import 'package:riverpod_annotation/riverpod_annotation.dart' ;
1516import 'package:tail_app/Backend/command_history.dart' ;
1617import 'package:tail_app/Backend/command_queue.dart' ;
@@ -42,7 +43,7 @@ class InitFlutterBluePlus extends _$InitFlutterBluePlus {
4243
4344 @override
4445 Future <void > build () async {
45- if (ref.read (getBluetoothPermissionProvider.future) == BluetoothPermissionStatus .denied) {
46+ if (await ref.read (getBluetoothPermissionProvider.future) == BluetoothPermissionStatus .denied) {
4647 ref.invalidateSelf ();
4748 _bluetoothPlusLogger.info ("Bluetooth permission not granted" );
4849 return ;
@@ -79,7 +80,6 @@ class InitFlutterBluePlus extends _$InitFlutterBluePlus {
7980 ref.watch (_onScanResultsProvider);
8081 // Shut down bluetooth related things
8182 ref.onDispose (() async {
82- stopScan ();
8383 //Disconnect any gear
8484 for (var element in FlutterBluePlus .connectedDevices) {
8585 await disconnect (element.remoteId.str);
@@ -102,7 +102,7 @@ class InitFlutterBluePlus extends _$InitFlutterBluePlus {
102102 isBluetoothEnabled.value = false ;
103103 _didInitFlutterBluePlus = false ; // Allow restarting ble stack
104104 });
105- ref.read (scanMonitorProvider );
105+ ref.read (scanProvider );
106106 }
107107}
108108
@@ -242,17 +242,19 @@ class _OnConnectionStateChanged extends _$OnConnectionStateChanged {
242242 ..fine ('Requesting notification permission' )
243243 ..finer ('Requesting notification permission result${await Permission .notification .request ()}' ); // Used only for Foreground service
244244 FlutterForegroundTask .init (
245- androidNotificationOptions: AndroidNotificationOptions (
246- channelId: 'foreground_service' ,
247- channelName: 'Gear Connected' ,
248- channelDescription: 'This notification appears when any gear is running.' ,
249- channelImportance: NotificationChannelImportance .LOW ,
250- priority: NotificationPriority .LOW ,
251- ),
252- iosNotificationOptions: const IOSNotificationOptions (),
253- foregroundTaskOptions: ForegroundTaskOptions (
254- eventAction: ForegroundTaskEventAction .nothing (),
255- ));
245+ androidNotificationOptions: AndroidNotificationOptions (
246+ channelId: 'foreground_service' ,
247+ channelName: 'Gear Connected' ,
248+ channelDescription: 'This notification appears when any gear is running.' ,
249+ channelImportance: NotificationChannelImportance .LOW ,
250+ priority: NotificationPriority .LOW ,
251+ ),
252+ iosNotificationOptions: const IOSNotificationOptions (),
253+ foregroundTaskOptions: ForegroundTaskOptions (
254+ eventAction: ForegroundTaskEventAction .repeat (500 ),
255+ allowWakeLock: true ,
256+ ),
257+ );
256258 FlutterForegroundTask .startService (
257259 notificationTitle: "Gear Connected" ,
258260 notificationText: "Gear is connected to The Tail Company app" ,
@@ -520,41 +522,86 @@ Future<void> connect(String id) async {
520522 }
521523}
522524
523- enum ScanReason { background, addGear, manual, notScanning }
525+ @Riverpod (keepAlive: true )
526+ class Scan extends _$Scan {
527+ StreamSubscription <bool >? isScanningStreamSubscription;
528+ @override
529+ ScanReason build () {
530+ isScanningStreamSubscription = FlutterBluePlus .isScanning.listen (onIsScanningChange);
524531
525- ScanReason _scanReason = ScanReason .notScanning ;
532+ ref. listen (isAllKnownGearConnectedProvider, isAllKnownGearConnectedProviderListener) ;
526533
527- Future <void > beginScan ({required ScanReason scanReason, Duration ? timeout}) async {
528- if (_didInitFlutterBluePlus && ! FlutterBluePlus .isScanningNow && isBluetoothEnabled.value) {
529- _bluetoothPlusLogger.info ("Starting scan" );
530- _scanReason = scanReason;
531- await FlutterBluePlus .startScan (withServices: DeviceRegistry .getAllIds ().map (Guid .new ).toList (), continuousUpdates: timeout == null , androidScanMode: AndroidScanMode .lowPower, timeout: timeout);
534+ Hive .box (settings).listenable (keys: [hasCompletedOnboarding])
535+ ..removeListener (isAllGearConnectedListener)
536+ ..addListener (isAllGearConnectedListener);
537+ isBluetoothEnabled
538+ ..removeListener (isAllGearConnectedListener)
539+ ..addListener (isAllGearConnectedListener);
540+ Future .delayed (
541+ Duration (milliseconds: 5 ),
542+ () => isAllGearConnectedListener (),
543+ );
544+
545+ ref.onDispose (
546+ () {
547+ isScanningStreamSubscription? .cancel ();
548+ stopScan ();
549+ },
550+ );
551+ return ScanReason .notScanning;
532552 }
533- }
534553
535- bool isScanningNow () {
536- if (! _didInitFlutterBluePlus) {
537- return false ;
554+ void isAllKnownGearConnectedProviderListener (bool ? previous, bool next) {
555+ isAllGearConnectedListener ();
538556 }
539- return FlutterBluePlus .isScanningNow;
540- }
541557
542- Stream <bool > isScanning () {
543- if (! _didInitFlutterBluePlus) {
544- return Stream .value (false );
558+ void onIsScanningChange (bool isScanning) {
559+ if (state != ScanReason .notScanning && ! isScanning) {
560+ state = ScanReason .notScanning;
561+ }
545562 }
546- return FlutterBluePlus .isScanning;
547- }
548563
549- Future <void > stopScan () async {
550- if (! _didInitFlutterBluePlus) {
551- return ;
564+ Future <void > beginScan ({required ScanReason scanReason, Duration ? timeout}) async {
565+ if (_didInitFlutterBluePlus && ! FlutterBluePlus .isScanningNow && isBluetoothEnabled.value) {
566+ _bluetoothPlusLogger.info ("Starting scan" );
567+ state = scanReason;
568+ await FlutterBluePlus .startScan (withServices: DeviceRegistry .getAllIds ().map (Guid .new ).toList (), continuousUpdates: timeout == null , androidScanMode: AndroidScanMode .lowPower, timeout: timeout);
569+ }
570+ }
571+
572+ void stopActiveScan () {
573+ if (state == ScanReason .addGear) {
574+ state = ScanReason .background;
575+ }
576+ isAllGearConnectedListener ();
577+ }
578+
579+ Future <void > stopScan () async {
580+ if (! _didInitFlutterBluePlus) {
581+ return ;
582+ }
583+ _bluetoothPlusLogger.info ("stopScan called" );
584+ await FlutterBluePlus .stopScan ();
585+ state = ScanReason .notScanning;
586+ }
587+
588+ void isAllGearConnectedListener () {
589+ if (! ref.exists (isAllKnownGearConnectedProvider)) {
590+ return ;
591+ }
592+
593+ bool allConnected = ref.read (isAllKnownGearConnectedProvider);
594+ bool isInOnboarding = HiveProxy .getOrDefault (settings, hasCompletedOnboarding, defaultValue: hasCompletedOnboardingDefault) < hasCompletedOnboardingVersionToAgree;
595+ if ((! allConnected || isInOnboarding) && isBluetoothEnabled.value) {
596+ beginScan (scanReason: ScanReason .background);
597+ } else if ((allConnected && ! isInOnboarding && state == ScanReason .background) || ! isBluetoothEnabled.value) {
598+ stopScan ();
599+ }
552600 }
553- _bluetoothPlusLogger.info ("stopScan called" );
554- await FlutterBluePlus .stopScan ();
555- _scanReason = ScanReason .notScanning;
556601}
557602
603+ enum ScanReason { background, addGear, notScanning }
604+
558605Future <void > sendMessage (BaseStatefulDevice device, List <int > message, {bool withoutResponse = false }) async {
559606 if (! _didInitFlutterBluePlus) {
560607 return ;
@@ -578,27 +625,16 @@ Future<void> sendMessage(BaseStatefulDevice device, List<int> message, {bool wit
578625 }
579626}
580627
581- @Riverpod (keepAlive: true )
582- class ScanMonitor extends _$ScanMonitor {
583- @override
584- void build () {
585- ref.watch (isAllKnownGearConnectedProvider);
586- Hive .box (settings).listenable (keys: [alwaysScanning])
587- ..removeListener (listener)
588- ..addListener (listener);
589- isBluetoothEnabled
590- ..removeListener (listener)
591- ..addListener (listener);
592- listener ();
628+ bool isScanningNow () {
629+ if (! _didInitFlutterBluePlus) {
630+ return false ;
593631 }
632+ return FlutterBluePlus .isScanningNow;
633+ }
594634
595- void listener () {
596- bool allConnected = ref.read (isAllKnownGearConnectedProvider);
597- bool alwaysScanningValue = HiveProxy .getOrDefault (settings, alwaysScanning, defaultValue: alwaysScanningDefault);
598- if (! allConnected && alwaysScanningValue && isBluetoothEnabled.value) {
599- beginScan (scanReason: ScanReason .background);
600- } else if ((allConnected && _scanReason == ScanReason .background) || ! isBluetoothEnabled.value) {
601- stopScan ();
602- }
635+ Stream <bool > isScanning () {
636+ if (! _didInitFlutterBluePlus) {
637+ return Stream .value (false );
603638 }
639+ return FlutterBluePlus .isScanning;
604640}
0 commit comments