From 0fbf2e205c8e8d797ed6f3da45db62e28f1d283f Mon Sep 17 00:00:00 2001 From: edde746 <86283021+edde746@users.noreply.github.com> Date: Mon, 24 Aug 2026 10:06:00 +0200 Subject: [PATCH] fix(profiles): skip the resume profile prompt during a live companion-remote session MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit With "Ask for profile on app open" enabled, every screen-off or app switch on a phone re-pushed the profile picker and PIN on resume, burying a companion-remote session that survives backgrounding since the reconnect cycle was made lifecycle-aware. Suppress the resume prompt while a companion session is live, mirroring the active-playback exemption — the session already belongs to the profile that started it. close #2087 --- lib/screens/main_screen.dart | 15 +++++++++++++-- test/screens/main_screen_layout_test.dart | 7 ++++++- 2 files changed, 19 insertions(+), 3 deletions(-) diff --git a/lib/screens/main_screen.dart b/lib/screens/main_screen.dart index 219a3988b..ad6e89a43 100644 --- a/lib/screens/main_screen.dart +++ b/lib/screens/main_screen.dart @@ -102,7 +102,10 @@ bool shouldHandleDesktopRootEscape({ /// mid-stream must resume the stream, not stack the root-navigator picker /// over the live player route, whose focus self-heal fights the picker for /// the remote (#2034) — the playback session already belongs to the profile -/// that started it. +/// that started it. Likewise never during a live companion-remote session: +/// a phone driving another device backgrounds and sleeps constantly, and +/// the picker + PIN would bury a session that already belongs to the +/// profile that started it (#2087). @visibleForTesting bool shouldShowProfileSelectionOnResume({ required bool resumedFromBackground, @@ -110,12 +113,14 @@ bool shouldShowProfileSelectionOnResume({ required bool alreadyShowingProfileSelection, required bool isMobilePlatform, required bool hasActiveVideoPlayback, + required bool hasActiveCompanionRemoteSession, }) { return resumedFromBackground && !isOffline && !alreadyShowingProfileSelection && isMobilePlatform && - !hasActiveVideoPlayback; + !hasActiveVideoPlayback && + !hasActiveCompanionRemoteSession; } /// Latches whether the app has genuinely left the foreground since the last @@ -1140,6 +1145,12 @@ class _MainScreenState extends State alreadyShowingProfileSelection: _isShowingProfileSelection, isMobilePlatform: Platform.isAndroid || Platform.isIOS, hasActiveVideoPlayback: VideoPlayerScreenState.activeGlobalKey != null, + // Short-circuit on resumedFromBackground: the provider is lazy and + // otherwise unused on phones, so an unconditional read would create it + // on the first lifecycle event for users who never open the remote. + // isInSession, not isConnected: on resume the held reconnect cycle + // means the session is typically still `reconnecting` (#2035). + hasActiveCompanionRemoteSession: resumedFromBackground && context.read().isInSession, )) { _showProfileSelectionOnResume(); } diff --git a/test/screens/main_screen_layout_test.dart b/test/screens/main_screen_layout_test.dart index e0ec005db..1fa9fa5cc 100644 --- a/test/screens/main_screen_layout_test.dart +++ b/test/screens/main_screen_layout_test.dart @@ -147,13 +147,14 @@ void main() { ); }); - test('resume prompt is suppressed during active video playback (#2034)', () { + test('resume prompt is suppressed during playback (#2034) and companion sessions (#2087)', () { bool should({ bool resumedFromBackground = true, bool isOffline = false, bool alreadyShowingProfileSelection = false, bool isMobilePlatform = true, bool hasActiveVideoPlayback = false, + bool hasActiveCompanionRemoteSession = false, }) { return shouldShowProfileSelectionOnResume( resumedFromBackground: resumedFromBackground, @@ -161,6 +162,7 @@ void main() { alreadyShowingProfileSelection: alreadyShowingProfileSelection, isMobilePlatform: isMobilePlatform, hasActiveVideoPlayback: hasActiveVideoPlayback, + hasActiveCompanionRemoteSession: hasActiveCompanionRemoteSession, ); } @@ -168,6 +170,9 @@ void main() { // Waking the device mid-stream resumes the stream; the picker would // fight the player's focus self-heal for the remote. expect(should(hasActiveVideoPlayback: true), isFalse); + // A phone driving another device backgrounds constantly; the picker + + // PIN would bury the live remote session. + expect(should(hasActiveCompanionRemoteSession: true), isFalse); expect(should(resumedFromBackground: false), isFalse); expect(should(isOffline: true), isFalse); expect(should(alreadyShowingProfileSelection: true), isFalse);