Skip to content

Commit dc23211

Browse files
committed
sessions: make the launch display authoritative and finish the foreground policy
startSession re-derived the emulator display from the global roles-swapped flag with a hardcoded DEFAULT_DISPLAY+1, clobbering the per-game display target handleDualPlay had already resolved, so cross-display sessions got tracked as same-display and killed by resume cleanup with their input never forwarded. The launch path write is now authoritative and startSession only falls back through assignEmulatorDisplayForSessionStart, which routes via DisplayAffinityHelper for the real secondary display id. SecondaryHomeActivity.onResume drops the 15s isPackageInForeground poll for the display rule mirrored from MainActivity: the companion surfacing on the display the emulator owned means the game left it and the session is over. Both activities enforce the same invariant on their own display. The immediate-end policy is scoped to dual-screen devices; single screen keeps the 15s grace in cleanupStaleSession so quick hop-out-and-back relaunches still resume the running emulator. onForegroundChanged reads in-memory hasLiveSession instead of the session prefs flag that races the async session-start write and lags save sync. onTaskRemoved treats a null DSM holder as no live session, a persisted session across a process restart is an orphan and must not block the bottom-screen release.
1 parent 9b8970a commit dc23211

5 files changed

Lines changed: 30 additions & 24 deletions

File tree

app/src/main/kotlin/com/nendo/argosy/DualScreenManager.kt

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2225,6 +2225,12 @@ class DualScreenManager(
22252225
emulatorDisplayId = displayId
22262226
}
22272227

2228+
/** Fallback for launch paths that never assigned a display; the launch path's write wins. */
2229+
fun assignEmulatorDisplayForSessionStart() {
2230+
if (emulatorDisplayId != null) return
2231+
emulatorDisplayId = displayAffinityHelper.getEmulatorDisplayId(_isRolesSwapped.value)
2232+
}
2233+
22282234
fun startStartupGuard() {
22292235
startupGuardJob?.cancel()
22302236
startupGuardJob = scope.launch {

app/src/main/kotlin/com/nendo/argosy/MainActivity.kt

Lines changed: 13 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -85,6 +85,7 @@ class MainActivity : ComponentActivity() {
8585
@Inject lateinit var ambientLedManager: AmbientLedManager
8686
@Inject lateinit var screenCaptureManager: ScreenCaptureManager
8787
@Inject lateinit var displayAffinityHelper: DisplayAffinityHelper
88+
@Inject lateinit var permissionHelper: com.nendo.argosy.util.PermissionHelper
8889
@Inject lateinit var gameActionsDelegate: GameActionsDelegate
8990
@Inject lateinit var gameLaunchDelegate: GameLaunchDelegate
9091
@Inject lateinit var saveCacheManager: SaveCacheManager
@@ -620,10 +621,12 @@ class MainActivity : ComponentActivity() {
620621
}
621622

622623
/**
623-
* The launcher UI returning to the foreground means the session on this display
624-
* is over: end it and restore the companion immediately. Only a session running
625-
* on a different display (swapped roles / per-game display target) survives,
626-
* since the game and the launcher UI legitimately coexist there.
624+
* On dual-screen devices the launcher UI returning to the foreground means the
625+
* session on this display is over: end it and restore the companion immediately.
626+
* Only a session on a different display (swapped roles / per-game display target)
627+
* survives, since the game and the launcher UI legitimately coexist there.
628+
* Single-screen devices keep the resume-friendly grace: a session whose emulator
629+
* was foregrounded within the last 15s stays alive so relaunching resumes it.
627630
*/
628631
private fun cleanupStaleSession() {
629632
activityScope.launch {
@@ -632,6 +635,12 @@ class MainActivity : ComponentActivity() {
632635
val emulatorDisplay = dualScreenManager.emulatorDisplayId
633636
val ownDisplay = window.decorView.display?.displayId
634637
if (emulatorDisplay != null && ownDisplay != null && emulatorDisplay != ownDisplay) return@launch
638+
if (!displayAffinityHelper.hasSecondaryDisplay && emulatorDisplay != null) {
639+
val emulatorPkg = sessionStateStore.getEmulatorPackage()
640+
if (emulatorPkg != null &&
641+
permissionHelper.isPackageInForeground(this@MainActivity, emulatorPkg, 15_000)
642+
) return@launch
643+
}
635644
if (playSessionTracker.activeSession.value == null &&
636645
preferencesRepository.getPersistedSession() == null
637646
) return@launch

app/src/main/kotlin/com/nendo/argosy/data/emulator/PlaySessionTracker.kt

Lines changed: 1 addition & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -448,12 +448,7 @@ class PlaySessionTracker @Inject constructor(
448448
variantFileId = variantFileId
449449
)
450450

451-
val displayId = if (sessionStateStore.isRolesSwapped()) {
452-
android.view.Display.DEFAULT_DISPLAY + 1
453-
} else {
454-
android.view.Display.DEFAULT_DISPLAY
455-
}
456-
DualScreenManagerHolder.instance?.setEmulatorDisplay(displayId)
451+
DualScreenManagerHolder.instance?.assignEmulatorDisplayForSessionStart()
457452

458453
broadcastSessionChanged(gameId, channelName, isHardcore)
459454

app/src/main/kotlin/com/nendo/argosy/hardware/CompanionGuardService.kt

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,6 @@ import androidx.core.app.NotificationCompat
1515
import com.nendo.argosy.DualScreenManagerHolder
1616
import com.nendo.argosy.MainActivity
1717
import com.nendo.argosy.R
18-
import com.nendo.argosy.data.preferences.SessionStateStore
1918
import com.nendo.argosy.util.SecondaryHomeComponent
2019

2120
class CompanionGuardService : Service() {
@@ -69,8 +68,7 @@ class CompanionGuardService : Service() {
6968
if (rootIntent?.component?.className != MainActivity::class.java.name) return
7069
if (SecondaryHomeComponent.isDefaultHome(this)) return
7170
val dsm = DualScreenManagerHolder.instance
72-
val sessionLive = dsm?.hasLiveSession()
73-
?: SessionStateStore(applicationContext).hasActiveSession()
71+
val sessionLive = dsm?.hasLiveSession() == true
7472
if (sessionLive) return
7573
Log.i(TAG, "Main task removed while not default home, releasing secondary display")
7674
SecondaryHomeComponent.setEnabled(this, false)

app/src/main/kotlin/com/nendo/argosy/hardware/SecondaryHomeActivity.kt

Lines changed: 9 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -287,16 +287,14 @@ class SecondaryHomeActivity :
287287
isSaveDirty = store.isSaveDirty()
288288
broadcasts.broadcastCompanionResumed()
289289

290-
if (isGameActive && dsm.emulatorDisplayId != null && !dsm.isLaunchingGame) {
291-
val emulatorPkg = dsm.sessionStateStore.getEmulatorPackage()
292-
if (emulatorPkg != null) {
293-
val helper = com.nendo.argosy.util.PermissionHelper()
294-
if (!helper.isPackageInForeground(this, emulatorPkg, withinMs = 15_000)) {
295-
android.util.Log.d("SecondaryHome", "Emulator exited on secondary display, ending session")
296-
dsm.emulatorDisplayId = null
297-
dsm.playSessionTracker.endSessionInBackground()
298-
dsm.broadcastSessionCleared()
299-
}
290+
if (isGameActive && !dsm.isLaunchingGame) {
291+
val emulatorDisplay = dsm.emulatorDisplayId
292+
val ownDisplay = window.decorView.display?.displayId
293+
if (emulatorDisplay != null && ownDisplay != null && emulatorDisplay == ownDisplay) {
294+
android.util.Log.d("SecondaryHome", "Companion resumed on the emulator's display, ending session")
295+
dsm.emulatorDisplayId = null
296+
dsm.playSessionTracker.endSessionInBackground()
297+
dsm.broadcastSessionCleared()
300298
}
301299
}
302300
}
@@ -384,7 +382,7 @@ class SecondaryHomeActivity :
384382
isArgosyForeground = isForeground
385383
if (isForeground && isGameActive) {
386384
val outOfGame = if (isShowcaseRole) {
387-
!dsm.sessionStateStore.hasActiveSession()
385+
!dsm.hasLiveSession()
388386
} else {
389387
true
390388
}

0 commit comments

Comments
 (0)