keyboard: pre-R IME inset reports total bottom area to match iOS

On Android pre-R with a fixed nav bar stacked below the IME (e.g.
Huawei EMUI 12, 3-button nav), the prior formula
`systemWindowInsetBottom - stableInsetBottom` reported only the IME
proper and dropped the nav-bar strip. Consumers max-ing the value
against viewPadding.bottom under-shifted by exactly the nav-bar
height, so the composer's bottom edge ended up behind the IME's top
edge by ~stableInsetBottom.

iOS reports keyboard frames spanning all the way to the bottom of
the screen (covering the home-indicator area), so the cross-platform
consumer pattern `max(keyboard.height, viewPadding.bottom)` already
assumes that semantic. Pre-R now matches: when the IME is open,
publish the full `systemWindowInsetBottom` (nav bar + IME);
otherwise 0.

R+ branch untouched — `Type.ime().bottom` typically already includes
the nav-bar area on devices where the IME extends to the screen
edge. Left a TODO on the R+ path for HarmonyOS R+ with a fixed
nav bar; not currently testable.
This commit is contained in:
agra
2026-05-29 08:40:04 +03:00
parent dc47fc0159
commit 4fa46725a9

View File

@@ -151,13 +151,25 @@ class KeyboardPlugin : NativePlugin, MethodChannel.MethodCallHandler {
private fun publishImeHeight(view: android.view.View, insets: WindowInsets) {
val density = view.resources.displayMetrics.density
val imePx: Int = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) {
// TODO: on devices where a fixed nav bar stacks below the IME
// (e.g. HarmonyOS R+ with 3-button nav), Type.ime().bottom may
// exclude the nav-bar strip — same shape as the pre-R bug below.
// Verify when an R+ Huawei device is available; if confirmed,
// add Type.navigationBars() when the IME is visible.
insets.getInsets(WindowInsets.Type.ime()).bottom
} else {
// systemWindowInsetBottom = nav bar + IME; stableInsetBottom = nav
// bar only (stable insets exclude things that animate in/out like
// the IME). The difference isolates IME height.
// Report the total bottom-occluded area when the IME is open, so
// callers can max(keyboard.height, viewPadding.bottom) without
// adding the safe-area inset separately — matching iOS, where the
// reported keyboard frame already covers the home-indicator strip.
//
// systemWindowInsetBottom = nav bar + IME (when open) or just nav bar
// stableInsetBottom = nav bar (stable, doesn't animate in/out)
@Suppress("DEPRECATION")
(insets.systemWindowInsetBottom - insets.stableInsetBottom).coerceAtLeast(0)
val systemBottom = insets.systemWindowInsetBottom
@Suppress("DEPRECATION")
val stableBottom = insets.stableInsetBottom
if (systemBottom > stableBottom) systemBottom else 0
}
val height = imePx.toDouble() / density