Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 1 addition & 3 deletions open_wearable/ios/Flutter/AppFrameworkInfo.plist
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,5 @@
<string>????</string>
<key>CFBundleVersion</key>
<string>1.0</string>
<key>MinimumOSVersion</key>
<string>13.0</string>
</dict>
</plist>
</plist>
3 changes: 2 additions & 1 deletion open_wearable/lib/router.dart
Original file line number Diff line number Diff line change
Expand Up @@ -55,8 +55,9 @@ final GoRouter router = GoRouter(
name: 'fota',
redirect: (context, state) {
final bool isAndroid = !kIsWeb && Platform.isAndroid;
final bool isIOS = !kIsWeb && Platform.isIOS;

if (!isAndroid) {
if (!isAndroid && !isIOS) {
WidgetsBinding.instance.addPostFrameCallback((_) {
final ctx = rootNavigatorKey.currentContext;
if (ctx == null) return;
Expand Down
91 changes: 62 additions & 29 deletions open_wearable/lib/view_models/wearables_provider.dart
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,9 @@ 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}';
Expand All @@ -65,14 +67,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 {
Expand All @@ -89,9 +93,9 @@ class WearablesProvider with ChangeNotifier {
Stream<UnsupportedFirmwareEvent> get unsupportedFirmwareStream =>
_unsupportedFirmwareEventsController.stream;

final _wearableEventController =
StreamController<WearableEvent>.broadcast();
Stream<WearableEvent> get wearableEventStream => _wearableEventController.stream;
final _wearableEventController = StreamController<WearableEvent>.broadcast();
Stream<WearableEvent> get wearableEventStream =>
_wearableEventController.stream;

final Map<Wearable, StreamSubscription> _capabilitySubscriptions = {};

Expand Down Expand Up @@ -135,7 +139,8 @@ class WearablesProvider with ChangeNotifier {
}) async {
try {
logger.d('Synchronizing time for wearable ${wearable.name}');
await (wearable.requireCapability<TimeSynchronizable>()).synchronizeTime();
await (wearable.requireCapability<TimeSynchronizable>())
.synchronizeTime();
logger.d('Time synchronized for wearable ${wearable.name}');
_emitWearableEvent(
WearableTimeSynchronizedEvent(
Expand All @@ -144,7 +149,9 @@ class WearablesProvider with ChangeNotifier {
),
);
} catch (e, st) {
logger.w('Failed to synchronize time for wearable ${wearable.name}: $e\n$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',
Expand All @@ -159,28 +166,35 @@ class WearablesProvider with ChangeNotifier {

_wearables.add(wearable);

_capabilitySubscriptions[wearable] = wearable.capabilityRegistered.listen((addedCapabilities) {
_handleCapabilitiesChanged(wearable: wearable, addedCapabilites: addedCapabilities);
_capabilitySubscriptions[wearable] =
wearable.capabilityRegistered.listen((addedCapabilities) {
_handleCapabilitiesChanged(
wearable: wearable,
addedCapabilites: addedCapabilities,
);
});

// Init SensorConfigurationProvider synchronously (no awaits here)
if (wearable.hasCapability<SensorConfigurationManager>()) {
_ensureSensorConfigProvider(wearable);
final notifier = _sensorConfigurationProviders[wearable]!;
for (final config
in (wearable.requireCapability<SensorConfigurationManager>()).sensorConfigurations) {
in (wearable.requireCapability<SensorConfigurationManager>())
.sensorConfigurations) {
if (notifier.getSelectedConfigurationValue(config) == null &&
config.values.isNotEmpty) {
notifier.addSensorConfiguration(config, config.values.first);
}
}
}
if (wearable.hasCapability<TimeSynchronizable>()) {
_scheduleMicrotask(() => _syncTimeAndEmit(
wearable: wearable,
successDescription: 'Time synchronized for ${wearable.name}',
failureDescription: 'Failed to synchronize time for ${wearable.name}',
),);
_scheduleMicrotask(
() => _syncTimeAndEmit(
wearable: wearable,
successDescription: 'Time synchronized for ${wearable.name}',
failureDescription: 'Failed to synchronize time for ${wearable.name}',
),
);
}

// Disconnect listener (sync)
Expand All @@ -195,17 +209,29 @@ class WearablesProvider with ChangeNotifier {
// 2) Slow/async work: run in microtasks so it doesn't block the add
// Stereo pairing (if applicable)
if (wearable.hasCapability<StereoDevice>()) {
_scheduleMicrotask(() => _maybeAutoPairStereoAsync(wearable.requireCapability<StereoDevice>()));
_scheduleMicrotask(
() => _maybeAutoPairStereoAsync(
wearable.requireCapability<StereoDevice>(),
),
);
}

// Firmware support check (if applicable)
if (wearable.hasCapability<DeviceFirmwareVersion>()) {
_scheduleMicrotask(() => _maybeEmitUnsupportedFirmwareAsync(wearable.requireCapability<DeviceFirmwareVersion>()));
_scheduleMicrotask(
() => _maybeEmitUnsupportedFirmwareAsync(
wearable.requireCapability<DeviceFirmwareVersion>(),
),
);
}

// Check for newer firmware (if applicable)
if (wearable.hasCapability<DeviceFirmwareVersion>()) {
_scheduleMicrotask(() => _checkForNewerFirmwareAsync(wearable.requireCapability<DeviceFirmwareVersion>()));
_scheduleMicrotask(
() => _checkForNewerFirmwareAsync(
wearable.requireCapability<DeviceFirmwareVersion>(),
),
);
}
}

Expand All @@ -214,7 +240,8 @@ class WearablesProvider with ChangeNotifier {
void _ensureSensorConfigProvider(Wearable wearable) {
if (!_sensorConfigurationProviders.containsKey(wearable)) {
_sensorConfigurationProviders[wearable] = SensorConfigurationProvider(
sensorConfigurationManager: wearable.requireCapability<SensorConfigurationManager>(),
sensorConfigurationManager:
wearable.requireCapability<SensorConfigurationManager>(),
);
}
}
Expand Down Expand Up @@ -285,7 +312,8 @@ class WearablesProvider with ChangeNotifier {

final currentVersion = await dev.readDeviceFirmwareVersion();
if (currentVersion == null || currentVersion.isEmpty) {
logger.d('Could not read firmware version for ${(dev as Wearable).name}');
logger
.d('Could not read firmware version for ${(dev as Wearable).name}');
return;
}

Expand Down Expand Up @@ -338,18 +366,23 @@ class WearablesProvider with ChangeNotifier {
return _sensorConfigurationProviders[wearable]!;
}

void _handleCapabilitiesChanged({required Wearable wearable, required List<Type> addedCapabilites}) {
void _handleCapabilitiesChanged({
required Wearable wearable,
required List<Type> 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',
),);
_scheduleMicrotask(
() => _syncTimeAndEmit(
wearable: wearable,
successDescription:
'Time synchronized for ${wearable.name} after capability change',
failureDescription:
'Failed to synchronize time for ${wearable.name} after capability change',
),
);
}
}
}
Loading