mirror of
https://github.com/Droid-Deck/DroidDeck.git
synced 2026-11-11 19:11:08 +00:00
fix: a session on an external display stays awake while the phone screen is off (#1211)
This commit is contained in:
1 parent
2862b77495
commit
78932ef836
4 files changed
+108
-11
No files matched your search
@@ -1601,6 +1601,7 @@ class SessionActivity : ComponentActivity(), SurfaceHolder.Callback {
|
||||
intent.getStringExtra(SessionService.EXTRA_STEAM_UI),
|
||||
intent.getStringExtra(SessionService.EXTRA_STEAM_URL),
|
||||
intent.getStringArrayExtra(SessionService.EXTRA_PROGRAM_ARGS),
|
||||
shownOnDisplayId,
|
||||
)
|
||||
}
|
||||
applyFrameGen()
|
||||
@@ -1651,6 +1652,7 @@ class SessionActivity : ComponentActivity(), SurfaceHolder.Callback {
|
||||
|
||||
override fun onConfigurationChanged(newConfig: android.content.res.Configuration) {
|
||||
super.onConfigurationChanged(newConfig)
|
||||
if (started) SessionService.setActivityDisplay(this, shownOnDisplayId)
|
||||
cancelTouchGestures()
|
||||
applyFollowOrientation(newConfig)
|
||||
applyUnfoldedControls()
|
||||
@@ -2274,9 +2276,13 @@ class SessionActivity : ComponentActivity(), SurfaceHolder.Callback {
|
||||
return true
|
||||
}
|
||||
|
||||
private val shownOnDisplayId: Int get() =
|
||||
if (Build.VERSION.SDK_INT >= 30) display?.displayId ?: windowManager.defaultDisplay.displayId
|
||||
else windowManager.defaultDisplay.displayId
|
||||
|
||||
private fun refreshSecondScreenDisplays() {
|
||||
if (!::displayManager.isInitialized) return
|
||||
val primaryId = display?.displayId ?: windowManager.defaultDisplay.displayId
|
||||
val primaryId = shownOnDisplayId
|
||||
val candidates = SecondScreenDisplays.available(displayManager, primaryId)
|
||||
.filter { glassesScreen == null || it.id != glasses.displayId }
|
||||
val oldIds = secondScreenDisplays.map { it.id }.toSet()
|
||||
@@ -2799,7 +2805,7 @@ class SessionActivity : ComponentActivity(), SurfaceHolder.Callback {
|
||||
refreshSecondScreenDisplays()
|
||||
if (secondScreenMode != SecondScreenMode.NONE && secondScreenPresentation == null) showSecondScreen(secondScreenMode)
|
||||
startedScreens++
|
||||
SessionService.setActivityVisible(this, true)
|
||||
SessionService.setActivityVisible(this, true, shownOnDisplayId)
|
||||
Glasses.rescan()
|
||||
updateGlassesScreen()
|
||||
updateHandCamera()
|
||||
|
||||
@@ -22,6 +22,8 @@ import android.os.IBinder
|
||||
import android.os.Handler
|
||||
import android.os.Looper
|
||||
import android.os.PowerManager
|
||||
import android.hardware.display.DisplayManager
|
||||
import android.view.Display
|
||||
import android.util.Log
|
||||
import com.droiddeck.launcher.R
|
||||
import com.droiddeck.launcher.SessionActivity
|
||||
@@ -76,6 +78,7 @@ class SessionService : Service() {
|
||||
private var signInHold = false
|
||||
private val hasVisibleSurface get() = sessionHasVisibleSurface(activityVisible || signInHold, screenOn, xrActivityVisible)
|
||||
private var screenOn = true
|
||||
private var sessionDisplayId = Display.DEFAULT_DISPLAY
|
||||
private var manualPauseRequested = false
|
||||
private var steamSleepToken: String? = null
|
||||
private var nativeSleepToken: String? = null
|
||||
@@ -95,8 +98,8 @@ class SessionService : Service() {
|
||||
private val screenReceiver = object : BroadcastReceiver() {
|
||||
override fun onReceive(context: Context?, intent: Intent?) {
|
||||
when (intent?.action) {
|
||||
Intent.ACTION_SCREEN_OFF -> screenOn = false
|
||||
Intent.ACTION_SCREEN_ON -> screenOn = true
|
||||
Intent.ACTION_SCREEN_OFF -> screenOn = readScreenOn(false)
|
||||
Intent.ACTION_SCREEN_ON -> screenOn = readScreenOn(true)
|
||||
else -> return
|
||||
}
|
||||
if (screenOn) resumeSteamSleepOnReturn()
|
||||
@@ -104,17 +107,49 @@ class SessionService : Service() {
|
||||
updateSuspendPolicy()
|
||||
}
|
||||
}
|
||||
private var displayListenerRegistered = false
|
||||
private val displayListener = object : DisplayManager.DisplayListener {
|
||||
override fun onDisplayAdded(displayId: Int) {}
|
||||
override fun onDisplayRemoved(displayId: Int) {
|
||||
if (displayId != sessionDisplayId) return
|
||||
sessionDisplayId = Display.DEFAULT_DISPLAY
|
||||
refreshScreenOn()
|
||||
}
|
||||
override fun onDisplayChanged(displayId: Int) {
|
||||
if (displayId == sessionDisplayId && displayId != Display.DEFAULT_DISPLAY) refreshScreenOn()
|
||||
}
|
||||
}
|
||||
|
||||
private fun readScreenOn(interactive: Boolean? = null): Boolean {
|
||||
val id = sessionDisplayId
|
||||
val state = if (id == Display.DEFAULT_DISPLAY) null
|
||||
else (getSystemService(Context.DISPLAY_SERVICE) as? DisplayManager)?.getDisplay(id)?.state
|
||||
return sessionScreenOn(id == Display.DEFAULT_DISPLAY,
|
||||
interactive ?: (getSystemService(Context.POWER_SERVICE) as? PowerManager)?.isInteractive ?: true, state)
|
||||
}
|
||||
|
||||
private fun refreshScreenOn() {
|
||||
val on = readScreenOn()
|
||||
if (on == screenOn) return
|
||||
screenOn = on
|
||||
if (on) resumeSteamSleepOnReturn()
|
||||
suspendAttemptFailed = false
|
||||
updateSuspendPolicy()
|
||||
}
|
||||
@Volatile private var sessionGen = 0
|
||||
|
||||
override fun onCreate() {
|
||||
super.onCreate()
|
||||
val power = getSystemService(Context.POWER_SERVICE) as? PowerManager
|
||||
screenOn = power?.isInteractive ?: true
|
||||
screenOn = readScreenOn()
|
||||
registerReceiver(screenReceiver, IntentFilter().apply {
|
||||
addAction(Intent.ACTION_SCREEN_OFF)
|
||||
addAction(Intent.ACTION_SCREEN_ON)
|
||||
})
|
||||
screenReceiverRegistered = true
|
||||
(getSystemService(Context.DISPLAY_SERVICE) as? DisplayManager)?.let {
|
||||
it.registerDisplayListener(displayListener, Handler(Looper.getMainLooper()))
|
||||
displayListenerRegistered = true
|
||||
}
|
||||
}
|
||||
|
||||
override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int {
|
||||
@@ -163,12 +198,19 @@ class SessionService : Service() {
|
||||
ACTION_ACTIVITY_VISIBLE -> {
|
||||
if (!SessionState.pipActive) pipTask = false
|
||||
activityVisible = true
|
||||
sessionDisplayId = intent.getIntExtra(EXTRA_DISPLAY_ID, sessionDisplayId)
|
||||
screenOn = readScreenOn()
|
||||
suspendDownloadNow = false
|
||||
resumeSteamSleepOnReturn()
|
||||
suspendAttemptFailed = false
|
||||
updateSuspendPolicy()
|
||||
return START_NOT_STICKY
|
||||
}
|
||||
ACTION_ACTIVITY_DISPLAY -> {
|
||||
sessionDisplayId = intent.getIntExtra(EXTRA_DISPLAY_ID, sessionDisplayId)
|
||||
refreshScreenOn()
|
||||
return START_NOT_STICKY
|
||||
}
|
||||
ACTION_ACTIVITY_HIDDEN -> {
|
||||
activityVisible = false
|
||||
suspendAttemptFailed = false
|
||||
@@ -232,7 +274,8 @@ class SessionService : Service() {
|
||||
activityVisible = true
|
||||
xrActivityVisible = false
|
||||
signInHold = false
|
||||
screenOn = (getSystemService(Context.POWER_SERVICE) as? PowerManager)?.isInteractive ?: true
|
||||
sessionDisplayId = intent?.getIntExtra(EXTRA_DISPLAY_ID, Display.DEFAULT_DISPLAY) ?: Display.DEFAULT_DISPLAY
|
||||
screenOn = readScreenOn()
|
||||
manualPauseRequested = false
|
||||
suspendDownloadNow = false
|
||||
steamDownloadMonitor.reset()
|
||||
@@ -1022,7 +1065,7 @@ class SessionService : Service() {
|
||||
|
||||
private fun resumeSession(panelVisible: Boolean = true) {
|
||||
if (panelVisible) activityVisible = true
|
||||
screenOn = (getSystemService(Context.POWER_SERVICE) as? PowerManager)?.isInteractive ?: screenOn
|
||||
screenOn = readScreenOn()
|
||||
manualPauseRequested = false
|
||||
suspendDownloadNow = false
|
||||
if (steamSleepToken == null && (nativeSleepToken == null || nativeSleepFallback || nativeSleepIdle)) requestSteamWake()
|
||||
@@ -1248,7 +1291,7 @@ class SessionService : Service() {
|
||||
suspendOperationPending = true
|
||||
SessionEvents.record(if (shouldSuspend) "session.suspend_requested" else "session.resume_requested", mapOf(
|
||||
"policy" to suspendPolicy, "activityVisible" to activityVisible,
|
||||
"screenOn" to screenOn, "steamSleep" to (steamSleepToken != null),
|
||||
"screenOn" to screenOn, "display" to sessionDisplayId, "steamSleep" to (steamSleepToken != null),
|
||||
"xrActivityVisible" to xrActivityVisible,
|
||||
"nativeSleep" to (nativeSleepToken != null),
|
||||
))
|
||||
@@ -1512,6 +1555,10 @@ class SessionService : Service() {
|
||||
unregisterReceiver(screenReceiver)
|
||||
screenReceiverRegistered = false
|
||||
}
|
||||
if (displayListenerRegistered) {
|
||||
(getSystemService(Context.DISPLAY_SERVICE) as? DisplayManager)?.unregisterDisplayListener(displayListener)
|
||||
displayListenerRegistered = false
|
||||
}
|
||||
super.onDestroy()
|
||||
}
|
||||
|
||||
@@ -1699,6 +1746,8 @@ class SessionService : Service() {
|
||||
private const val ACTION_SUSPEND_POLICY_CHANGED = "com.droiddeck.launcher.SUSPEND_POLICY_CHANGED"
|
||||
private const val ACTION_ACTIVITY_VISIBLE = "com.droiddeck.launcher.ACTIVITY_VISIBLE"
|
||||
private const val ACTION_ACTIVITY_HIDDEN = "com.droiddeck.launcher.ACTIVITY_HIDDEN"
|
||||
private const val ACTION_ACTIVITY_DISPLAY = "com.droiddeck.launcher.ACTIVITY_DISPLAY"
|
||||
private const val EXTRA_DISPLAY_ID = "displayId"
|
||||
private const val ACTION_XR_ACTIVITY_VISIBLE = "com.droiddeck.launcher.XR_ACTIVITY_VISIBLE"
|
||||
private const val ACTION_XR_ACTIVITY_HIDDEN = "com.droiddeck.launcher.XR_ACTIVITY_HIDDEN"
|
||||
private const val ACTION_SIGN_IN_HOLD = "com.droiddeck.launcher.SIGN_IN_HOLD"
|
||||
@@ -1735,8 +1784,10 @@ class SessionService : Service() {
|
||||
fun start(
|
||||
context: Context, mode: String = MODE_STEAM, program: String? = null,
|
||||
steamUi: String? = null, steamUrl: String? = null, programArgs: Array<String>? = null,
|
||||
displayId: Int = Display.DEFAULT_DISPLAY,
|
||||
) {
|
||||
val intent = Intent(context, SessionService::class.java).putExtra(EXTRA_MODE, mode)
|
||||
.putExtra(EXTRA_DISPLAY_ID, displayId)
|
||||
if (program != null) intent.putExtra(EXTRA_PROGRAM, program)
|
||||
if (programArgs != null) intent.putExtra(EXTRA_PROGRAM_ARGS, programArgs)
|
||||
if (steamUi != null) intent.putExtra(EXTRA_STEAM_UI, steamUi)
|
||||
@@ -1757,10 +1808,15 @@ class SessionService : Service() {
|
||||
context.startService(Intent(context, SessionService::class.java).setAction(ACTION_PIP_BEGIN))
|
||||
}
|
||||
|
||||
fun setActivityVisible(context: Context, visible: Boolean) {
|
||||
fun setActivityVisible(context: Context, visible: Boolean, displayId: Int = Display.DEFAULT_DISPLAY) {
|
||||
if (!SessionState.running) return
|
||||
val action = if (visible) ACTION_ACTIVITY_VISIBLE else ACTION_ACTIVITY_HIDDEN
|
||||
context.startService(Intent(context, SessionService::class.java).setAction(action))
|
||||
context.startService(Intent(context, SessionService::class.java).setAction(action).putExtra(EXTRA_DISPLAY_ID, displayId))
|
||||
}
|
||||
|
||||
fun setActivityDisplay(context: Context, displayId: Int) {
|
||||
if (!SessionState.running) return
|
||||
context.startService(Intent(context, SessionService::class.java).setAction(ACTION_ACTIVITY_DISPLAY).putExtra(EXTRA_DISPLAY_ID, displayId))
|
||||
}
|
||||
|
||||
fun setXrActivityVisible(context: Context, visible: Boolean) {
|
||||
|
||||
@@ -1,4 +1,10 @@
|
||||
package com.droiddeck.launcher.session
|
||||
|
||||
import android.view.Display
|
||||
|
||||
internal fun sessionHasVisibleSurface(panelVisible: Boolean, screenOn: Boolean, xrVisible: Boolean): Boolean =
|
||||
xrVisible || (panelVisible && screenOn)
|
||||
|
||||
internal fun sessionScreenOn(onDefaultDisplay: Boolean, interactive: Boolean, displayState: Int?): Boolean =
|
||||
if (onDefaultDisplay || displayState == null || displayState == Display.STATE_UNKNOWN) interactive
|
||||
else displayState == Display.STATE_ON || displayState == Display.STATE_VR || displayState == Display.STATE_ON_SUSPEND
|
||||
@@ -1,5 +1,6 @@
|
||||
package com.droiddeck.launcher.session
|
||||
|
||||
import android.view.Display
|
||||
import org.junit.Assert.assertFalse
|
||||
import org.junit.Assert.assertTrue
|
||||
import org.junit.Test
|
||||
@@ -20,6 +21,34 @@ class SessionVisibilityTest {
|
||||
assertFalse(sessionHasVisibleSurface(false, false, false))
|
||||
}
|
||||
|
||||
@Test fun phoneDisplayFollowsTheInteractiveState() {
|
||||
assertTrue(sessionScreenOn(true, true, null))
|
||||
assertFalse(sessionScreenOn(true, false, null))
|
||||
assertFalse(sessionScreenOn(true, false, Display.STATE_ON))
|
||||
}
|
||||
|
||||
@Test fun externalDisplayKeepsTheSessionAwakeWhileThePhoneScreenIsOff() {
|
||||
assertTrue(sessionScreenOn(false, false, Display.STATE_ON))
|
||||
assertTrue(sessionScreenOn(false, false, Display.STATE_ON_SUSPEND))
|
||||
assertTrue(sessionScreenOn(false, false, Display.STATE_VR))
|
||||
}
|
||||
|
||||
@Test fun externalDisplayTurnedOffSuspendsEvenWithThePhoneScreenOn() {
|
||||
assertFalse(sessionScreenOn(false, true, Display.STATE_OFF))
|
||||
assertFalse(sessionScreenOn(false, true, Display.STATE_DOZE))
|
||||
assertFalse(sessionScreenOn(false, true, Display.STATE_DOZE_SUSPEND))
|
||||
}
|
||||
|
||||
@Test fun missingExternalDisplayFallsBackToThePhone() {
|
||||
assertTrue(sessionScreenOn(false, true, null))
|
||||
assertFalse(sessionScreenOn(false, false, null))
|
||||
}
|
||||
|
||||
@Test fun unknownExternalDisplayStateFallsBackToThePhone() {
|
||||
assertTrue(sessionScreenOn(false, true, Display.STATE_UNKNOWN))
|
||||
assertFalse(sessionScreenOn(false, false, Display.STATE_UNKNOWN))
|
||||
}
|
||||
|
||||
@Test fun losingXrVisibilityReturnsToNormalSuspendRules() {
|
||||
assertTrue(sessionHasVisibleSurface(false, false, true))
|
||||
assertFalse(sessionHasVisibleSurface(false, false, false))
|
||||
|
||||
Reference in new issue
Block a user