From fc67d3d20314e7836e7ec35d0408f6c4fe7c4914 Mon Sep 17 00:00:00 2001 From: KaraZajac Date: Thu, 7 May 2026 23:52:57 -0400 Subject: [PATCH] =?UTF-8?q?v0.3.2=20=E2=80=94=20fix=20overlay=20crash=20on?= =?UTF-8?q?=20first=20show?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit v0.3.1 introduced TouchInterceptor (a FrameLayout wrapping the ComposeView) so we could intercept touches before the inner MapView saw ACTION_DOWN. That made the wrapper the window-root view (the View actually attached to WindowManager) but I left the ViewTreeLifecycleOwner / ViewTreeSavedStateRegistryOwner tags on the inner ComposeView. Compose's WindowRecomposer.create looks up findViewTreeLifecycleOwner on the window-root, not on the inner ComposeView. With the tag missing on the wrapper, Compose throws IllegalStateException at composition startup — service crash on overlay enable. Fix: move setViewTreeLifecycleOwner + setViewTreeSavedStateRegistryOwner to the wrapper. v0.3.0 worked because the ComposeView itself was the window-root then; the same pattern (owners on whatever's attached directly to WindowManager) holds here. --- app/build.gradle.kts | 4 ++-- .../org/soulstone/overwatch/service/OverlayManager.kt | 9 +++++++-- 2 files changed, 9 insertions(+), 4 deletions(-) diff --git a/app/build.gradle.kts b/app/build.gradle.kts index 119150f..badc49e 100644 --- a/app/build.gradle.kts +++ b/app/build.gradle.kts @@ -12,8 +12,8 @@ android { applicationId = "org.soulstone.overwatch" minSdk = 26 targetSdk = 35 - versionCode = 13 - versionName = "0.3.1" + versionCode = 14 + versionName = "0.3.2" } buildTypes { diff --git a/app/src/main/kotlin/org/soulstone/overwatch/service/OverlayManager.kt b/app/src/main/kotlin/org/soulstone/overwatch/service/OverlayManager.kt index 7918274..4227a11 100644 --- a/app/src/main/kotlin/org/soulstone/overwatch/service/OverlayManager.kt +++ b/app/src/main/kotlin/org/soulstone/overwatch/service/OverlayManager.kt @@ -92,12 +92,17 @@ class OverlayManager( val newOwner = OverlayOwner() val composeView = ComposeView(context).apply { - setViewTreeLifecycleOwner(newOwner) - setViewTreeSavedStateRegistryOwner(newOwner) setContent { OverlayBubble() } } // Wrap so we can intercept *before* MapView's own touch handling. + // Compose's WindowRecomposer reads findViewTreeLifecycleOwner from + // the *window-root* view (= the wrapper here, since it's what's + // attached to WindowManager). Setting the owner only on the inner + // ComposeView throws IllegalStateException at composition startup — + // that was the v0.3.1 crash. val wrapper = TouchInterceptor(context).apply { + setViewTreeLifecycleOwner(newOwner) + setViewTreeSavedStateRegistryOwner(newOwner) addView( composeView, FrameLayout.LayoutParams(