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 f01f8898..f58f2c2a 100644 --- a/open_wearable/lib/view_models/wearables_provider.dart +++ b/open_wearable/lib/view_models/wearables_provider.dart @@ -194,18 +194,18 @@ 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) { - _scheduleMicrotask(() => _maybeAutoPairStereoAsync(wearable as StereoDevice)); + if (wearable.hasCapability()) { + _scheduleMicrotask(() => _maybeAutoPairStereoAsync(wearable.requireCapability())); } // Firmware support check (if applicable) - if (wearable is DeviceFirmwareVersion) { - _scheduleMicrotask(() => _maybeEmitUnsupportedFirmwareAsync(wearable as DeviceFirmwareVersion)); + if (wearable.hasCapability()) { + _scheduleMicrotask(() => _maybeEmitUnsupportedFirmwareAsync(wearable.requireCapability())); } // Check for newer firmware (if applicable) - if (wearable is DeviceFirmwareVersion) { - _scheduleMicrotask(() => _checkForNewerFirmwareAsync(wearable as DeviceFirmwareVersion)); + if (wearable.hasCapability()) { + _scheduleMicrotask(() => _checkForNewerFirmwareAsync(wearable.requireCapability())); } } @@ -214,7 +214,7 @@ class WearablesProvider with ChangeNotifier { void _ensureSensorConfigProvider(Wearable wearable) { if (!_sensorConfigurationProviders.containsKey(wearable)) { _sensorConfigurationProviders[wearable] = SensorConfigurationProvider( - sensorConfigurationManager: wearable as SensorConfigurationManager, + sensorConfigurationManager: wearable.requireCapability(), ); } } 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(