diff --git a/open_wearable/android/gradle.properties b/open_wearable/android/gradle.properties index 25971708..4147ba38 100644 --- a/open_wearable/android/gradle.properties +++ b/open_wearable/android/gradle.properties @@ -1,3 +1,7 @@ org.gradle.jvmargs=-Xmx4G -XX:MaxMetaspaceSize=2G -XX:+HeapDumpOnOutOfMemoryError android.useAndroidX=true android.enableJetifier=true +# This builtInKotlin flag was added automatically by Flutter migrator +android.builtInKotlin=false +# This newDsl flag was added automatically by Flutter migrator +android.newDsl=false diff --git a/open_wearable/lib/main.dart b/open_wearable/lib/main.dart index d0a34cce..b9ed66e0 100644 --- a/open_wearable/lib/main.dart +++ b/open_wearable/lib/main.dart @@ -190,25 +190,37 @@ class _MyAppState extends State with WidgetsBindingObserver { appBannerController.showBanner( (id) { final colorScheme = Theme.of(context).colorScheme; - final bool isError = event is WearableErrorEvent; + final severity = + event is WearableErrorEvent ? event.severity : null; + final bool isError = severity == DeviceErrorLevel.error || + severity == DeviceErrorLevel.fatal; + final bool isWarning = severity == DeviceErrorLevel.warning; final bool isTimeSync = event is WearableTimeSynchronizedEvent; const timeSyncBackground = Color(0xFFEDE4FF); const timeSyncForeground = Color(0xFF5A2EA6); + const warningBackground = Color(0xFFFFF3CD); + const warningForeground = Color(0xFF6B4E00); final backgroundColor = isError ? colorScheme.errorContainer - : isTimeSync - ? timeSyncBackground - : colorScheme.primaryContainer; + : isWarning + ? warningBackground + : isTimeSync + ? timeSyncBackground + : colorScheme.primaryContainer; final textColor = isError ? colorScheme.onErrorContainer - : isTimeSync - ? timeSyncForeground - : colorScheme.onPrimaryContainer; + : isWarning + ? warningForeground + : isTimeSync + ? timeSyncForeground + : colorScheme.onPrimaryContainer; final icon = isError ? Icons.error_outline_rounded - : isTimeSync - ? Icons.schedule_rounded - : Icons.info_outline_rounded; + : isWarning + ? Icons.warning_amber_rounded + : isTimeSync + ? Icons.schedule_rounded + : Icons.info_outline_rounded; final textStyle = Theme.of(context).textTheme.bodyMedium?.copyWith( color: textColor, fontWeight: FontWeight.w600, diff --git a/open_wearable/lib/view_models/wearables_provider.dart b/open_wearable/lib/view_models/wearables_provider.dart index f81bba4e..0f0d2235 100644 --- a/open_wearable/lib/view_models/wearables_provider.dart +++ b/open_wearable/lib/view_models/wearables_provider.dart @@ -6,7 +6,7 @@ import 'package:open_wearable/models/device_name_formatter.dart'; import 'package:open_wearable/models/wearable_display_group.dart'; import 'package:open_wearable/models/wearable_status_cache.dart'; import 'package:open_wearable/view_models/sensor_configuration_provider.dart'; - +import 'package:open_earable_flutter/src/models/error/sensor_error.dart'; import '../models/logger.dart'; /// Event emitted when a newer firmware version is available for a wearable. @@ -75,9 +75,12 @@ class WearableTimeSynchronizedEvent extends WearableEvent { /// Emitted when wearable-side operations fail and should surface in UI. class WearableErrorEvent extends WearableEvent { final String errorMessage; + final DeviceErrorLevel severity; + WearableErrorEvent({ required super.wearable, required this.errorMessage, + this.severity = DeviceErrorLevel.error, String? description, }) : super( description: description ?? @@ -156,6 +159,7 @@ class WearablesProvider with ChangeNotifier { _wearableEventController.stream; final Map _capabilitySubscriptions = {}; + final Map _errorSubscriptions = {}; // MARK: Internal helpers @@ -179,12 +183,14 @@ class WearablesProvider with ChangeNotifier { void _emitWearableError({ required Wearable wearable, required String errorMessage, + DeviceErrorLevel severity = DeviceErrorLevel.error, String? description, }) { _emitWearableEvent( WearableErrorEvent( wearable: wearable, errorMessage: errorMessage, + severity: severity, description: description, ), ); @@ -282,7 +288,7 @@ class WearablesProvider with ChangeNotifier { ), ); } - + _handleWearableErrors(wearable); // Disconnect listener (sync) wearable.addDisconnectListener(() { removeWearable(wearable); @@ -506,6 +512,7 @@ class WearablesProvider with ChangeNotifier { _wearables.remove(wearable); _sensorConfigurationProviders.remove(wearable)?.dispose(); _capabilitySubscriptions.remove(wearable)?.cancel(); + _errorSubscriptions.remove(wearable)?.cancel(); if (!_disposed) { notifyListeners(); } @@ -552,6 +559,10 @@ class WearablesProvider with ChangeNotifier { unawaited(sub.cancel()); } _capabilitySubscriptions.clear(); + for (final sub in _errorSubscriptions.values) { + unawaited(sub.cancel()); + } + _errorSubscriptions.clear(); for (final provider in _sensorConfigurationProviders.values) { provider.dispose(); @@ -564,4 +575,28 @@ class WearablesProvider with ChangeNotifier { unawaited(_wearableEventController.close()); super.dispose(); } + + void _handleWearableErrors(Wearable wearable) { + _errorSubscriptions[wearable]?.cancel(); + _errorSubscriptions[wearable] = wearable.onError.listen((error) { + if (error is SensorError) { + _emitWearableEvent( + WearableErrorEvent( + wearable: wearable, + errorMessage: error.formattedMessage, + severity: error.level, + description: error.formattedMessage, + ), + ); + } else { + _emitWearableEvent( + WearableErrorEvent( + wearable: wearable, + errorMessage: error.toString(), + description: error.toString(), + ), + ); + } + }); + } } diff --git a/open_wearable/pubspec.lock b/open_wearable/pubspec.lock index 7031a8b8..1f49553b 100644 --- a/open_wearable/pubspec.lock +++ b/open_wearable/pubspec.lock @@ -524,10 +524,10 @@ packages: dependency: transitive description: name: meta - sha256: "23f08335362185a5ea2ad3a4e597f1375e78bce8a040df5c600c8d3552ef2394" + sha256: "1741988757a65eb6b36abe716829688cf01910bbf91c34354ff7ec1c3de2b349" url: "https://pub.dev" source: hosted - version: "1.17.0" + version: "1.18.0" mime: dependency: transitive description: @@ -563,11 +563,10 @@ packages: open_earable_flutter: dependency: "direct main" description: - name: open_earable_flutter - sha256: "078c8a64ad05265b5b7afae991830549e08729fecacfd255dc4a8e038f8ad12b" - url: "https://pub.dev" - source: hosted - version: "2.3.5" + path: "E:/teco/bluetooth_error/open_earable_flutter" + relative: false + source: path + version: "2.3.6" open_file: dependency: "direct main" description: @@ -945,10 +944,10 @@ packages: dependency: transitive description: name: test_api - sha256: "8161c84903fd860b26bfdefb7963b3f0b68fee7adea0f59ef805ecca346f0c7a" + sha256: "949a932224383300f01be9221c39180316445ecb8e7547f70a41a35bf421fb9e" url: "https://pub.dev" source: hosted - version: "0.7.10" + version: "0.7.11" tuple: dependency: transitive description: diff --git a/open_wearable/pubspec.yaml b/open_wearable/pubspec.yaml index 6147e6aa..91e5806d 100644 --- a/open_wearable/pubspec.yaml +++ b/open_wearable/pubspec.yaml @@ -35,7 +35,8 @@ dependencies: # Use with the CupertinoIcons class for iOS style icons. cupertino_icons: ^1.0.8 open_file: ^3.3.2 - open_earable_flutter: ^2.3.5 + open_earable_flutter: + path: E:/teco/bluetooth_error/open_earable_flutter universal_ble: ^0.21.1 flutter_platform_widgets: ^9.0.0 provider: ^6.1.2