diff --git a/app/src/main/java/top/yeij/cyrene/service/CyreneVoiceInteractionSession.kt b/app/src/main/java/top/yeij/cyrene/service/CyreneVoiceInteractionSession.kt index aec24ce..52c51b7 100644 --- a/app/src/main/java/top/yeij/cyrene/service/CyreneVoiceInteractionSession.kt +++ b/app/src/main/java/top/yeij/cyrene/service/CyreneVoiceInteractionSession.kt @@ -60,19 +60,6 @@ class CyreneVoiceInteractionSession(context: Context) : RuntimeLog.general("overlay", "ViewModel unavailable, overlay static") } - // Configure window: prevent IME resize, don't cover status bar - try { - val method = VoiceInteractionSession::class.java.getDeclaredMethod("getWindow") - method.isAccessible = true - val w = method.invoke(this) as? android.view.Window - w?.apply { - setSoftInputMode(WindowManager.LayoutParams.SOFT_INPUT_ADJUST_NOTHING) - clearFlags(WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS) - clearFlags(WindowManager.LayoutParams.FLAG_TRANSLUCENT_NAVIGATION) - addFlags(WindowManager.LayoutParams.FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS) - } - } catch (_: Exception) { } - lifecycleRegistry.currentState = Lifecycle.State.CREATED val vm = overlayViewModel return ComposeView(context).apply { @@ -102,6 +89,9 @@ class CyreneVoiceInteractionSession(context: Context) : RuntimeLog.general("overlay", "onShow, vm=${overlayViewModel != null}") lifecycleRegistry.currentState = Lifecycle.State.STARTED + // Configure window: extend behind status bar, don't resize for IME + configureWindow() + val screenContent = CyreneAccessibilityService.getScreenContent() if (screenContent.isNotBlank()) { overlayViewModel?.sendScreenContext(screenContent) @@ -109,6 +99,20 @@ class CyreneVoiceInteractionSession(context: Context) : } } + private fun configureWindow() { + try { + val method = VoiceInteractionSession::class.java.getDeclaredMethod("getWindow") + method.isAccessible = true + val w = method.invoke(this) as? android.view.Window ?: return + w.addFlags(WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS) + w.addFlags(WindowManager.LayoutParams.FLAG_TRANSLUCENT_NAVIGATION) + w.setSoftInputMode(WindowManager.LayoutParams.SOFT_INPUT_ADJUST_NOTHING) + Log.d(TAG, "Window configured: translucent status/nav, adjust nothing for IME") + } catch (e: Exception) { + Log.w(TAG, "Failed to configure window: ${e.message}") + } + } + override fun onHide() { RuntimeLog.general("overlay", "onHide") lifecycleRegistry.currentState = Lifecycle.State.DESTROYED diff --git a/app/src/main/java/top/yeij/cyrene/ui/overlay/OverlayContent.kt b/app/src/main/java/top/yeij/cyrene/ui/overlay/OverlayContent.kt index 76da014..48cf520 100644 --- a/app/src/main/java/top/yeij/cyrene/ui/overlay/OverlayContent.kt +++ b/app/src/main/java/top/yeij/cyrene/ui/overlay/OverlayContent.kt @@ -18,11 +18,9 @@ import androidx.compose.foundation.layout.fillMaxHeight import androidx.compose.foundation.layout.fillMaxSize import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.foundation.layout.imePadding -import androidx.compose.foundation.layout.navigationBarsPadding import androidx.compose.foundation.layout.offset import androidx.compose.foundation.layout.padding import androidx.compose.foundation.layout.size -import androidx.compose.foundation.layout.statusBarsPadding import androidx.compose.foundation.layout.width import androidx.compose.foundation.lazy.LazyColumn import androidx.compose.foundation.lazy.items @@ -54,6 +52,8 @@ import androidx.compose.ui.Modifier import androidx.compose.ui.draw.clip import androidx.compose.ui.input.pointer.pointerInput import androidx.compose.ui.platform.LocalConfiguration +import androidx.compose.ui.platform.LocalContext +import androidx.compose.ui.platform.LocalDensity import androidx.compose.ui.text.style.TextAlign import androidx.compose.ui.unit.IntOffset import androidx.compose.ui.unit.dp @@ -131,6 +131,25 @@ fun OverlayContent( val configuration = LocalConfiguration.current val isLandscape = configuration.orientation == Configuration.ORIENTATION_LANDSCAPE + // Manual status bar height — Compose WindowInsets may not work in VoiceInteractionSession + val context = androidx.compose.ui.platform.LocalContext.current + val statusBarHeight = remember { + val resourceId = context.resources.getIdentifier("status_bar_height", "dimen", "android") + if (resourceId > 0) context.resources.getDimensionPixelSize(resourceId) else 0 + } + val statusBarPaddingDp = with(androidx.compose.ui.platform.LocalDensity.current) { + statusBarHeight.toDp() + } + + // Manual nav bar height + val navBarHeight = remember { + val resourceId = context.resources.getIdentifier("navigation_bar_height", "dimen", "android") + if (resourceId > 0) context.resources.getDimensionPixelSize(resourceId) else 0 + } + val navBarPaddingDp = with(androidx.compose.ui.platform.LocalDensity.current) { + navBarHeight.toDp() + } + LaunchedEffect(messages.size) { if (messages.isNotEmpty()) { listState.animateScrollToItem(messages.size - 1) @@ -152,8 +171,7 @@ fun OverlayContent( Box( modifier = Modifier .fillMaxSize() - .statusBarsPadding() - .navigationBarsPadding(), + .padding(top = statusBarPaddingDp, bottom = navBarPaddingDp), ) { if (isLandscape) { LandscapeContent(