feat(android): add LAST_KNOWN eventMode config option - #78
Conversation
This adds an eventMode configuration option for Android to smooth out intermediate/jittering keyboard heights that are incorrectly emitted during WindowInsetsAnimation. Setting this to LAST_KNOWN ensures that listeners only receive the true/final keyboard height, resolving layout jump issues on Android 15/edge-to-edge configurations.
|
Hey Lincoln, thanks for looking into this! Your patch partially fixes the issue. It works correctly when the keyboard has no suggestion bar (or any other UI element rendered above the keyboard). However, with the Microsoft SwiftKey keyboard on a text input, the problem still occurs. Steps to reproduce:
The issue only appears in this specific sequence. Let me know if you need any more details or a screen recording. |
Hey, thanks for trying the patch -- I have a few questions. It would be helpful to have some more information/context:
Attach the Android debugger -- Check the logs from: https://github.com/ionic-team/capacitor-keyboard/pull/78/changes#diff-bdfde9ceeea23c25067c2b23ad24db3f4ac5c312a7fed3e23c8f7451c6ea387fR125 and provide some specifics for the expected values and timings.
Thank you! |
|
Hey, it works correctly in some scenarios, but I'm still seeing incorrect height values in others. Here's what I found: Issue 1: Incorrect height with Microsoft SwiftKey (initial show) Both keyboardWillShow and keyboardDidShow return an incorrect height when the input type is email or text. Input types number and decimal work correctly. Issue 2: Incorrect height on quick re-trigger (tap background → immediately refocus input) keyboardWillShow: reproducible on SwiftKey and Gboard, with input types email and text Screen recordings (demo app with a footer subscribed to keyboardHeight): keyboardWillShow: https://jumpshare.com/share/LYPiwVVupIq2VTIeYF2l Logs keyboardWillShow — SwiftKey, input type text: keyboardDidShow logs (gboard text input type): Please let me know if you need anything else |
…om corrupting keyboard ceiling
|
@kosserin PR updated. please try again! |
|
I've tried the update and here are my observations:
Screen recording of quick re-trigger with keyboardWillShow screen-20260824-222515-1787603090421.mp4Screen recording of switching between different input type modes screen-20260824-230402-1787605429128.mp4 |
…tion glitches * Extracts Android window inset math into a pure Java KeyboardHeightFilter state controller. * Implements a Map<Integer, Integer> to track the maximum known keyboard height independently for different screen widths (e.g., Portrait vs Landscape). * Fixes rapid-abort and SwiftKey double-fire glitches by capping spurious animation heights to the known ceiling. * Fixes rotation transition bugs where the OS fires showing=true with a 0px height. * Adds a comprehensive suite of pure JUnit tests to guarantee 100% coverage of all lifecycle and glitch scenarios. * Retains android.util.Log traces in Keyboard.java to assist with production debugging.
|
@kosserin Thanks for all of this, the logs and scenarios were incredibly helpful! I believe almost all of these edge cases should now be fully accounted for. I've just pushed a series of major updates to the patch: Rotation & Split-Screen Support: I completely rewrote the ceiling logic to use a Map grouped by screen width. This allows the plugin to natively adapt its heights when you rotate between Portrait and Landscape (or resize the app), while still aggressively suppressing the rapid-abort and SwiftKey double-fire glitches. The Android 13 Navigation Bar Gap: Interesting find on this! I did some digging and I don't think this is an OS bug, but an intended feature of Android 11+ WindowInsets. The OS should always return the absolute height from the bottom of the physical screen. However, if a Capacitor app isn't explicitly drawing Edge-to-Edge (resizeOnFullScreen = false), the webview stops above the Navigation Bar, meaning Capacitor was accidentally double-offsetting the web layout. I've added a fix that dynamically intercepts this and subtracts the Navigation Bar height, which should eliminate that empty gap. If you have time to pull the latest changes, let me know how it goes! |
…d height * Fixes a bug in Capacitor where absolute Android WindowInsets were incorrectly passed to the WebView when resizeOnFullScreen is false. * Adds calculateImeHeight to the KeyboardHeightFilter to translate absolute OS coordinates into relative WebView coordinates by subtracting the Navigation Bar height. * Resolves the 40-100px double-offset empty gap above the keyboard in standard (non edge-to-edge) Capacitor apps. * Adds testCalculateImeHeightAccountsForNavBar to guarantee 100% coverage of the math boundaries.
|
Hey Lincoln, thank you for taking time to fix this and read my feedback. I tested new changes and here's my feedback:
screen-20260827-212310-1787858578822.mp4
screen-20260827-215032-1787860207374.mp4Logs for 1 (fresh tap on input field -> background tap -> quick tap on input field) Logs for 2 (sometimes incorrect height fired on willShow and didShow on switching keyboards: Android to Gboard not working properly: SwiftKey to Gboard working good: Please let me know if you need anything else. Happy to help get this solved! |
When an OS rotation layout pass reports an invalid layout height that triggers the known-height clamp, it produces a transient event that will be corrected by another layout pass a few milliseconds later. This commit ensures we do not emit 'keyboardDidShow' to the Javascript layer for these transient glitches, preventing unnecessary DOM recalculations mid-rotation.
Automatically disables nav bar subtraction on Android 15+ devices where Edge-to-Edge is enforced by the OS. Exposes a new 'navigationBarInsets' config option to allow developers to manually override this behavior.
|
@kosserin Your continued testing is very helpful :) To be honest I don't know that we will get this perfect. Unfortunately with the OS sending incorrect values at various times, there is only so much we can do to correct this. Everything is a trade-off since this isn't "acting normally" at the Android level. Try the latest version and let me know if the remaining issues are resolved? Note the new configuration settings for |


Bug Report / Issue
Closes ionic-team/capacitor-plugins#2587
Description
On Android, the
@capacitor/keyboardplugin can report inconsistent or incorrect keyboard heights (sometimes smaller than the actual keyboard, or0) during keyboard animations or whenWindowInsetsare applied. The eventskeyboardWillShowandkeyboardDidShowmay fire with intermediate sizes instead of the final settled keyboard height, causing UI jumps or layout issues, particularly noticeable on newer Android versions (14/15) with edge-to-edge configurations.This is a root cause of the common "Grey bar above keyboard" issue reported in many other issues (such as ionic-team/capacitor#8525, ionic-team/capacitor#8575, ionic-team/capacitor#8329, and ionic-team/capacitor-plugins#2205).
This PR introduces an
eventMode: "LAST_KNOWN"configuration option for Android to smooth out the keyboard animation.What it does:
knownKeyboardHeightto avoid emitting intermediate sizes during theWindowInsetsAnimationCompatlifecycle (onPrepare,onProgress,onStart,onEnd).WILL_SHOWandDID_SHOWif the height actually changes, falling back to theLAST_KNOWNheight instead of transient smaller heights during animation.This change is backward compatible as the default
eventModeremains"DEFAULT".