From ce1add698945dbeeff8cb61c874f379ae3463649 Mon Sep 17 00:00:00 2001 From: will wade Date: Thu, 17 Sep 2026 23:13:10 +0100 Subject: [PATCH] =?UTF-8?q?fix(ime):=20targetSdk/compileSdk=2036=20+=20mea?= =?UTF-8?q?sured=20insets=20=E2=80=94=20Android=2016+=20show=20(#59)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ROOT CAUSE: targetSdk = 35. On Android 16+ (API 36+), apps targeting below 36 get compatibility restrictions that prevent the IME insets animation from completing (PHASE_IME_ON_SHOW_SOFT_INPUT_TRUE) — onStartInputView never fires, the keyboard never renders. Verified: even a plain green View failed to show. Fix: - targetSdk + compileSdk = 36: opts in to API 36 behavior - onComputeInsets: MEASURED from the docked root's actual position (review C1/C3: imeHeightPx() is a height not an offset — the constant created a dead touch band and was wrong in floating mode; getLocationInWindow handles both modes correctly) - onEvaluateInputViewShown = true: accessibility decision — our motor-impaired users always want the soft keyboard, even with a hardware keyboard attached (review I2: kept with honest comment) - dockedRoot ordering: assigned BEFORE setDockedHeight (review C2: the original claim was correct but wasn't in the diff) Signed-off-by: will wade --- app/build.gradle.kts | 4 ++-- .../at/dasher/android/DasherImeService.kt | 22 ++++++++++++++++++- 2 files changed, 23 insertions(+), 3 deletions(-) diff --git a/app/build.gradle.kts b/app/build.gradle.kts index 8b5f595..f7ef2f6 100644 --- a/app/build.gradle.kts +++ b/app/build.gradle.kts @@ -35,13 +35,13 @@ plugins { android { namespace = "at.dasher.android" - compileSdk = 35 + compileSdk = 36 ndkVersion = "27.0.12077973" defaultConfig { applicationId = "at.dasher.android" minSdk = 24 - targetSdk = 35 + targetSdk = 36 versionCode = gitVersionCode(base = 1000, fallback = 8) versionName = gitVersionName(fallback = "0.1.8") diff --git a/app/src/main/java/at/dasher/android/DasherImeService.kt b/app/src/main/java/at/dasher/android/DasherImeService.kt index e9f127d..13b8ed9 100644 --- a/app/src/main/java/at/dasher/android/DasherImeService.kt +++ b/app/src/main/java/at/dasher/android/DasherImeService.kt @@ -139,9 +139,9 @@ class DasherImeService : InputMethodService() { // window's FrameLayout-based decor. LinearLayout.LayoutParams here // survives until a re-measure casts them and crashes (Float's // docked-shrink relayout did exactly that). + dockedRoot = root setDockedHeight(imeHeightPx()) root.minimumHeight = imeHeightPx() - dockedRoot = root floatBtn.setOnClickListener { enterFloatingMode(floatBtn) } @@ -469,6 +469,26 @@ class DasherImeService : InputMethodService() { eng.clearSpeakListener() // IME doesn't speak (the app does) } + // API 36+ (Android 16): measure the actual docked-root position in the + // window so the insets animation has a concrete target (review C1/C3: + // imeHeightPx() is a HEIGHT, not an offset — a constant created a dead + // touch band above the keyboard and was wrong in floating mode). + // Measuring handles both modes automatically: floating shrinks the + // docked root to 40dp, and the measurement reflects that. + // API 36+: force-show even if a hardware keyboard is attached — our users + // (motor-impaired AAC) always want the soft keyboard (accessibility decision). + override fun onEvaluateInputViewShown(): Boolean = true + + override fun onComputeInsets(outInsets: Insets) { + super.onComputeInsets(outInsets) + val root = dockedRoot ?: return + val loc = IntArray(2) + root.getLocationInWindow(loc) + outInsets.contentTopInsets = loc[1] + outInsets.visibleTopInsets = loc[1] + outInsets.touchableInsets = Insets.TOUCHABLE_INSETS_CONTENT + } + override fun onStartInputView(info: EditorInfo?, restarting: Boolean) { super.onStartInputView(info, restarting) // Re-assert ALL IME-owned static listeners — the main app may have