diff --git a/open_wearable/lib/apps/widgets/apps_page.dart b/open_wearable/lib/apps/widgets/apps_page.dart index b54f388f..9400bf94 100644 --- a/open_wearable/lib/apps/widgets/apps_page.dart +++ b/open_wearable/lib/apps/widgets/apps_page.dart @@ -32,7 +32,7 @@ List _apps = [ widget: SelectEarableView(startApp: (wearable, sensorConfigProvider) { return PostureTrackerView( EarableAttitudeTracker( - wearable as SensorManager, + wearable.requireCapability(), sensorConfigProvider, wearable.name.endsWith("L"), ), @@ -45,9 +45,9 @@ List _apps = [ description: "Track your heart rate and other vitals", widget: SelectEarableView( startApp: (wearable, _) { - if (wearable is SensorManager) { + if (wearable.hasCapability()) { //TODO: show alert if no ppg sensor is found - Sensor ppgSensor = (wearable as SensorManager).sensors.firstWhere( + Sensor ppgSensor = wearable.requireCapability().sensors.firstWhere( (s) => s.sensorName.toLowerCase() == "photoplethysmography".toLowerCase(), ); diff --git a/open_wearable/lib/view_models/sensor_recorder_provider.dart b/open_wearable/lib/view_models/sensor_recorder_provider.dart index 134aea85..11ff9909 100644 --- a/open_wearable/lib/view_models/sensor_recorder_provider.dart +++ b/open_wearable/lib/view_models/sensor_recorder_provider.dart @@ -74,8 +74,8 @@ class SensorRecorderProvider with ChangeNotifier { notifyListeners(); }); - if (wearable is SensorManager) { - for (Sensor sensor in (wearable as SensorManager).sensors) { + if (wearable.hasCapability()) { + for (Sensor sensor in wearable.requireCapability().sensors) { if (!_recorders[wearable]!.containsKey(sensor)) { _recorders[wearable]![sensor] = Recorder(columns: sensor.axisNames); } diff --git a/open_wearable/lib/view_models/wearables_provider.dart b/open_wearable/lib/view_models/wearables_provider.dart index 3a287723..f58f2c2a 100644 --- a/open_wearable/lib/view_models/wearables_provider.dart +++ b/open_wearable/lib/view_models/wearables_provider.dart @@ -53,7 +53,7 @@ class WearableTimeSynchronizedEvent extends WearableEvent { WearableTimeSynchronizedEvent({ required super.wearable, String? description, - }): super(description: description ?? 'Time synchronized for ${wearable.name}'); + }) : super(description: description ?? 'Time synchronized for ${wearable.name}'); @override String toString() => 'WearableTimeSynchronizedEvent for ${wearable.name}'; @@ -65,13 +65,16 @@ class WearableErrorEvent extends WearableEvent { required super.wearable, required this.errorMessage, String? description, - }): super(description: description ?? 'Error for ${wearable.name}: $errorMessage'); + }) : super(description: description ?? 'Error for ${wearable.name}: $errorMessage'); @override String toString() => 'WearableErrorEvent for ${wearable.name}: $errorMessage, description: $description'; } + +// MARK: WearablesProvider + class WearablesProvider with ChangeNotifier { final List _wearables = []; final Map @@ -90,43 +93,95 @@ class WearablesProvider with ChangeNotifier { StreamController.broadcast(); Stream get wearableEventStream => _wearableEventController.stream; - void addWearable(Wearable wearable) { - // 1) Fast path: ignore duplicates and push into lists/maps synchronously - if (_wearables.any((w) => w.deviceId == wearable.deviceId)) { - return; - } + final Map _capabilitySubscriptions = {}; + + // MARK: Internal helpers - if (wearable is TimeSynchronizable) { + bool _isDuplicateDevice(Wearable wearable) => + _wearables.any((w) => w.deviceId == wearable.deviceId); + + void _emitWearableEvent(WearableEvent event) { + _wearableEventController.add(event); + } + + void _emitWearableError({ + required Wearable wearable, + required String errorMessage, + String? description, + }) { + _emitWearableEvent( + WearableErrorEvent( + wearable: wearable, + errorMessage: errorMessage, + description: description, + ), + ); + } + + void _scheduleMicrotask(FutureOr Function() work) { + Future.microtask(() async { + try { + await work(); + } catch (e, st) { + logger.w('WearablesProvider microtask failed: $e\n$st'); + } + }); + } + + Future _syncTimeAndEmit({ + required Wearable wearable, + required String successDescription, + required String failureDescription, + }) async { + try { logger.d('Synchronizing time for wearable ${wearable.name}'); - (wearable as TimeSynchronizable).synchronizeTime().then((_) { - logger.d('Time synchronized for wearable ${wearable.name}'); - _wearableEventController.add(WearableTimeSynchronizedEvent(wearable: wearable, description: 'Time synchronized for ${wearable.name}')); - }).catchError((e, st) { - logger.w('Failed to synchronize time for wearable ${wearable.name}: $e\n$st'); - _wearableEventController.add( - WearableErrorEvent( - wearable: wearable, - errorMessage: 'Failed to synchronize time with ${wearable.name}: $e', - description: 'Failed to synchronize time for ${wearable.name}', - ), - ); - }); + await (wearable.requireCapability()).synchronizeTime(); + logger.d('Time synchronized for wearable ${wearable.name}'); + _emitWearableEvent( + WearableTimeSynchronizedEvent( + wearable: wearable, + description: successDescription, + ), + ); + } catch (e, st) { + logger.w('Failed to synchronize time for wearable ${wearable.name}: $e\n$st'); + _emitWearableError( + wearable: wearable, + errorMessage: 'Failed to synchronize time with ${wearable.name}: $e', + description: failureDescription, + ); } + } + + void addWearable(Wearable wearable) { + // 1) Fast path: ignore duplicates and push into lists/maps synchronously + if (_isDuplicateDevice(wearable)) return; _wearables.add(wearable); + _capabilitySubscriptions[wearable] = wearable.capabilityRegistered.listen((addedCapabilities) { + _handleCapabilitiesChanged(wearable: wearable, addedCapabilites: addedCapabilities); + }); + // Init SensorConfigurationProvider synchronously (no awaits here) - if (wearable is SensorConfigurationManager) { + if (wearable.hasCapability()) { _ensureSensorConfigProvider(wearable); final notifier = _sensorConfigurationProviders[wearable]!; for (final config - in (wearable as SensorConfigurationManager).sensorConfigurations) { + in (wearable.requireCapability()).sensorConfigurations) { if (notifier.getSelectedConfigurationValue(config) == null && config.values.isNotEmpty) { notifier.addSensorConfiguration(config, config.values.first); } } } + if (wearable.hasCapability()) { + _scheduleMicrotask(() => _syncTimeAndEmit( + wearable: wearable, + successDescription: 'Time synchronized for ${wearable.name}', + failureDescription: 'Failed to synchronize time for ${wearable.name}', + ),); + } // Disconnect listener (sync) wearable.addDisconnectListener(() { @@ -139,35 +194,27 @@ class WearablesProvider with ChangeNotifier { // 2) Slow/async work: run in microtasks so it doesn't block the add // Stereo pairing (if applicable) - if (wearable is StereoDevice) { - Future.microtask( - () => _maybeAutoPairStereoAsync(wearable as StereoDevice), - ); + if (wearable.hasCapability()) { + _scheduleMicrotask(() => _maybeAutoPairStereoAsync(wearable.requireCapability())); } // Firmware support check (if applicable) - if (wearable is DeviceFirmwareVersion) { - Future.microtask( - () => _maybeEmitUnsupportedFirmwareAsync( - wearable as DeviceFirmwareVersion, - ), - ); + if (wearable.hasCapability()) { + _scheduleMicrotask(() => _maybeEmitUnsupportedFirmwareAsync(wearable.requireCapability())); } // Check for newer firmware (if applicable) - if (wearable is DeviceFirmwareVersion) { - Future.microtask( - () => _checkForNewerFirmwareAsync(wearable as DeviceFirmwareVersion), - ); + if (wearable.hasCapability()) { + _scheduleMicrotask(() => _checkForNewerFirmwareAsync(wearable.requireCapability())); } } - // --- Helpers --------------------------------------------------------------- + // MARK: Helpers void _ensureSensorConfigProvider(Wearable wearable) { if (!_sensorConfigurationProviders.containsKey(wearable)) { _sensorConfigurationProviders[wearable] = SensorConfigurationProvider( - sensorConfigurationManager: wearable as SensorConfigurationManager, + sensorConfigurationManager: wearable.requireCapability(), ); } } @@ -202,6 +249,7 @@ class WearablesProvider with ChangeNotifier { DeviceFirmwareVersion dev, ) async { try { + final wearable = dev as Wearable; // In your abstraction, isFirmwareSupported is a Future getter. final supportStatus = await dev.checkFirmwareSupport(); switch (supportStatus) { @@ -210,21 +258,22 @@ class WearablesProvider with ChangeNotifier { break; case FirmwareSupportStatus.tooNew: _unsupportedFirmwareEventsController - .add(FirmwareTooNewEvent(dev as Wearable)); + .add(FirmwareTooNewEvent(wearable)); break; case FirmwareSupportStatus.unsupported: _unsupportedFirmwareEventsController - .add(FirmwareUnsupportedEvent(dev as Wearable)); + .add(FirmwareUnsupportedEvent(wearable)); break; case FirmwareSupportStatus.tooOld: _unsupportedFirmwareEventsController - .add(FirmwareTooOldEvent(dev as Wearable)); + .add(FirmwareTooOldEvent(wearable)); case FirmwareSupportStatus.unknown: - logger.w('Firmware support unknown for ${(dev as Wearable).name}'); + logger.w('Firmware support unknown for ${wearable.name}'); break; } } catch (e, st) { - logger.w('Firmware check failed for ${(dev as Wearable).name}: $e\n$st'); + final wearable = dev as Wearable; + logger.w('Firmware check failed for ${wearable.name}: $e\n$st'); } } @@ -274,6 +323,7 @@ class WearablesProvider with ChangeNotifier { void removeWearable(Wearable wearable) { _wearables.remove(wearable); _sensorConfigurationProviders.remove(wearable); + _capabilitySubscriptions.remove(wearable)?.cancel(); notifyListeners(); } @@ -287,4 +337,19 @@ class WearablesProvider with ChangeNotifier { } return _sensorConfigurationProviders[wearable]!; } + + void _handleCapabilitiesChanged({required Wearable wearable, required List addedCapabilites}) { + if (addedCapabilites.contains(SensorConfigurationManager)) { + _ensureSensorConfigProvider(wearable); + } + if (addedCapabilites.contains(TimeSynchronizable)) { + _scheduleMicrotask(() => _syncTimeAndEmit( + wearable: wearable, + successDescription: + 'Time synchronized for ${wearable.name} after capability change', + failureDescription: + 'Failed to synchronize time for ${wearable.name} after capability change', + ),); + } + } } diff --git a/open_wearable/lib/widgets/devices/battery_state.dart b/open_wearable/lib/widgets/devices/battery_state.dart index d4e654b6..cbaaa3e7 100644 --- a/open_wearable/lib/widgets/devices/battery_state.dart +++ b/open_wearable/lib/widgets/devices/battery_state.dart @@ -13,9 +13,9 @@ class BatteryStateView extends StatelessWidget { return Row( mainAxisSize: MainAxisSize.min, children: [ - if (_device is BatteryLevelStatus) + if (_device.hasCapability()) StreamBuilder( - stream: (_device as BatteryLevelStatus).batteryPercentageStream, + stream: _device.requireCapability().batteryPercentageStream, builder: (context, snapshot) { if (snapshot.hasData) { return PlatformText("${snapshot.data}%"); @@ -24,9 +24,9 @@ class BatteryStateView extends StatelessWidget { } }, ), - if (_device is BatteryLevelStatusService) + if (_device.hasCapability()) StreamBuilder( - stream: (_device as BatteryLevelStatusService).powerStatusStream, + stream: _device.requireCapability().powerStatusStream, builder: (context, snapshot) { if (snapshot.hasData) { if (!snapshot.data!.batteryPresent) { @@ -52,9 +52,9 @@ class BatteryStateView extends StatelessWidget { } }, ) - else if (_device is BatteryLevelStatus) + else if (_device.hasCapability()) StreamBuilder( - stream: (_device as BatteryLevelStatus).batteryPercentageStream, + stream: _device.requireCapability().batteryPercentageStream, builder: (context, snapshot) { if (snapshot.hasData) { return Icon(getBatteryIcon(snapshot.data!)); diff --git a/open_wearable/lib/widgets/devices/device_detail/device_detail_page.dart b/open_wearable/lib/widgets/devices/device_detail/device_detail_page.dart index 4480dcfa..677d36d0 100644 --- a/open_wearable/lib/widgets/devices/device_detail/device_detail_page.dart +++ b/open_wearable/lib/widgets/devices/device_detail/device_detail_page.dart @@ -36,8 +36,8 @@ class _DeviceDetailPageState extends State { } Future _initSelectedMicrophone() async { - if (widget.device is MicrophoneManager) { - final mic = await (widget.device as MicrophoneManager).getMicrophone(); + if (widget.device.hasCapability()) { + final mic = await widget.device.requireCapability().getMicrophone(); setState(() { selectedMicrophone = mic; }); @@ -67,10 +67,10 @@ class _DeviceDetailPageState extends State { mainAxisAlignment: MainAxisAlignment.center, children: [ BatteryStateView(device: widget.device), - if (widget.device is StereoDevice) + if (widget.device.hasCapability()) Padding( padding: const EdgeInsets.only(left: 8.0), - child: StereoPosLabel(device: widget.device as StereoDevice), + child: StereoPosLabel(device: widget.device.requireCapability()), ), ], ), @@ -79,7 +79,7 @@ class _DeviceDetailPageState extends State { Row( mainAxisAlignment: MainAxisAlignment.spaceEvenly, children: [ - if (widget.device is SystemDevice && (widget.device as SystemDevice).isConnectedViaSystem) + if (widget.device.hasCapability() && widget.device.requireCapability().isConnectedViaSystem) PlatformElevatedButton( child: PlatformText("Forget Device"), onPressed: () { @@ -112,12 +112,12 @@ class _DeviceDetailPageState extends State { ], ), // MARK: Audio Mode - if (widget.device is AudioModeManager) - AudioModeWidget(device: widget.device as AudioModeManager), + if (widget.device.hasCapability()) + AudioModeWidget(device: widget.device.requireCapability()), // MARK: Microphone Control - if (widget.device is MicrophoneManager) + if (widget.device.hasCapability()) MicrophoneSelectionWidget( - device: widget.device as MicrophoneManager, + device: widget.device.requireCapability(), ), // MARK: Device info PlatformText("Device Info", style: Theme.of(context).textTheme.titleSmall), @@ -129,14 +129,14 @@ class _DeviceDetailPageState extends State { subtitle: PlatformText(widget.device.deviceId), ), // MARK: Device Identifier - if (widget.device is DeviceIdentifier) + if (widget.device.hasCapability()) PlatformListTile( title: PlatformText( "Device Identifier", style: Theme.of(context).textTheme.bodyLarge, ), subtitle: FutureBuilder( - future: (widget.device as DeviceIdentifier) + future: widget.device.requireCapability() .readDeviceIdentifier(), builder: (context, snapshot) { if (snapshot.connectionState == ConnectionState.done) { @@ -155,7 +155,7 @@ class _DeviceDetailPageState extends State { ), ), // MARK: Device Firmware Version - if (widget.device is DeviceFirmwareVersion) + if (widget.device.hasCapability()) PlatformListTile( title: PlatformText( "Firmware Version", @@ -163,7 +163,7 @@ class _DeviceDetailPageState extends State { ), subtitle: Row(children: [ FutureBuilder( - future: (widget.device as DeviceFirmwareVersion) + future: widget.device.requireCapability() .readDeviceFirmwareVersion(), builder: (context, snapshot) { if (snapshot.connectionState == ConnectionState.done) { @@ -181,7 +181,7 @@ class _DeviceDetailPageState extends State { }, ), FutureBuilder( - future: (widget.device as DeviceFirmwareVersion) + future: widget.device.requireCapability() .checkFirmwareSupport(), builder: (context, snapshot) { if (snapshot.connectionState == ConnectionState.done) { @@ -222,14 +222,14 @@ class _DeviceDetailPageState extends State { ), ), // MARK: Device Hardware Version - if (widget.device is DeviceHardwareVersion) + if (widget.device.hasCapability()) PlatformListTile( title: PlatformText( "Hardware Version", style: Theme.of(context).textTheme.bodyLarge, ), subtitle: FutureBuilder( - future: (widget.device as DeviceHardwareVersion) + future: widget.device.requireCapability() .readDeviceHardwareVersion(), builder: (context, snapshot) { if (snapshot.connectionState == ConnectionState.done) { @@ -249,17 +249,17 @@ class _DeviceDetailPageState extends State { ), // MARK: Status LED control - if (widget.device is StatusLed) ...[ + if (widget.device.hasCapability() && widget.device.hasCapability()) ...[ PlatformText( "Control Status LED", style: Theme.of(context).textTheme.titleSmall, ), StatusLEDControlWidget( - statusLED: widget.device as StatusLed, - rgbLed: widget.device as RgbLed, + statusLED: widget.device.requireCapability(), + rgbLed: widget.device.requireCapability(), ), - ] else if (widget.device is RgbLed && - widget.device is! StatusLed) ...[ + ] else if (widget.device.hasCapability() && + !widget.device.hasCapability()) ...[ PlatformText( "Control RGB LED", style: Theme.of(context).textTheme.titleSmall, @@ -269,18 +269,18 @@ class _DeviceDetailPageState extends State { "LED Color", style: Theme.of(context).textTheme.bodyLarge, ), - trailing: RgbControlView(rgbLed: widget.device as RgbLed), + trailing: RgbControlView(rgbLed: widget.device.requireCapability()), ), ], // MARK: Device Battery State - if (widget.device is BatteryEnergyStatusService) ...[ + if (widget.device.hasCapability()) ...[ PlatformText( "Battery Energy Status", style: Theme.of(context).textTheme.titleSmall, ), StreamBuilder( - stream: (widget.device as BatteryEnergyStatusService) + stream: widget.device.requireCapability() .energyStatusStream, builder: (context, snapshot) { if (snapshot.connectionState == ConnectionState.waiting) { @@ -326,13 +326,13 @@ class _DeviceDetailPageState extends State { ], // MARK: Battery Health - if (widget.device is BatteryHealthStatusService) ...[ + if (widget.device.hasCapability()) ...[ PlatformText( "Battery Health Status", style: Theme.of(context).textTheme.titleSmall, ), StreamBuilder( - stream: (widget.device as BatteryHealthStatusService) + stream: widget.device.requireCapability() .healthStatusStream, builder: (context, snapshot) { if (snapshot.connectionState == ConnectionState.waiting) { diff --git a/open_wearable/lib/widgets/devices/devices_page.dart b/open_wearable/lib/widgets/devices/devices_page.dart index e912890b..b3b33cd1 100644 --- a/open_wearable/lib/widgets/devices/devices_page.dart +++ b/open_wearable/lib/widgets/devices/devices_page.dart @@ -220,20 +220,20 @@ class DeviceRow extends StatelessWidget { ), Row(children: [ BatteryStateView(device: _device), - if (_device is StereoDevice) + if (_device.hasCapability()) Padding( padding: EdgeInsets.only(left: 8.0), - child: StereoPosLabel(device: _device as StereoDevice), + child: StereoPosLabel(device: _device.requireCapability()), ), ], ), ], ), Spacer(), - if (_device is DeviceIdentifier) + if (_device.hasCapability()) FutureBuilder( future: - (_device as DeviceIdentifier).readDeviceIdentifier(), + _device.requireCapability().readDeviceIdentifier(), builder: (context, snapshot) { if (snapshot.connectionState == ConnectionState.waiting) { @@ -249,12 +249,12 @@ class DeviceRow extends StatelessWidget { PlatformText(_device.deviceId), ], ), - if (_device is DeviceFirmwareVersion) + if (_device.hasCapability()) Row( children: [ PlatformText("Firmware Version: "), FutureBuilder( - future: (_device as DeviceFirmwareVersion) + future: _device.requireCapability() .readDeviceFirmwareVersion(), builder: (context, snapshot) { if (snapshot.connectionState == @@ -268,7 +268,7 @@ class DeviceRow extends StatelessWidget { }, ), FutureBuilder( - future: (_device as DeviceFirmwareVersion) + future: _device.requireCapability() .checkFirmwareSupport(), builder: (context, snapshot) { if (snapshot.connectionState == @@ -300,12 +300,12 @@ class DeviceRow extends StatelessWidget { ), ], ), - if (_device is DeviceHardwareVersion) + if (_device.hasCapability()) Row( children: [ PlatformText("Hardware Version: "), FutureBuilder( - future: (_device as DeviceHardwareVersion) + future: _device.requireCapability() .readDeviceHardwareVersion(), builder: (context, snapshot) { if (snapshot.connectionState == diff --git a/open_wearable/lib/widgets/fota/firmware_select/firmware_list.dart b/open_wearable/lib/widgets/fota/firmware_select/firmware_list.dart index 2fd7b009..564cc03c 100644 --- a/open_wearable/lib/widgets/fota/firmware_select/firmware_list.dart +++ b/open_wearable/lib/widgets/fota/firmware_select/firmware_list.dart @@ -36,9 +36,9 @@ class _FirmwareListState extends State { final wearable = Provider.of(context, listen: false) .selectedWearable; - if (wearable is DeviceFirmwareVersion) { + if (wearable != null && wearable.hasCapability()) { final version = - await (wearable as DeviceFirmwareVersion).readDeviceFirmwareVersion(); + await wearable.requireCapability().readDeviceFirmwareVersion(); setState(() { firmwareVersion = version; }); diff --git a/open_wearable/lib/widgets/fota/fota_warning_page.dart b/open_wearable/lib/widgets/fota/fota_warning_page.dart index 94e52b42..6eb028d9 100644 --- a/open_wearable/lib/widgets/fota/fota_warning_page.dart +++ b/open_wearable/lib/widgets/fota/fota_warning_page.dart @@ -33,9 +33,9 @@ class _FotaWarningPageState extends State { ); final device = updateProvider.selectedWearable; - if (device != null && device is BatteryLevelStatus) { + if (device != null && device.hasCapability()) { // Get the current battery level from the stream - final batteryLevel = await (device as BatteryLevelStatus) + final batteryLevel = await device.requireCapability() .batteryPercentageStream .first .timeout( diff --git a/open_wearable/lib/widgets/sensors/configuration/sensor_configuration_device_row.dart b/open_wearable/lib/widgets/sensors/configuration/sensor_configuration_device_row.dart index eaae8a1d..78224085 100644 --- a/open_wearable/lib/widgets/sensors/configuration/sensor_configuration_device_row.dart +++ b/open_wearable/lib/widgets/sensors/configuration/sensor_configuration_device_row.dart @@ -65,8 +65,8 @@ class _SensorConfigurationDeviceRowState .bodyLarge ?.copyWith(fontWeight: FontWeight.bold), ), - if (device is StereoDevice) - StereoPosLabel(device: device as StereoDevice), + if (device.hasCapability()) + StereoPosLabel(device: device.requireCapability()), ], ), trailing: _buildTabBar(context), @@ -80,7 +80,7 @@ class _SensorConfigurationDeviceRowState Future _updateContent() async { final Wearable device = widget.device; - if (device is! SensorConfigurationManager) { + if (!device.hasCapability()) { if (!mounted) return; setState(() { _content = [ @@ -94,17 +94,19 @@ class _SensorConfigurationDeviceRowState } final SensorConfigurationManager sensorManager = - device as SensorConfigurationManager; + device.requireCapability(); if (_tabController.index == 0) { - _buildNewTabContent(sensorManager); + _buildNewTabContent(device); } else { await _buildLoadTabContent(sensorManager); } } - void _buildNewTabContent(SensorConfigurationManager device) { - final List content = device.sensorConfigurations + void _buildNewTabContent(Wearable device) { + SensorConfigurationManager sensorManager = + device.requireCapability(); + final List content = sensorManager.sensorConfigurations .map( (config) => SensorConfigurationValueRow(sensorConfiguration: config), ) @@ -116,10 +118,10 @@ class _SensorConfigurationDeviceRowState const SaveConfigRow(), ]); - if (device is EdgeRecorderManager) { + if (device.hasCapability()) { content.addAll([ const Divider(), - EdgeRecorderPrefixRow(manager: device as EdgeRecorderManager), + EdgeRecorderPrefixRow(manager: device.requireCapability()), ]); } @@ -197,7 +199,7 @@ class _SensorConfigurationDeviceRowState } Widget? _buildTabBar(BuildContext context) { - if (widget.device is! SensorConfigurationManager) return null; + if (!widget.device.hasCapability()) return null; return SizedBox( width: MediaQuery.of(context).size.width * 0.4, diff --git a/open_wearable/lib/widgets/sensors/configuration/sensor_configuration_view.dart b/open_wearable/lib/widgets/sensors/configuration/sensor_configuration_view.dart index 2d2d6a11..2dedc189 100644 --- a/open_wearable/lib/widgets/sensors/configuration/sensor_configuration_view.dart +++ b/open_wearable/lib/widgets/sensors/configuration/sensor_configuration_view.dart @@ -42,7 +42,7 @@ class SensorConfigurationView extends StatelessWidget { : ListView( children: [ ...wearablesProvider.wearables.map((wearable) { - if (wearable is SensorConfigurationManager) { + if (wearable.hasCapability()) { return ChangeNotifierProvider.value( value: wearablesProvider.getSensorConfigurationProvider(wearable), child: SensorConfigurationDeviceRow(device: wearable), @@ -55,7 +55,7 @@ class SensorConfigurationView extends StatelessWidget { _buildSetConfigButton( configProviders: wearablesProvider.wearables // ignore: prefer_iterable_wheretype - .where((wearable) => wearable is SensorConfigurationManager) + .where((wearable) => wearable.hasCapability()) .map( (wearable) => wearablesProvider.getSensorConfigurationProvider(wearable), ).toList(), @@ -177,11 +177,11 @@ class SensorConfigurationView extends StatelessWidget { /// Determines how many columns a device should span int _getGridSpanForDevice(Wearable device) { - if (device is! SensorConfigurationManager) { + if (!device.hasCapability()) { return 1; // Default size } - int sensorConfigCount = (device as SensorConfigurationManager).sensorConfigurations.length; + int sensorConfigCount = device.requireCapability().sensorConfigurations.length; return sensorConfigCount.clamp(1, 4); } diff --git a/open_wearable/lib/widgets/sensors/values/sensor_values_page.dart b/open_wearable/lib/widgets/sensors/values/sensor_values_page.dart index 2858a18a..97b6101f 100644 --- a/open_wearable/lib/widgets/sensors/values/sensor_values_page.dart +++ b/open_wearable/lib/widgets/sensors/values/sensor_values_page.dart @@ -17,8 +17,8 @@ class SensorValuesPage extends StatelessWidget { builder: (context, wearablesProvider, child) { List charts = []; for (var wearable in wearablesProvider.wearables) { - if (wearable is SensorManager) { - for (Sensor sensor in (wearable as SensorManager).sensors) { + if (wearable.hasCapability()) { + for (Sensor sensor in wearable.requireCapability().sensors) { if (!_sensorDataProvider.containsKey((wearable, sensor))) { _sensorDataProvider[(wearable, sensor)] = SensorDataProvider(sensor: sensor); } @@ -33,9 +33,9 @@ class SensorValuesPage extends StatelessWidget { } _sensorDataProvider.removeWhere((key, _) => - !wearablesProvider.wearables.any((device) => device is SensorManager + !wearablesProvider.wearables.any((device) => device.hasCapability() && device == key.$1 - && (device as SensorManager).sensors.contains(key.$2),), + && device.requireCapability().sensors.contains(key.$2),), ); return LayoutBuilder( diff --git a/open_wearable/pubspec.lock b/open_wearable/pubspec.lock index 0458b180..1a527f13 100644 --- a/open_wearable/pubspec.lock +++ b/open_wearable/pubspec.lock @@ -21,10 +21,10 @@ packages: dependency: transitive description: name: bloc - sha256: a2cebb899f91d36eeeaa55c7b20b5915db5a9df1b8fd4a3c9c825e22e474537d + sha256: a48653a82055a900b88cd35f92429f068c5a8057ae9b136d197b3d56c57efb81 url: "https://pub.dev" source: hosted - version: "9.1.0" + version: "9.2.0" bluez: dependency: transitive description: @@ -125,10 +125,10 @@ packages: dependency: transitive description: name: equatable - sha256: "567c64b3cb4cf82397aac55f4f0cbd3ca20d77c6c03bedbc4ceaddc08904aef7" + sha256: "3e0141505477fd8ad55d6eb4e7776d3fe8430be8e497ccb1521370c3f21a3e2b" url: "https://pub.dev" source: hosted - version: "2.0.7" + version: "2.0.8" fake_async: dependency: transitive description: @@ -141,10 +141,10 @@ packages: dependency: transitive description: name: ffi - sha256: "289279317b4b16eb2bb7e271abccd4bf84ec9bdcbe999e278a94b804f5630418" + sha256: d07d37192dbf97461359c1518788f203b0c9102cfd2c35a716b823741219542c url: "https://pub.dev" source: hosted - version: "2.1.4" + version: "2.1.5" file: dependency: transitive description: @@ -157,10 +157,10 @@ packages: dependency: "direct main" description: name: file_picker - sha256: "7872545770c277236fd32b022767576c562ba28366204ff1a5628853cf8f2200" + sha256: d974b6ba2606371ac71dd94254beefb6fa81185bde0b59bdc1df09885da85fde url: "https://pub.dev" source: hosted - version: "10.3.7" + version: "10.3.8" file_selector: dependency: "direct main" description: @@ -181,10 +181,10 @@ packages: dependency: transitive description: name: file_selector_ios - sha256: "628ec99afd8bb40620b4c8707d5fd5fc9e89d83e9b0b327d471fe5f7bc5fc33f" + sha256: e2ecf2885c121691ce13b60db3508f53c01f869fb6e8dc5c1cfa771e4c46aeca url: "https://pub.dev" source: hosted - version: "0.5.3+4" + version: "0.5.3+5" file_selector_linux: dependency: transitive description: @@ -468,18 +468,18 @@ packages: dependency: "direct main" description: name: open_earable_flutter - sha256: fb05d84b71e5dead780f0ff7ad0483183a75e7ca5d2b0208ac402129990a74d9 + sha256: beec110a534837dedec5ab92118824def851d9a712d1f749b486affef0af9d7d url: "https://pub.dev" source: hosted - version: "2.2.6" + version: "2.3.0" open_file: dependency: "direct main" description: name: open_file - sha256: d17e2bddf5b278cb2ae18393d0496aa4f162142ba97d1a9e0c30d476adf99c0e + sha256: b22decdae85b459eac24aeece48f33845c6f16d278a9c63d75c5355345ca236b url: "https://pub.dev" source: hosted - version: "3.5.10" + version: "3.5.11" open_file_android: dependency: transitive description: @@ -492,10 +492,10 @@ packages: dependency: transitive description: name: open_file_ios - sha256: "02996f01e5f6863832068e97f8f3a5ef9b613516db6897f373b43b79849e4d07" + sha256: a5acd07ba1f304f807a97acbcc489457e1ad0aadff43c467987dd9eef814098f url: "https://pub.dev" source: hosted - version: "1.0.3" + version: "1.0.4" open_file_linux: dependency: transitive description: @@ -508,10 +508,10 @@ packages: dependency: transitive description: name: open_file_mac - sha256: "1440b1e37ceb0642208cfeb2c659c6cda27b25187a90635c9d1acb7d0584d324" + sha256: cd293f6750de6438ab2390513c99128ade8c974825d4d8128886d1cda8c64d01 url: "https://pub.dev" source: hosted - version: "1.0.3" + version: "1.0.4" open_file_platform_interface: dependency: transitive description: @@ -724,18 +724,18 @@ packages: dependency: "direct main" description: name: shared_preferences - sha256: "6e8bf70b7fef813df4e9a36f658ac46d107db4b4cfe1048b477d4e453a8159f5" + sha256: "2939ae520c9024cb197fc20dee269cd8cdbf564c8b5746374ec6cacdc5169e64" url: "https://pub.dev" source: hosted - version: "2.5.3" + version: "2.5.4" shared_preferences_android: dependency: transitive description: name: shared_preferences_android - sha256: "46a46fd64659eff15f4638bbe19de43f9483f0e0bf024a9fb6b3582064bacc7b" + sha256: "83af5c682796c0f7719c2bbf74792d113e40ae97981b8f266fa84574573556bc" url: "https://pub.dev" source: hosted - version: "2.4.17" + version: "2.4.18" shared_preferences_foundation: dependency: transitive description: @@ -998,5 +998,5 @@ packages: source: hosted version: "6.6.1" sdks: - dart: ">=3.9.0 <4.0.0" - flutter: ">=3.35.0" + dart: ">=3.10.0 <4.0.0" + flutter: ">=3.38.0" diff --git a/open_wearable/pubspec.yaml b/open_wearable/pubspec.yaml index 6b2c9f9d..588ae299 100644 --- a/open_wearable/pubspec.yaml +++ b/open_wearable/pubspec.yaml @@ -35,7 +35,7 @@ dependencies: # Use with the CupertinoIcons class for iOS style icons. cupertino_icons: ^1.0.8 open_file: ^3.3.2 - open_earable_flutter: ^2.2.6 + open_earable_flutter: ^2.3.0 flutter_platform_widgets: ^9.0.0 provider: ^6.1.2 logger: ^2.5.0