Fix WebView system bar and keyboard insets
This commit is contained in:
@@ -13,10 +13,10 @@ android {
|
||||
|
||||
defaultConfig {
|
||||
applicationId = "monster.autisme.tsjetpiti"
|
||||
minSdk = 26
|
||||
minSdk = 30
|
||||
targetSdk = 35
|
||||
versionCode = 1
|
||||
versionName = "0.1.0"
|
||||
versionCode = 2
|
||||
versionName = "0.1.1"
|
||||
|
||||
// On-device LLM => 64-bit ARM only. Keeps the APK from ballooning.
|
||||
ndk {
|
||||
|
||||
@@ -7,6 +7,12 @@
|
||||
--bubble: #141414;
|
||||
--bubble-line: #242424;
|
||||
|
||||
/* Window insets (status/nav bar, cutout, keyboard) — set from Android. */
|
||||
--inset-top: 0px;
|
||||
--inset-right: 0px;
|
||||
--inset-bottom: 0px;
|
||||
--inset-left: 0px;
|
||||
|
||||
/* Generic, corporate/IT sans stack — resolves offline on any device. */
|
||||
--font: "Segoe UI", "Helvetica Neue", Helvetica, Arial, "Liberation Sans",
|
||||
system-ui, sans-serif;
|
||||
@@ -37,7 +43,8 @@ body {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
padding: max(env(safe-area-inset-top), 12px) 16px 12px;
|
||||
padding: calc(var(--inset-top) + 12px) calc(var(--inset-right) + 16px) 12px
|
||||
calc(var(--inset-left) + 16px);
|
||||
background: var(--panel);
|
||||
border-bottom: 1px solid var(--line);
|
||||
flex: 0 0 auto;
|
||||
@@ -64,7 +71,7 @@ body {
|
||||
#chat {
|
||||
flex: 1 1 auto;
|
||||
overflow-y: auto;
|
||||
padding: 18px 16px;
|
||||
padding: 18px calc(var(--inset-right) + 16px) 18px calc(var(--inset-left) + 16px);
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 12px;
|
||||
@@ -99,7 +106,8 @@ body {
|
||||
display: flex;
|
||||
gap: 10px;
|
||||
align-items: flex-end;
|
||||
padding: 12px 14px calc(env(safe-area-inset-bottom) + 12px);
|
||||
padding: 12px calc(var(--inset-right) + 14px) calc(var(--inset-bottom) + 12px)
|
||||
calc(var(--inset-left) + 14px);
|
||||
background: var(--panel);
|
||||
border-top: 1px solid var(--line);
|
||||
flex: 0 0 auto;
|
||||
|
||||
@@ -2,7 +2,9 @@ package monster.autisme.tsjetpiti
|
||||
|
||||
import android.annotation.SuppressLint
|
||||
import android.app.Activity
|
||||
import android.graphics.Color
|
||||
import android.os.Bundle
|
||||
import android.view.WindowInsets
|
||||
import android.webkit.JavascriptInterface
|
||||
import android.webkit.WebSettings
|
||||
import android.webkit.WebView
|
||||
@@ -32,6 +34,12 @@ class MainActivity : Activity() {
|
||||
@Volatile private var handle: Long = 0L
|
||||
@Volatile private var setupStarted = false
|
||||
|
||||
// Window insets (status/nav bar, cutout, keyboard) in CSS px, pushed to the page.
|
||||
private var insetTop = 0
|
||||
private var insetBottom = 0
|
||||
private var insetLeft = 0
|
||||
private var insetRight = 0
|
||||
|
||||
companion object {
|
||||
private const val N_CTX = 4096
|
||||
private const val MAX_TOKENS = 220
|
||||
@@ -55,6 +63,30 @@ class MainActivity : Activity() {
|
||||
webView = WebView(this)
|
||||
setContentView(webView)
|
||||
|
||||
// Edge-to-edge (default on Android 15+): draw behind the system bars, then
|
||||
// inset the WebView by the status bar, nav bar, display cutout, and IME so
|
||||
// none of them ever cover the chat UI (portrait or landscape).
|
||||
window.setDecorFitsSystemWindows(false)
|
||||
webView.setBackgroundColor(Color.BLACK)
|
||||
// WebView.setPadding doesn't inset HTML content (Chromium sizes 100dvh to the
|
||||
// full view), so feed the insets to CSS as variables instead. This also tracks
|
||||
// the IME: when the keyboard shows, bottom grows and the composer rises.
|
||||
webView.setOnApplyWindowInsetsListener { _, insets ->
|
||||
val bars = insets.getInsets(
|
||||
WindowInsets.Type.systemBars() or
|
||||
WindowInsets.Type.ime() or
|
||||
WindowInsets.Type.displayCutout()
|
||||
)
|
||||
val d = resources.displayMetrics.density
|
||||
insetTop = (bars.top / d).toInt()
|
||||
insetBottom = (bars.bottom / d).toInt()
|
||||
insetLeft = (bars.left / d).toInt()
|
||||
insetRight = (bars.right / d).toInt()
|
||||
pushInsets()
|
||||
insets
|
||||
}
|
||||
webView.requestApplyInsets()
|
||||
|
||||
webView.settings.apply {
|
||||
javaScriptEnabled = true
|
||||
domStorageEnabled = true
|
||||
@@ -63,6 +95,7 @@ class MainActivity : Activity() {
|
||||
webView.addJavascriptInterface(Bridge(), "TsjetNative")
|
||||
webView.webViewClient = object : WebViewClient() {
|
||||
override fun onPageFinished(view: WebView?, url: String?) {
|
||||
pushInsets() // apply insets now that the DOM exists
|
||||
if (!setupStarted) {
|
||||
setupStarted = true
|
||||
startSetup()
|
||||
@@ -80,6 +113,16 @@ class MainActivity : Activity() {
|
||||
}
|
||||
}
|
||||
|
||||
/** Push the current window insets to the page as CSS variables on :root. */
|
||||
private fun pushInsets() {
|
||||
val js = "var s=document.documentElement.style;" +
|
||||
"s.setProperty('--inset-top','${insetTop}px');" +
|
||||
"s.setProperty('--inset-bottom','${insetBottom}px');" +
|
||||
"s.setProperty('--inset-left','${insetLeft}px');" +
|
||||
"s.setProperty('--inset-right','${insetRight}px');"
|
||||
runOnUiThread { webView.evaluateJavascript(js, null) }
|
||||
}
|
||||
|
||||
private fun startSetup() = setupExecutor.execute {
|
||||
try {
|
||||
callJs("onStatus", "Waking the tsjet…")
|
||||
|
||||
Reference in New Issue
Block a user