From ed9f4eaac27a1b6e426d54d9eeff6cb898b5bc64 Mon Sep 17 00:00:00 2001 From: Adrian Castro <22133246+castdrian@users.noreply.github.com> Date: Sun, 23 Aug 2026 11:01:00 +0200 Subject: [PATCH] fix: refresh Thor diagnostics after display events --- .../java/dev/adrian/thortools/MainActivity.kt | 32 ++++++++++++++++--- .../MainActivityLifecycleContractTest.kt | 5 ++- 2 files changed, 31 insertions(+), 6 deletions(-) diff --git a/app/src/main/java/dev/adrian/thortools/MainActivity.kt b/app/src/main/java/dev/adrian/thortools/MainActivity.kt index 89ddb54..561770a 100644 --- a/app/src/main/java/dev/adrian/thortools/MainActivity.kt +++ b/app/src/main/java/dev/adrian/thortools/MainActivity.kt @@ -30,6 +30,7 @@ class MainActivity : ComponentActivity() { private var displayManager: DisplayManager? = null private var secondaryPresentation: ThorPresentation? = null private var secondaryDisplayRetry: Job? = null + private var displayRefreshJob: Job? = null private var secondaryDisplayRetryCount = 0 private var secondaryPresentationRequested = false private var activityResumed = false @@ -38,10 +39,12 @@ class MainActivity : ComponentActivity() { private val displayListener = object : DisplayManager.DisplayListener { override fun onDisplayAdded(displayId: Int) { + scheduleDisplayRefresh() requestSecondaryDisplay() } override fun onDisplayRemoved(displayId: Int) { + scheduleDisplayRefresh() if (secondaryPresentation?.display?.displayId == displayId) { dismissSecondaryDisplay() scheduleSecondaryDisplayRetry() @@ -49,6 +52,7 @@ class MainActivity : ComponentActivity() { } override fun onDisplayChanged(displayId: Int) { + scheduleDisplayRefresh() requestSecondaryDisplay() } } @@ -92,6 +96,8 @@ class MainActivity : ComponentActivity() { activityResumed = false secondaryDisplayRetry?.cancel() secondaryDisplayRetry = null + displayRefreshJob?.cancel() + displayRefreshJob = null super.onPause() } @@ -109,6 +115,8 @@ class MainActivity : ComponentActivity() { activityResumed = false secondaryDisplayRetry?.cancel() secondaryDisplayRetry = null + displayRefreshJob?.cancel() + displayRefreshJob = null secondaryDisplayRetryCount = 0 dismissSecondaryDisplay(clearRequest = true) displayManager?.unregisterDisplayListener(displayListener) @@ -130,11 +138,13 @@ class MainActivity : ComponentActivity() { .firstOrNull { candidate -> candidate.displayId != Display.DEFAULT_DISPLAY && candidate.modeOrNull()?.let { mode -> - DeviceProfile.isThorLowerDisplay( - mode.physicalWidth, - mode.physicalHeight, - candidate.rotation, - ) + candidate.rotationOrNull()?.let { rotation -> + DeviceProfile.isThorLowerDisplay( + mode.physicalWidth, + mode.physicalHeight, + rotation, + ) + } } == true } if (display == null) { @@ -176,6 +186,16 @@ class MainActivity : ComponentActivity() { } } + private fun scheduleDisplayRefresh() { + if (!sessionLoaded || !activityResumed || isFinishing) return + displayRefreshJob?.cancel() + displayRefreshJob = lifecycleScope.launch { + delay(250L) + displayRefreshJob = null + if (activityResumed && !isFinishing) session.refresh() + } + } + private fun scheduleSecondaryDisplayRetry() { if (!secondaryPresentationRequested || !activityResumed || isFinishing || secondaryDisplayRetry?.isActive == true) return secondaryDisplayRetryCount = (secondaryDisplayRetryCount + 1).coerceAtMost(8) @@ -201,6 +221,8 @@ class MainActivity : ComponentActivity() { private fun Display.modeOrNull(): Display.Mode? = runCatching { mode }.getOrNull() +private fun Display.rotationOrNull(): Int? = runCatching { rotation }.getOrNull() + private class ThorPresentation( private val activity: ComponentActivity, display: Display, diff --git a/app/src/test/java/dev/adrian/thortools/MainActivityLifecycleContractTest.kt b/app/src/test/java/dev/adrian/thortools/MainActivityLifecycleContractTest.kt index eb62aff..2c96ff5 100644 --- a/app/src/test/java/dev/adrian/thortools/MainActivityLifecycleContractTest.kt +++ b/app/src/test/java/dev/adrian/thortools/MainActivityLifecycleContractTest.kt @@ -15,13 +15,16 @@ class MainActivityLifecycleContractTest { val removed = source.substringAfter("override fun onDisplayRemoved(displayId: Int)").substringBefore("override fun onDisplayChanged") assertTrue(source.contains("private var secondaryPresentationRequested = false")) + assertTrue(source.contains("private var displayRefreshJob: Job? = null")) assertTrue(source.contains("secondaryPresentationRequested = true")) + assertTrue(source.contains("scheduleDisplayRefresh()")) assertTrue(show.contains("!secondaryPresentationRequested || !activityResumed || isFinishing")) assertTrue(show.contains("if (display == null)")) assertTrue(show.contains("scheduleSecondaryDisplayRetry()")) - assertTrue(show.contains("candidate.rotation")) + assertTrue(show.contains("candidate.rotationOrNull()")) assertTrue(show.contains("candidate.modeOrNull()")) assertTrue(source.contains("private fun Display.modeOrNull(): Display.Mode?")) + assertTrue(source.contains("private fun Display.rotationOrNull(): Int?")) assertFalse(show.contains("candidate.mode.physicalWidth")) assertTrue(removed.contains("scheduleSecondaryDisplayRetry()")) assertTrue(show.contains("catch (_: WindowManager.BadTokenException)"))