import 'package:flutter/foundation.dart'; import '../../dji_service.dart'; import 'settings_overlay.dart'; /// KeyManager values the settings sheet watches (plan P6), keyed by /// [DjiKey.id], from the bridge's `key` events — overlaid with the sheet's /// optimistic writes through [SettingsOverlay], like camera and gimbal settings. /// /// [supported] records `isKeySupported` per key: a row hides once its key is /// known to be unsupported, and stays visible (as "—") while still unknown. class KeyState extends ChangeNotifier with SettingsOverlay { final Map _supported = {}; /// True / false once asked, null while unknown. bool? supported(DjiKey key) => _supported[key.id]; Object? value(DjiKey key) => setting(key.id); bool pendingFor(DjiKey key) => isPending(key.id); void setSupported(DjiKey key, bool on) { if (_supported[key.id] == on) return; _supported[key.id] = on; notifyListeners(); } /// One `key` event: `{id, value}`. void apply(String id, Object? value) { replaceReported({...reportedSettings, id: value}); notifyListeners(); } /// A new aircraft answers `isKeySupported` afresh. void forgetSupport() { if (_supported.isEmpty) return; _supported.clear(); notifyListeners(); } void clear() { _supported.clear(); clearSettings(); notifyListeners(); } } /// The `listenKey` subscriptions one screen holds open — the settings sheet /// while it is up. [watch] asks `isKeySupported` once per key and listens to /// the supported ones; [dispose] cancels them all. class KeySubscriptions { KeySubscriptions(this.dji, this.state); final DjiService dji; final KeyState state; final Set _asked = {}; final Set _listening = {}; bool _disposed = false; Future watch(Iterable keys) async { await Future.wait(>[ for (final DjiKey k in keys) if (_asked.add(k)) _watchOne(k), ]); } Future _watchOne(DjiKey key) async { bool ok; try { ok = await dji.isKeySupported(key); } catch (_) { ok = false; } if (_disposed) return; state.setSupported(key, ok); if (!ok) return; try { final String id = await dji.listenKey(key); if (_disposed) { await dji.cancelKey(id); } else { _listening.add(id); } } catch (_) { // The row shows "—" until a value arrives some other way. } } /// Asks every key again — after a reconnect or product change. Future rewatch(Iterable keys) async { await _cancelAll(); _asked.clear(); state.forgetSupport(); await watch(keys); } Future _cancelAll() async { final List ids = _listening.toList(); _listening.clear(); for (final String id in ids) { try { await dji.cancelKey(id); } catch (_) {} } } void dispose() { _disposed = true; _cancelAll(); } }