diff --git a/lib/features/midi/providers/midi_connection_notifier.dart b/lib/features/midi/providers/midi_connection_notifier.dart index 49c9425..f9b17a5 100644 --- a/lib/features/midi/providers/midi_connection_notifier.dart +++ b/lib/features/midi/providers/midi_connection_notifier.dart @@ -24,6 +24,9 @@ class MidiConnectionNotifier extends Notifier { static const Duration _findTargetTimeout = Duration(seconds: 8); static const Duration _reconnectAttemptTimeout = Duration(seconds: 12); static const Duration _connectedPublishTimeout = Duration(seconds: 3); + static const Duration _longBackgroundReconnectThreshold = Duration( + minutes: 3, + ); static const bool _debugLog = midiDebug; bool _startupAttempted = false; @@ -34,6 +37,8 @@ class MidiConnectionNotifier extends Notifier { bool _backgrounded = false; bool _attemptInFlight = false; + DateTime? _backgroundBeganAt; + Duration? _lastBackgroundDuration; // Dedupe guard: prevents repeated persistence writes when the // connected-device stream re-emits the same device id. @@ -211,8 +216,20 @@ class MidiConnectionNotifier extends Notifier { } void setBackgrounded(bool value) { + if (_backgrounded == value) { + return; + } _backgrounded = value; if (_debugLog) debugPrint('[CONN] backgrounded=$_backgrounded'); + if (_backgrounded) { + _backgroundBeganAt = DateTime.now(); + } else { + final beganAt = _backgroundBeganAt; + _backgroundBeganAt = null; + _lastBackgroundDuration = beganAt == null + ? null + : DateTime.now().difference(beganAt); + } if (_backgrounded) { // Controller-owned policy: background cancels attempts and stops scanning. unawaited(cancel(reason: 'background')); @@ -301,13 +318,19 @@ class MidiConnectionNotifier extends Notifier { // If already connected to the last connected device, do nothing. final current = ref.read(midiDeviceManagerProvider).connectedDevice; + final forceResumeReconnect = + reason == 'resume' && + (_lastBackgroundDuration ?? Duration.zero) >= + _longBackgroundReconnectThreshold; if (current?.id == lastConnectedDeviceId && current?.isConnected == true) { - final stillConnected = await _withTimeout( - _midi.isStillConnected(lastConnectedDeviceId), - timeout: const Duration(seconds: 2), - onTimeout: false, - ); + final stillConnected = forceResumeReconnect + ? false + : await _withTimeout( + _midi.isStillConnected(lastConnectedDeviceId), + timeout: const Duration(seconds: 2), + onTimeout: false, + ); if (stillConnected == true) { if (_debugLog) { debugPrint('[CONN] stillConnected id=$lastConnectedDeviceId'); diff --git a/lib/features/midi/providers/midi_device_manager.dart b/lib/features/midi/providers/midi_device_manager.dart index 39fff98..0f166a4 100644 --- a/lib/features/midi/providers/midi_device_manager.dart +++ b/lib/features/midi/providers/midi_device_manager.dart @@ -343,7 +343,9 @@ class MidiDeviceManager extends Notifier { bool scanIfNeeded = false, }) { final inflight = _reconcileInFlight; - if (inflight != null) return inflight; + if (inflight != null) { + return inflight; + } if (_debugLog) { debugPrint( @@ -365,7 +367,9 @@ class MidiDeviceManager extends Notifier { required bool scanIfNeeded, }) async { final current = state.connectedDevice; - if (current == null) return; + if (current == null) { + return; + } try { // If we believe we are connected, it is reasonable to prime the central @@ -527,7 +531,6 @@ class MidiDeviceManager extends Notifier { 'connected=${state.connectedDevice?.id}/${state.connectedDevice?.isConnected}', ); } - state = state.copyWith(devices: devices); _signalDevicesChanged();