fix(profiles): skip the resume profile prompt during a live companion-remote session

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
This commit is contained in:
edde746
2026-08-24 10:06:00 +02:00
parent a9f2c1ff99
commit 0fbf2e205c
2 changed files with 19 additions and 3 deletions
+13 -2
View File
@@ -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<MainScreen>
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<CompanionRemoteProvider>().isInSession,
)) {
_showProfileSelectionOnResume();
}
+6 -1
View File
@@ -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);