diff --git a/open_wearable/ios/Podfile.lock b/open_wearable/ios/Podfile.lock index 764cb645..6db677a6 100644 --- a/open_wearable/ios/Podfile.lock +++ b/open_wearable/ios/Podfile.lock @@ -1,4 +1,6 @@ PODS: + - device_info_plus (0.0.1): + - Flutter - DKImagePickerController/Core (4.3.9): - DKImagePickerController/ImageDataManager - DKImagePickerController/Resource @@ -48,11 +50,16 @@ PODS: - SwiftProtobuf - open_file_ios (1.0.3): - Flutter + - path_provider_foundation (0.0.1): + - Flutter + - FlutterMacOS - permission_handler_apple (9.3.0): - Flutter - SDWebImage (5.21.5): - SDWebImage/Core (= 5.21.5) - SDWebImage/Core (5.21.5) + - sensors_plus (0.0.1): + - Flutter - share_plus (0.0.1): - Flutter - shared_preferences_foundation (0.0.1): @@ -69,13 +76,16 @@ PODS: - ZIPFoundation (0.9.19) DEPENDENCIES: + - device_info_plus (from `.symlinks/plugins/device_info_plus/ios`) - file_picker (from `.symlinks/plugins/file_picker/ios`) - file_selector_ios (from `.symlinks/plugins/file_selector_ios/ios`) - Flutter (from `Flutter`) - flutter_archive (from `.symlinks/plugins/flutter_archive/ios`) - mcumgr_flutter (from `.symlinks/plugins/mcumgr_flutter/ios`) - open_file_ios (from `.symlinks/plugins/open_file_ios/ios`) + - path_provider_foundation (from `.symlinks/plugins/path_provider_foundation/darwin`) - permission_handler_apple (from `.symlinks/plugins/permission_handler_apple/ios`) + - sensors_plus (from `.symlinks/plugins/sensors_plus/ios`) - share_plus (from `.symlinks/plugins/share_plus/ios`) - shared_preferences_foundation (from `.symlinks/plugins/shared_preferences_foundation/darwin`) - universal_ble (from `.symlinks/plugins/universal_ble/darwin`) @@ -93,6 +103,8 @@ SPEC REPOS: - ZIPFoundation EXTERNAL SOURCES: + device_info_plus: + :path: ".symlinks/plugins/device_info_plus/ios" file_picker: :path: ".symlinks/plugins/file_picker/ios" file_selector_ios: @@ -105,8 +117,12 @@ EXTERNAL SOURCES: :path: ".symlinks/plugins/mcumgr_flutter/ios" open_file_ios: :path: ".symlinks/plugins/open_file_ios/ios" + path_provider_foundation: + :path: ".symlinks/plugins/path_provider_foundation/darwin" permission_handler_apple: :path: ".symlinks/plugins/permission_handler_apple/ios" + sensors_plus: + :path: ".symlinks/plugins/sensors_plus/ios" share_plus: :path: ".symlinks/plugins/share_plus/ios" shared_preferences_foundation: @@ -117,6 +133,7 @@ EXTERNAL SOURCES: :path: ".symlinks/plugins/url_launcher_ios/ios" SPEC CHECKSUMS: + device_info_plus: 21fcca2080fbcd348be798aa36c3e5ed849eefbe DKImagePickerController: 946cec48c7873164274ecc4624d19e3da4c1ef3c DKPhotoGallery: b3834fecb755ee09a593d7c9e389d8b5d6deed60 file_picker: a0560bc09d61de87f12d246fc47d2119e6ef37be @@ -126,8 +143,10 @@ SPEC CHECKSUMS: iOSMcuManagerLibrary: e9555825af11a61744fe369c12e1e66621061b58 mcumgr_flutter: 969e99cc15e9fe658242669ce1075bf4612aef8a open_file_ios: 46184d802ee7959203f6392abcfa0dd49fdb5be0 + path_provider_foundation: bb55f6dbba17d0dccd6737fe6f7f34fbd0376880 permission_handler_apple: 4ed2196e43d0651e8ff7ca3483a069d469701f2d SDWebImage: e9c98383c7572d713c1a0d7dd2783b10599b9838 + sensors_plus: 6a11ed0c2e1d0bd0b20b4029d3bad27d96e0c65b share_plus: 50da8cb520a8f0f65671c6c6a99b3617ed10a58a shared_preferences_foundation: 7036424c3d8ec98dfe75ff1667cb0cd531ec82bb SwiftCBOR: 465775bed0e8bac7bfb8160bcf7b95d7f75971e4 diff --git a/open_wearable/ios/Runner/Info.plist b/open_wearable/ios/Runner/Info.plist index acaa1598..56e5c1a9 100644 --- a/open_wearable/ios/Runner/Info.plist +++ b/open_wearable/ios/Runner/Info.plist @@ -51,6 +51,8 @@ This app requires Bluetooth access to communicate with wearable devices. NSPhotoLibraryUsageDescription Needed for optional file selection functionality. + NSMotionUsageDescription + This app requires access to device motion in order to provide sensor data. ITSAppUsesNonExemptEncryption UIFileSharingEnabled diff --git a/open_wearable/lib/models/this_device_wearable.dart b/open_wearable/lib/models/this_device_wearable.dart new file mode 100644 index 00000000..8e0b6d93 --- /dev/null +++ b/open_wearable/lib/models/this_device_wearable.dart @@ -0,0 +1,557 @@ +import 'dart:async'; + +import 'package:device_info_plus/device_info_plus.dart'; +import 'package:flutter/foundation.dart'; +import 'package:open_earable_flutter/open_earable_flutter.dart' hide Version, logger; +import 'package:pub_semver/pub_semver.dart'; +import 'package:sensors_plus/sensors_plus.dart'; + +import 'logger.dart'; + +class ThisDeviceWearable extends Wearable + implements SensorManager, SensorConfigurationManager, DeviceFirmwareVersion { + @override + final List sensors = []; + + @override + final List sensorConfigurations = []; + + final StreamController> + _sensorConfigurationController = StreamController.broadcast(); + + @override + Stream> + get sensorConfigurationStream => _sensorConfigurationController.stream; + + final DeviceProfile deviceProfile; + + final WearableDisconnectNotifier _disconnectNotifier; + + ThisDeviceWearable._({ + required super.disconnectNotifier, + required this.deviceProfile, + }) : _disconnectNotifier = disconnectNotifier, + super(name: deviceProfile.displayName); + + static Future create({ + required WearableDisconnectNotifier disconnectNotifier, + }) async { + final profile = await DeviceProfile.fetch(); + logger.d('Fetched device profile: $profile'); + final wearable = ThisDeviceWearable._( + disconnectNotifier: disconnectNotifier, + deviceProfile: profile, + ); + await wearable._initSensors(); + return wearable; + } + + @override + String get deviceId => deviceProfile.deviceId; + + @override + Future disconnect() async { + _disconnectNotifier.notifyListeners(); + } + + @override + String? getWearableIconPath({bool darkmode = false}) { + return null; + } + + void _emitSensorConfigurationChange( + SensorConfiguration configuration, + SensorConfigurationValue value, + ) { + _sensorConfigurationController.add({configuration: value}); + } + + Future _initSensors() async { + if (await _isSensorAvailable(gyroscopeEventStream())) { + final gyroConfig = DeviceSensorConfiguration( + name: 'Gyroscope', + onChange: _emitSensorConfigurationChange, + ); + sensorConfigurations.add(gyroConfig); + _emitSensorConfigurationChange(gyroConfig, gyroConfig.currentValue); + sensors.add( + ThisDeviceSensor( + config: gyroConfig, + sensorName: 'Gyroscope', + chartTitle: 'Gyroscope', + shortChartTitle: 'Gyro', + axisNames: ['X', 'Y', 'Z'], + axisUnits: ['rad/s', 'rad/s', 'rad/s'], + valueExtractor: (event) => SensorDoubleValue( + values: [event.x, event.y, event.z], + timestamp: event.timestamp.millisecondsSinceEpoch, + ), + sensorStreamProvider: gyroscopeEventStream, + ), + ); + } + if (await _isSensorAvailable( + accelerometerEventStream(), + )) { + final accelConfig = DeviceSensorConfiguration( + name: 'Accelerometer', + onChange: _emitSensorConfigurationChange, + ); + sensorConfigurations.add(accelConfig); + _emitSensorConfigurationChange(accelConfig, accelConfig.currentValue); + sensors.add( + ThisDeviceSensor( + config: accelConfig, + sensorName: 'Accelerometer', + chartTitle: 'Accelerometer', + shortChartTitle: 'Accel', + axisNames: ['X', 'Y', 'Z'], + axisUnits: ['m/s²', 'm/s²', 'm/s²'], + valueExtractor: (event) => SensorDoubleValue( + values: [event.x, event.y, event.z], + timestamp: event.timestamp.millisecondsSinceEpoch, + ), + sensorStreamProvider: accelerometerEventStream, + ), + ); + } + if (await _isSensorAvailable(magnetometerEventStream())) { + final magConfig = DeviceSensorConfiguration( + name: 'Magnetometer', + onChange: _emitSensorConfigurationChange, + ); + sensorConfigurations.add(magConfig); + _emitSensorConfigurationChange(magConfig, magConfig.currentValue); + sensors.add( + ThisDeviceSensor( + config: magConfig, + sensorName: 'Magnetometer', + chartTitle: 'Magnetometer', + shortChartTitle: 'Mag', + axisNames: ['X', 'Y', 'Z'], + axisUnits: ['µT', 'µT', 'µT'], + valueExtractor: (event) => SensorDoubleValue( + values: [event.x, event.y, event.z], + timestamp: event.timestamp.millisecondsSinceEpoch, + ), + sensorStreamProvider: magnetometerEventStream, + ), + ); + } + if (await _isSensorAvailable(barometerEventStream())) { + final baroConfig = DeviceSensorConfiguration( + name: 'Barometer', + onChange: _emitSensorConfigurationChange, + ); + sensorConfigurations.add(baroConfig); + _emitSensorConfigurationChange(baroConfig, baroConfig.currentValue); + sensors.add( + ThisDeviceSensor( + config: baroConfig, + sensorName: 'Barometer', + chartTitle: 'Barometer', + shortChartTitle: 'Baro', + axisNames: ['Pressure'], + axisUnits: ['hPa'], + valueExtractor: (event) => SensorDoubleValue( + values: [event.pressure], + timestamp: event.timestamp.millisecondsSinceEpoch, + ), + sensorStreamProvider: barometerEventStream, + ), + ); + } + } + + static Future _isSensorAvailable(Stream stream) async { + try { + await stream.first.timeout(const Duration(milliseconds: 800)); + return true; + } catch (_) { + return false; + } + } + + @override + Future checkFirmwareSupport() { + return Future.value(FirmwareSupportStatus.supported); + } + + @override + Future readDeviceFirmwareVersion() { + return deviceProfile.osVersion != null + ? Future.value(deviceProfile.osVersion) + : Future.error('OS version not available'); + } + + @override + Future readFirmwareVersionNumber() { + if (deviceProfile.osVersion == null) { + return Future.error('OS version not available'); + } + try { + final version = Version.parse(deviceProfile.osVersion!); + return Future.value(version); + } catch (e) { + return Future.error('Failed to parse OS version: $e'); + } + } + + @override + VersionConstraint get supportedFirmwareRange => VersionConstraint.any; +} + +class DeviceProfile { + final String displayName; + final String deviceId; + final String? model; + final String? manufacturer; + final String? osVersion; + final String? platform; + + const DeviceProfile({ + required this.displayName, + required this.deviceId, + this.model, + this.manufacturer, + this.osVersion, + this.platform, + }); + + static Future fetch() async { + final deviceInfo = DeviceInfoPlugin(); + try { + if (kIsWeb) { + final info = await deviceInfo.webBrowserInfo; + logger.d("Fetched web browser info: $info"); + final browserName = info.browserName.name; + final displayName = _firstNonEmpty( + [info.platform, browserName, info.appName], + 'Web Browser', + ); + final deviceId = _firstNonEmpty( + [info.userAgent, info.appVersion], + 'WEB-DEVICE', + ); + return DeviceProfile( + displayName: displayName, + deviceId: deviceId, + model: browserName, + manufacturer: info.vendor, + osVersion: info.appVersion, + platform: 'web', + ); + } + + switch (defaultTargetPlatform) { + case TargetPlatform.android: + final info = await deviceInfo.androidInfo; + logger.d("Fetched Android device info: $info"); + final displayName = _firstNonEmpty( + [ + _joinNonEmpty([info.brand, info.model]), + info.model, + info.device, + info.product, + ], + 'Android Device', + ); + final deviceId = _firstNonEmpty( + [info.id, info.device, info.product, info.model], + 'ANDROID-DEVICE', + ); + final osVersion = _joinNonEmpty( + [ + 'Android', + info.version.release, + 'SDK ${info.version.sdkInt}', + ], + ); + return DeviceProfile( + displayName: displayName, + deviceId: deviceId, + model: info.model, + manufacturer: info.manufacturer, + osVersion: osVersion, + platform: 'android', + ); + case TargetPlatform.iOS: + final info = await deviceInfo.iosInfo; + logger.d("Fetched iOS device info: $info"); + final displayName = _firstNonEmpty( + [info.name, info.localizedModel, info.model], + 'iOS Device', + ); + final deviceId = _firstNonEmpty( + [info.identifierForVendor, info.name, info.model], + 'IOS-DEVICE', + ); + final osVersion = _joinNonEmpty( + [info.systemName, info.systemVersion], + ); + return DeviceProfile( + displayName: displayName, + deviceId: deviceId, + model: info.model, + manufacturer: 'Apple', + osVersion: osVersion, + platform: 'ios', + ); + case TargetPlatform.macOS: + final info = await deviceInfo.macOsInfo; + logger.d("Fetched macOS device info: $info"); + final displayName = _firstNonEmpty( + [info.computerName, info.model], + 'macOS Device', + ); + final deviceId = _firstNonEmpty( + [info.computerName, info.model], + 'MAC-DEVICE', + ); + final osVersion = _joinNonEmpty( + ['macOS', info.osRelease], + ); + return DeviceProfile( + displayName: displayName, + deviceId: deviceId, + model: info.model, + manufacturer: 'Apple', + osVersion: osVersion, + platform: 'macos', + ); + case TargetPlatform.windows: + final info = await deviceInfo.windowsInfo; + logger.d("Fetched Windows device info: $info"); + final displayName = _firstNonEmpty( + [info.computerName, info.productName], + 'Windows Device', + ); + final deviceId = _firstNonEmpty( + [info.deviceId, info.computerName, info.productName], + 'WINDOWS-DEVICE', + ); + final osVersion = _joinNonEmpty( + ['Windows', info.displayVersion, info.buildNumber.toString()], + ); + return DeviceProfile( + displayName: displayName, + deviceId: deviceId, + model: info.productName, + manufacturer: 'Microsoft', + osVersion: osVersion, + platform: 'windows', + ); + case TargetPlatform.linux: + final info = await deviceInfo.linuxInfo; + logger.d("Fetched Linux device info: $info"); + final displayName = _firstNonEmpty( + [info.prettyName, info.name], + 'Linux Device', + ); + final deviceId = _firstNonEmpty( + [info.machineId, info.prettyName, info.name], + 'LINUX-DEVICE', + ); + final osVersion = _joinNonEmpty( + [info.name, info.version], + ); + return DeviceProfile( + displayName: displayName, + deviceId: deviceId, + model: info.prettyName, + manufacturer: null, + osVersion: osVersion, + platform: 'linux', + ); + case TargetPlatform.fuchsia: + break; + } + } catch (_) { + // Fall back to default profile below. + } + + return const DeviceProfile( + displayName: 'This Device', + deviceId: 'THIS-DEVICE-001', + platform: 'unknown', + ); + } + + @override + String toString() { + return 'DeviceProfile(displayName: $displayName, deviceId: $deviceId, model: $model, manufacturer: $manufacturer, osVersion: $osVersion, platform: $platform)'; + } +} + +String _firstNonEmpty(List candidates, String fallback) { + for (final candidate in candidates) { + if (candidate == null) continue; + final trimmed = candidate.trim(); + if (trimmed.isNotEmpty) return trimmed; + } + return fallback; +} + +String? _joinNonEmpty(List parts) { + final cleaned = []; + for (final part in parts) { + if (part == null) continue; + final trimmed = part.trim(); + if (trimmed.isNotEmpty) cleaned.add(trimmed); + } + if (cleaned.isEmpty) return null; + return cleaned.join(' '); +} + +class ThisDeviceSensor extends Sensor { + final DeviceSensorConfiguration config; + late final StreamController _controller; + StreamSubscription? _subscription; + final Stream Function({required Duration samplingPeriod}) _sensorStreamProvider; + final SensorDoubleValue Function(SensorEvent event) _valueExtractor; + + ThisDeviceSensor({ + required super.sensorName, + required super.chartTitle, + required super.shortChartTitle, + required this.config, + required List axisNames, + required List axisUnits, + required SensorDoubleValue Function(SensorEvent event) valueExtractor, + required Stream Function({required Duration samplingPeriod}) sensorStreamProvider, + }) : _axisNames = axisNames, + _axisUnits = axisUnits, + _valueExtractor = valueExtractor, + _sensorStreamProvider = sensorStreamProvider { + _controller = StreamController.broadcast( + onListen: _updateSubscription, + onCancel: _updateSubscription, + ); + config.changes.listen((value) { + _updateSubscription(); + }); + } + + final List _axisNames; + @override + List get axisNames => _axisNames; + + final List _axisUnits; + @override + List get axisUnits => _axisUnits; + + @override + Stream get sensorStream => _controller.stream; + + void _updateSubscription() { + if (!_controller.hasListener) { + _cancelSubscription(); + return; + } + + final value = config.currentValue; + if (value.isOff) { + _cancelSubscription(); + return; + } + + final samplingPeriod = value.frequencyHz > 0 ? Duration(milliseconds: (1000 / value.frequencyHz).round()) : SensorInterval.normalInterval; + + _cancelSubscription(); + _subscription = + _sensorStreamProvider(samplingPeriod: samplingPeriod).listen( + (event) { + _controller.add( + _valueExtractor(event), + ); + }, + onError: _controller.addError, + ); + } + + void _cancelSubscription() { + _subscription?.cancel(); + _subscription = null; + } +} + +class DeviceSensorConfiguration + extends SensorFrequencyConfiguration { + final void Function( + SensorConfiguration configuration, + SensorConfigurationValue value, + ) onChange; + + DeviceSensorFrequencyValue _currentValue; + + DeviceSensorConfiguration({ + required super.name, + required this.onChange, + }) : _currentValue = DeviceSensorFrequencyValue.normal(), + super( + values: DeviceSensorFrequencyValue.defaults(), + offValue: DeviceSensorFrequencyValue.off(), + ); + + DeviceSensorFrequencyValue get currentValue => _currentValue; + + Stream get changes => + _changesController.stream; + + final StreamController _changesController = + StreamController.broadcast(); + + @override + void setConfiguration(DeviceSensorFrequencyValue configuration) { + _currentValue = configuration; + onChange(this, configuration); + _changesController.add(configuration); + } +} + +class DeviceSensorFrequencyValue extends SensorFrequencyConfigurationValue { + DeviceSensorFrequencyValue({ + required super.frequencyHz, + String? key, + }) : super( + key: key ?? _formatKey(frequencyHz), + ); + + bool get isOff => frequencyHz <= 0; + + static DeviceSensorFrequencyValue off() { + return DeviceSensorFrequencyValue( + frequencyHz: 0, + key: 'Off', + ); + } + + static DeviceSensorFrequencyValue normal() { + return DeviceSensorFrequencyValue( + frequencyHz: 5, + ); + } + + static List defaults() { + return [ + off(), + normal(), + DeviceSensorFrequencyValue( + frequencyHz: 15, + ), + DeviceSensorFrequencyValue( + frequencyHz: 30, + ), + DeviceSensorFrequencyValue( + frequencyHz: 50, + ), + ]; + } + + static String _formatKey(double frequencyHz) { + if (frequencyHz == frequencyHz.roundToDouble()) { + return '${frequencyHz.toInt()} Hz'; + } + return '${frequencyHz.toStringAsFixed(2)} Hz'; + } +} diff --git a/open_wearable/lib/widgets/devices/connect_devices_page.dart b/open_wearable/lib/widgets/devices/connect_devices_page.dart index 96bc20ef..8518aca0 100644 --- a/open_wearable/lib/widgets/devices/connect_devices_page.dart +++ b/open_wearable/lib/widgets/devices/connect_devices_page.dart @@ -1,8 +1,11 @@ import 'dart:async'; +import 'dart:typed_data'; import 'package:flutter/material.dart'; import 'package:flutter_platform_widgets/flutter_platform_widgets.dart'; import 'package:open_earable_flutter/open_earable_flutter.dart' hide logger; +import 'package:open_wearable/models/this_device_wearable.dart'; +import 'package:open_wearable/view_models/sensor_recorder_provider.dart'; import 'package:open_wearable/view_models/wearables_provider.dart'; import 'package:provider/provider.dart'; @@ -26,11 +29,13 @@ class _ConnectDevicesPageState extends State { List discoveredDevices = []; Map connectingDevices = {}; + DiscoveredDevice? _thisDeviceEntry; @override void initState() { super.initState(); _startScanning(); + _addThisDeviceToDiscovered(); } @override @@ -46,13 +51,21 @@ class _ConnectDevicesPageState extends State { trailing: Icon(PlatformIcons(context).checkMark), ); }).toList(); - List discoveredDevicesWidgets = discoveredDevices.map((device) { + final connectedIds = + wearablesProvider.wearables.map((wearable) => wearable.deviceId).toSet(); + List discoveredDevicesWidgets = discoveredDevices + .where((device) => !connectedIds.contains(device.id)) + .map((device) { return PlatformListTile( title: PlatformText(device.name), subtitle: PlatformText(device.id), trailing: _buildTrailingWidget(device.id), onTap: () { - _connectToDevice(device, context); + if (_thisDeviceEntry?.id == device.id) { + _connectToThisDevice(context); + } else { + _connectToDevice(device, context); + } }, ); }).toList(); @@ -117,6 +130,54 @@ class _ConnectDevicesPageState extends State { }); } + Future _addThisDeviceToDiscovered() async { + if (_thisDeviceEntry != null) return; + final profile = await DeviceProfile.fetch(); + if (!mounted) return; + + final thisDevice = DiscoveredDevice( + id: profile.deviceId, + name: profile.displayName, + manufacturerData: Uint8List(0), + rssi: 0, + serviceUuids: const [], + ); + + setState(() { + _thisDeviceEntry = thisDevice; + if (!discoveredDevices.any((device) => device.id == thisDevice.id)) { + discoveredDevices.insert(0, thisDevice); + } + }); + } + + Future _connectToThisDevice(BuildContext context) async { + final device = _thisDeviceEntry; + if (device == null) return; + + setState(() { + connectingDevices[device.id] = true; + }); + + try { + final wearable = await ThisDeviceWearable.create( + disconnectNotifier: WearableDisconnectNotifier(), + ); + if (!context.mounted) return; + context.read().addWearable(wearable); + context.read().addWearable(wearable); + setState(() { + discoveredDevices.removeWhere((d) => d.id == device.id); + }); + } finally { + if (context.mounted) { + setState(() { + connectingDevices.remove(device.id); + }); + } + } + } + Future _connectToDevice( DiscoveredDevice device, BuildContext context, diff --git a/open_wearable/macos/Flutter/GeneratedPluginRegistrant.swift b/open_wearable/macos/Flutter/GeneratedPluginRegistrant.swift index 7da018f9..d09d4ff1 100644 --- a/open_wearable/macos/Flutter/GeneratedPluginRegistrant.swift +++ b/open_wearable/macos/Flutter/GeneratedPluginRegistrant.swift @@ -5,20 +5,24 @@ import FlutterMacOS import Foundation +import device_info_plus import file_picker import file_selector_macos import flutter_archive import open_file_mac +import path_provider_foundation import share_plus import shared_preferences_foundation import universal_ble import url_launcher_macos func RegisterGeneratedPlugins(registry: FlutterPluginRegistry) { + DeviceInfoPlusMacosPlugin.register(with: registry.registrar(forPlugin: "DeviceInfoPlusMacosPlugin")) FilePickerPlugin.register(with: registry.registrar(forPlugin: "FilePickerPlugin")) FileSelectorPlugin.register(with: registry.registrar(forPlugin: "FileSelectorPlugin")) FlutterArchivePlugin.register(with: registry.registrar(forPlugin: "FlutterArchivePlugin")) OpenFilePlugin.register(with: registry.registrar(forPlugin: "OpenFilePlugin")) + PathProviderPlugin.register(with: registry.registrar(forPlugin: "PathProviderPlugin")) SharePlusMacosPlugin.register(with: registry.registrar(forPlugin: "SharePlusMacosPlugin")) SharedPreferencesPlugin.register(with: registry.registrar(forPlugin: "SharedPreferencesPlugin")) UniversalBlePlugin.register(with: registry.registrar(forPlugin: "UniversalBlePlugin")) diff --git a/open_wearable/macos/Podfile.lock b/open_wearable/macos/Podfile.lock index 1abbe1f3..a8f8885a 100644 --- a/open_wearable/macos/Podfile.lock +++ b/open_wearable/macos/Podfile.lock @@ -1,4 +1,6 @@ PODS: + - device_info_plus (0.0.1): + - FlutterMacOS - file_picker (0.0.1): - FlutterMacOS - file_selector_macos (0.0.1): @@ -7,7 +9,7 @@ PODS: - FlutterMacOS - ZIPFoundation (= 0.9.19) - FlutterMacOS (1.0.0) - - open_file_mac (0.0.1): + - open_file_mac (1.0.3): - FlutterMacOS - path_provider_foundation (0.0.1): - Flutter @@ -25,6 +27,7 @@ PODS: - ZIPFoundation (0.9.19) DEPENDENCIES: + - device_info_plus (from `Flutter/ephemeral/.symlinks/plugins/device_info_plus/macos`) - file_picker (from `Flutter/ephemeral/.symlinks/plugins/file_picker/macos`) - file_selector_macos (from `Flutter/ephemeral/.symlinks/plugins/file_selector_macos/macos`) - flutter_archive (from `Flutter/ephemeral/.symlinks/plugins/flutter_archive/macos`) @@ -41,6 +44,8 @@ SPEC REPOS: - ZIPFoundation EXTERNAL SOURCES: + device_info_plus: + :path: Flutter/ephemeral/.symlinks/plugins/device_info_plus/macos file_picker: :path: Flutter/ephemeral/.symlinks/plugins/file_picker/macos file_selector_macos: @@ -63,11 +68,12 @@ EXTERNAL SOURCES: :path: Flutter/ephemeral/.symlinks/plugins/url_launcher_macos/macos SPEC CHECKSUMS: + device_info_plus: 4fb280989f669696856f8b129e4a5e3cd6c48f76 file_picker: 7584aae6fa07a041af2b36a2655122d42f578c1a file_selector_macos: 9e9e068e90ebee155097d00e89ae91edb2374db7 flutter_archive: 07888d9aeb79da005e0ad8b9d347d17cdea07f68 FlutterMacOS: d0db08ddef1a9af05a5ec4b724367152bb0500b1 - open_file_mac: 01874b6d6a2c1485ac9b126d7105b99102dea2cf + open_file_mac: 76f06c8597551249bdb5e8fd8827a98eae0f4585 path_provider_foundation: bb55f6dbba17d0dccd6737fe6f7f34fbd0376880 share_plus: 510bf0af1a42cd602274b4629920c9649c52f4cc shared_preferences_foundation: 7036424c3d8ec98dfe75ff1667cb0cd531ec82bb diff --git a/open_wearable/pubspec.lock b/open_wearable/pubspec.lock index 034ce0e3..cb7a8ddc 100644 --- a/open_wearable/pubspec.lock +++ b/open_wearable/pubspec.lock @@ -45,10 +45,10 @@ packages: dependency: transitive description: name: characters - sha256: faf38497bda5ead2a8c7615f4f7939df04333478bf32e4173fcb06d428b5716b + sha256: f71061c654a3380576a52b451dd5532377954cf9dbd272a78fc8479606670803 url: "https://pub.dev" source: hosted - version: "1.4.1" + version: "1.4.0" clock: dependency: transitive description: @@ -57,14 +57,6 @@ packages: url: "https://pub.dev" source: hosted version: "1.1.2" - code_assets: - dependency: transitive - description: - name: code_assets - sha256: "83ccdaa064c980b5596c35dd64a8d3ecc68620174ab9b90b6343b753aa721687" - url: "https://pub.dev" - source: hosted - version: "1.0.0" collection: dependency: transitive description: @@ -125,10 +117,26 @@ packages: dependency: transitive description: name: dbus - sha256: "79e0c23480ff85dc68de79e2cd6334add97e48f7f4865d17686dd6ea81a47e8c" + sha256: d0c98dcd4f5169878b6cf8f6e0a52403a9dff371a3e2f019697accbf6f44a270 url: "https://pub.dev" source: hosted - version: "0.7.11" + version: "0.7.12" + device_info_plus: + dependency: "direct main" + description: + name: device_info_plus + sha256: "4df8babf73058181227e18b08e6ea3520cf5fc5d796888d33b7cb0f33f984b7c" + url: "https://pub.dev" + source: hosted + version: "12.3.0" + device_info_plus_platform_interface: + dependency: transitive + description: + name: device_info_plus_platform_interface + sha256: e1ea89119e34903dca74b883d0dd78eb762814f97fb6c76f35e9ff74d261a18f + url: "https://pub.dev" + source: hosted + version: "7.0.3" equatable: dependency: transitive description: @@ -336,14 +344,6 @@ packages: description: flutter source: sdk version: "0.0.0" - glob: - dependency: transitive - description: - name: glob - sha256: c3f1ee72c96f8f78935e18aa8cecced9ab132419e8625dc187e1c2408efc20de - url: "https://pub.dev" - source: hosted - version: "2.1.3" go_router: dependency: "direct main" description: @@ -352,14 +352,6 @@ packages: url: "https://pub.dev" source: hosted version: "14.8.1" - hooks: - dependency: transitive - description: - name: hooks - sha256: "7a08a0d684cb3b8fb604b78455d5d352f502b68079f7b80b831c62220ab0a4f6" - url: "https://pub.dev" - source: hosted - version: "1.0.1" http: dependency: "direct main" description: @@ -444,18 +436,18 @@ packages: dependency: transitive description: name: matcher - sha256: "12956d0ad8390bbcc63ca2e1469c0619946ccb52809807067a7020d57e647aa6" + sha256: dc58c723c3c24bf8d3e2d3ad3f2f9d7bd9cf43ec6feaa64181775e60190153f2 url: "https://pub.dev" source: hosted - version: "0.12.18" + version: "0.12.17" material_color_utilities: dependency: transitive description: name: material_color_utilities - sha256: "9c337007e82b1889149c82ed242ed1cb24a66044e30979c44912381e9be4c48b" + sha256: f7142bb1154231d7ea5f96bc7bde4bda2a0945d2806bb11670e30b850d56bdec url: "https://pub.dev" source: hosted - version: "0.13.0" + version: "0.11.1" mcumgr_flutter: dependency: "direct main" description: @@ -480,14 +472,6 @@ packages: url: "https://pub.dev" source: hosted version: "2.0.0" - native_toolchain_c: - dependency: transitive - description: - name: native_toolchain_c - sha256: "89e83885ba09da5fdf2cdacc8002a712ca238c28b7f717910b34bcd27b0d03ac" - url: "https://pub.dev" - source: hosted - version: "0.17.4" nested: dependency: transitive description: @@ -496,22 +480,14 @@ packages: url: "https://pub.dev" source: hosted version: "1.0.0" - objective_c: - dependency: transitive - description: - name: objective_c - sha256: "983c7fa1501f6dcc0cb7af4e42072e9993cb28d73604d25ebf4dab08165d997e" - url: "https://pub.dev" - source: hosted - version: "9.2.5" open_earable_flutter: dependency: "direct main" description: name: open_earable_flutter - sha256: "23b784abdb9aa2a67afd6bcf22778cc9e3d124eba5a4d02f49443581fa3f8958" + sha256: "3176341e123cd3c4b870913d3f1d5282d47ee524347dbe414e0ecec18c84b117" url: "https://pub.dev" source: hosted - version: "2.3.1" + version: "2.3.2" open_file: dependency: "direct main" description: @@ -612,10 +588,10 @@ packages: dependency: transitive description: name: path_provider_foundation - sha256: "2a376b7d6392d80cd3705782d2caa734ca4727776db0b6ec36ef3f1855197699" + sha256: "6d13aece7b3f5c5a9731eaf553ff9dcbc2eff41087fd2df587fd0fed9a3eb0c4" url: "https://pub.dev" source: hosted - version: "2.6.0" + version: "2.5.1" path_provider_linux: dependency: transitive description: @@ -729,7 +705,7 @@ packages: source: hosted version: "6.1.5+1" pub_semver: - dependency: transitive + dependency: "direct main" description: name: pub_semver sha256: "5bfcf68ca79ef689f8990d1160781b4bad40a3bd5e5218ad4076ddb7f4081585" @@ -744,6 +720,22 @@ packages: url: "https://pub.dev" source: hosted version: "0.28.0" + sensors_plus: + dependency: "direct main" + description: + name: sensors_plus + sha256: "56e8cd4260d9ed8e00ecd8da5d9fdc8a1b2ec12345a750dfa51ff83fcf12e3fa" + url: "https://pub.dev" + source: hosted + version: "7.0.0" + sensors_plus_platform_interface: + dependency: transitive + description: + name: sensors_plus_platform_interface + sha256: "58815d2f5e46c0c41c40fb39375d3f127306f7742efe3b891c0b1c87e2b5cd5d" + url: "https://pub.dev" + source: hosted + version: "2.0.1" share_plus: dependency: "direct main" description: @@ -825,10 +817,10 @@ packages: dependency: transitive description: name: source_span - sha256: "254ee5351d6cb365c859e20ee823c3bb479bf4a293c22d17a9f1bf144ce86f7c" + sha256: "56a02f1f4cd1a2d96303c0144c93bd6d909eea6bee6bf5a0e0b685edbd4c47ab" url: "https://pub.dev" source: hosted - version: "1.10.1" + version: "1.10.2" stack_trace: dependency: transitive description: @@ -865,10 +857,10 @@ packages: dependency: transitive description: name: test_api - sha256: "19a78f63e83d3a61f00826d09bc2f60e191bf3504183c001262be6ac75589fb8" + sha256: ab2726c1a94d3176a45960b6234466ec367179b87dd74f1611adb1f3b5fb9d55 url: "https://pub.dev" source: hosted - version: "0.7.8" + version: "0.7.7" tuple: dependency: transitive description: @@ -913,10 +905,10 @@ packages: dependency: transitive description: name: url_launcher_ios - sha256: cfde38aa257dae62ffe79c87fab20165dfdf6988c1d31b58ebf59b9106062aad + sha256: b1aca26728b7cc7a3af971bb6f601554a8ae9df2e0a006de8450ba06a17ad36a url: "https://pub.dev" source: hosted - version: "6.3.6" + version: "6.4.0" url_launcher_linux: dependency: transitive description: @@ -1021,6 +1013,14 @@ packages: url: "https://pub.dev" source: hosted version: "5.15.0" + win32_registry: + dependency: transitive + description: + name: win32_registry + sha256: "6f1b564492d0147b330dd794fee8f512cec4977957f310f9951b5f9d83618dae" + url: "https://pub.dev" + source: hosted + version: "2.1.0" xdg_directories: dependency: transitive description: @@ -1037,14 +1037,6 @@ packages: url: "https://pub.dev" source: hosted version: "6.6.1" - yaml: - dependency: transitive - description: - name: yaml - sha256: b9da305ac7c39faa3f030eccd175340f968459dae4af175130b3fc47e40d76ce - url: "https://pub.dev" - source: hosted - version: "3.1.3" sdks: - dart: ">=3.10.3 <4.0.0" - flutter: ">=3.38.4" + dart: ">=3.10.0 <4.0.0" + flutter: ">=3.38.0" diff --git a/open_wearable/pubspec.yaml b/open_wearable/pubspec.yaml index 6b9c9fa2..890d8a8c 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.3.1 + open_earable_flutter: ^2.3.2 flutter_platform_widgets: ^9.0.0 provider: ^6.1.2 logger: ^2.5.0 @@ -55,6 +55,9 @@ dependencies: url_launcher: ^6.3.2 go_router: ^14.6.2 http: ^1.6.0 + sensors_plus: ^7.0.0 + device_info_plus: ^12.3.0 + pub_semver: ^2.2.0 dev_dependencies: flutter_test: