Skip to content

feat(android): add LAST_KNOWN eventMode config option - #78

Open
lincolnthree wants to merge 8 commits into
ionic-team:mainfrom
lincolnthree:fix/android-keyboard-animation-heights
Open

lincolnthree wants to merge 8 commits into
ionic-team:mainfrom
lincolnthree:fix/android-keyboard-animation-heights

Conversation

@lincolnthree

@lincolnthree lincolnthree commented Aug 19, 2026 •

Copy link
Copy Markdown

Bug Report / Issue

Closes #80

Description

On Android, the @capacitor/keyboard plugin can report inconsistent or incorrect keyboard heights (sometimes smaller than the actual keyboard, or 0) during keyboard animations or when WindowInsets are applied. The events keyboardWillShow and keyboardDidShow may 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:

  1. Tracks the Highest Known Height: Tracks the knownKeyboardHeight to avoid emitting intermediate sizes during the WindowInsetsAnimationCompat lifecycle (onPrepare, onProgress, onStart, onEnd).
  2. Smooths Event Emission: Only emits WILL_SHOW and DID_SHOW if the height actually changes, falling back to the LAST_KNOWN height instead of transient smaller heights during animation.

This change is backward compatible as the default eventMode remains "DEFAULT".

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.
@kosserin

Copy link
Copy Markdown

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:

  1. Tap the input field
  2. Tap the background (to dismiss the keyboard)
  3. Quickly tap the input field again

The issue only appears in this specific sequence. Let me know if you need any more details or a screen recording.

@lincolnthree

Copy link
Copy Markdown
Author

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:

  1. Tap the input field
  2. Tap the background (to dismiss the keyboard)
  3. Quickly tap the input field again

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:

  1. You said it partially fixes the issue:
    1a) In what way was your issue fixed? Please describe in detail.
    1b) In what way was your issue NOT fixed? Please describe in detail.

  2. I don't have a Microsoft device. Could you please do some debugging and see where things are breaking down?

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.

  1. Yes, a video recording would be very helpful.

Thank you!

@kosserin

Copy link
Copy Markdown

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
keyboardDidShow: reproducible on SwiftKey with input types email and text, and on Gboard with all input types

Screen recordings (demo app with a footer subscribed to keyboardHeight):

keyboardWillShow: https://jumpshare.com/share/LYPiwVVupIq2VTIeYF2l
keyboardDidShow: https://jumpshare.com/share/0ybIv8eIKnssB9Czad1V

Logs

keyboardWillShow — SwiftKey, input type text:
08-24 16:50:05.881 32753 32753 D Keyboard: onApplyWindowInsets - showingKeyboard=true imeHeight=946 rawIme=946 nav=63 sdk=37
08-24 16:50:05.883 32753 32753 D Keyboard: onStart - showingKeyboard=true imeHeight=946 rawIme=946 nav=63 sdk=37
08-24 16:50:08.805 32753 32753 D Keyboard: onApplyWindowInsets - showingKeyboard=false imeHeight=0 rawIme=0 nav=63 sdk=37
08-24 16:50:08.807 32753 32753 D Keyboard: onStart - showingKeyboard=false imeHeight=0 rawIme=0 nav=63 sdk=37
08-24 16:50:09.111 32753 32753 D Keyboard: onApplyWindowInsets - showingKeyboard=false imeHeight=0 rawIme=0 nav=63 sdk=37
08-24 16:50:09.182 32753 32753 D Keyboard: onApplyWindowInsets - showingKeyboard=true imeHeight=714 rawIme=714 nav=63 sdk=37
08-24 16:50:09.184 32753 32753 D Keyboard: onStart - showingKeyboard=true imeHeight=714 rawIme=714 nav=63 sdk=37
08-24 16:50:09.485 32753 32753 D Keyboard: onApplyWindowInsets - showingKeyboard=true imeHeight=946 rawIme=946 nav=63 sdk=37

keyboardDidShow logs (gboard text input type):
08-24 17:13:49.730 6484 6484 D CapKeyboard: onApplyWindowInsets - showingKeyboard=true imeHeight=941 rawIme=941 nav=63 sdk=37
08-24 17:13:49.732 6484 6484 D CapKeyboard: onStart - showingKeyboard=true imeHeight=941 rawIme=941 nav=63 sdk=37
08-24 17:13:51.609 6484 6484 D CapKeyboard: onApplyWindowInsets - showingKeyboard=false imeHeight=0 rawIme=0 nav=63 sdk=37
08-24 17:13:51.611 6484 6484 D CapKeyboard: onStart - showingKeyboard=false imeHeight=0 rawIme=0 nav=63 sdk=37
08-24 17:13:51.908 6484 6484 D CapKeyboard: onApplyWindowInsets - showingKeyboard=false imeHeight=0 rawIme=0 nav=63 sdk=37
08-24 17:13:51.948 6484 6484 D CapKeyboard: onApplyWindowInsets - showingKeyboard=true imeHeight=1038 rawIme=1038 nav=63 sdk=37
08-24 17:13:51.950 6484 6484 D CapKeyboard: onStart - showingKeyboard=true imeHeight=1038 rawIme=1038 nav=63 sdk=37
08-24 17:13:52.251 6484 6484 D CapKeyboard: onApplyWindowInsets - showingKeyboard=true imeHeight=941 rawIme=941 nav=63 sdk=37

Please let me know if you need anything else

@lincolnthree

Copy link
Copy Markdown
Author

@kosserin PR updated. please try again!

@kosserin

Copy link
Copy Markdown

I've tried the update and here are my observations:

  • On first tap or after couple of seconds tap on any input, only keyboardWillShow fires. keyboardDidShow never fires in that scenario. It fires in other cases. Reproducible on all keyboards.
  • On quick re-trigger after background tap, keyboardWillShow fires twice. Reproducible only on Microsoft SwiftKey keyboard for input type text and email. Gboard input focus after background tap now is working properly for all input types.
  • Switching between different input types doesn't fire keyboardWillShow nor keyboardDidShow. Reproducible only on Microsoft SwiftKey keyboard (keyboard that has different heights for different input types e.g.316 is text, 272 is decimal)
  • No keyboard event is being fired on keyboard height update (suggestion bar show/hide). Reproducible with Microsoft SwiftKey Keyboard on text and email input type modes.
  • Switching between keyboards (Gboard/Microsoft SwiftKey) doesn't trigger keyboardWillShow if keyboard height of that new keyboard is bigger than current.
  • On Android 13 there is a gap between keyboard and footer in value that is equal to navigation bar's height. Not sure if it's related to keyboard and if it should be solved by this plugin.

Attachments:
Screenshot_20260824_230641_ionic-tabs-app
Screenshot_20260824_230836_ionic-tabs-app

Screen recording of quick re-trigger with keyboardWillShow

screen-20260824-222515-1787603090421.mp4

Screen 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.
@lincolnthree

lincolnthree commented Aug 26, 2026 •

Copy link
Copy Markdown
Author

@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.
@kosserin

Copy link
Copy Markdown

Hey Lincoln, thank you for taking time to fix this and read my feedback.

I tested new changes and here's my feedback:

  • You fixed issue with navbar height on older androids (Android 13 in my case) but you messed up newer androids (see screenshot provided from my Android 16: https://ibb.co/4nVs6qMB)
  • All keyboards on switch between different inputs types works great
  • Gboard keyboard (keyboard with fixed height) works great in all scenarios
  • SwiftKey (keyboard with suggestion bar that seems to be on top of keyboard layout) triggers double keyboardWillShow events on quick retrigger after background tap (screen recording and logs provided down below)
  • keyboardDidShow for all keyboards trigger once and correct. Only issue is switching between keyboards (see next line)
  • Sometimes when switching keyboards (Gboard, SwiftKey, etc.) fires incorrect values both for keyboardWillShow and keyboardDidShow. Might be related to new keyboard height is bigger than last or vice-versa (screen recording and logs provided down below).
  1. SwiftKey double keyboardWillShow on quick retrigger (on start incorrect value and on end correct one)
screen-20260827-212310-1787858578822.mp4
  1. Switching keyboards sometimes fire incorrect values for both keyboardWillShow and keyboardDidShow
screen-20260827-215032-1787860207374.mp4

Logs for 1 (fresh tap on input field -> background tap -> quick tap on input field)
08-27 21:42:35.960 1622 1622 I Capacitor/Keyboard: onPrepareAnimation
08-27 21:42:35.963 1622 1622 I Capacitor/Keyboard: onApplyWindowInsets: showing=true rawHeight=316 emit=0 shouldEmit=false
08-27 21:42:35.964 1622 1622 I Capacitor/Keyboard: onStart: showing=true rawHeight=316 emit=316 shouldEmit=true
08-27 21:42:36.262 1622 1622 I Capacitor/Keyboard: onEnd: showing=true rawHeight=316 emit=316 shouldEmit=true
08-27 21:42:36.870 1622 1622 I Capacitor/Keyboard: onPrepareAnimation
08-27 21:42:36.878 1622 1622 I Capacitor/Keyboard: onApplyWindowInsets: showing=false rawHeight=0 emit=0 shouldEmit=false
08-27 21:42:36.878 1622 1622 I Capacitor/Keyboard: onStart: showing=false rawHeight=0 emit=0 shouldEmit=true
08-27 21:42:37.173 1622 1622 I Capacitor/Keyboard: onEnd: showing=false rawHeight=0 emit=0 shouldEmit=true
08-27 21:42:37.175 1622 1622 I Capacitor/Keyboard: onApplyWindowInsets: showing=false rawHeight=0 emit=0 shouldEmit=false
08-27 21:42:37.358 1622 1622 I Capacitor/Keyboard: onPrepareAnimation
08-27 21:42:37.375 1622 1622 I Capacitor/Keyboard: onApplyWindowInsets: showing=true rawHeight=272 emit=0 shouldEmit=false
08-27 21:42:37.376 1622 1622 I Capacitor/Keyboard: onStart: showing=true rawHeight=272 emit=272 shouldEmit=true
08-27 21:42:37.674 1622 1622 I Capacitor/Keyboard: onEnd: showing=true rawHeight=272 emit=272 shouldEmit=true
08-27 21:42:37.676 1622 1622 I Capacitor/Keyboard: onApplyWindowInsets: showing=true rawHeight=316 emit=316 shouldEmit=true

Logs for 2 (sometimes incorrect height fired on willShow and didShow on switching keyboards:
SwiftKey to Gboard not working properly:
08-27 21:52:57.076 3342 3342 I Capacitor/Keyboard: onApplyWindowInsets: showing=false rawHeight=0 emit=0 shouldEmit=false
08-27 21:52:57.285 3342 3342 I Capacitor/Keyboard: onPrepareAnimation
08-27 21:52:57.293 3342 3342 I Capacitor/Keyboard: onApplyWindowInsets: showing=true rawHeight=358 emit=0 shouldEmit=false
08-27 21:52:57.294 3342 3342 I Capacitor/Keyboard: onStart: showing=true rawHeight=358 emit=316 shouldEmit=true
08-27 21:52:57.595 3342 3342 I Capacitor/Keyboard: onEnd: showing=true rawHeight=358 emit=316 shouldEmit=true

Android to Gboard not working properly:
08-27 21:56:20.093 3342 3342 I Capacitor/Keyboard: onPrepareAnimation
08-27 21:56:20.104 3342 3342 I Capacitor/Keyboard: onApplyWindowInsets: showing=true rawHeight=358 emit=0 shouldEmit=false
08-27 21:56:20.105 3342 3342 I Capacitor/Keyboard: onStart: showing=true rawHeight=358 emit=343 shouldEmit=true
08-27 21:56:20.407 3342 3342 I Capacitor/Keyboard: onEnd: showing=true rawHeight=358 emit=343 shouldEmit=true

SwiftKey to Gboard working good:
08-27 22:04:24.679 3342 3342 I Capacitor/Keyboard: onApplyWindowInsets: showing=false rawHeight=0 emit=0 shouldEmit=false
08-27 22:04:24.894 3342 3342 I Capacitor/Keyboard: onPrepareAnimation
08-27 22:04:24.904 3342 3342 I Capacitor/Keyboard: onApplyWindowInsets: showing=true rawHeight=316 emit=0 shouldEmit=false
08-27 22:04:24.905 3342 3342 I Capacitor/Keyboard: onStart: showing=true rawHeight=316 emit=316 shouldEmit=true
08-27 22:04:25.207 3342 3342 I Capacitor/Keyboard: onEnd: showing=true rawHeight=316 emit=316 shouldEmit=true

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.
@lincolnthree

Copy link
Copy Markdown
Author

@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 navigationBarInsets: https://github.com/ionic-team/capacitor-keyboard/pull/78/changes#diff-f1f3c6677661c21128a30f64358c6615c6801043e1cbfcc904302422a17b3246R66

@lincolnthree

Copy link
Copy Markdown
Author

@kosserin Have you had a chance to test the latest? I am interested if your remaining issues are fixed.

@kosserin

kosserin commented Sep 1, 2026

Copy link
Copy Markdown

Hey Lincoln, sorry for taking some time to respond. I was quite busy with some other tasks that were not related to Capacitor.
Thank you for taking time to look at this issue. I really appreciate it and I agree that we most likely can't make it perfect. It's on android side.
Your solution has following issues:

  1. Issue with increasing height by toggling (SwiftKey). First time it does it correctly, but after it nothing happens (only onApplyWindowInsets gets triggered but no event triggered).
    Screen recordings:
screen-20260901-192558-1788283544057.mp4.2.mp4

Logs:
09-01 19:25:44.557 31560 31560 I Capacitor/Keyboard: onPrepareAnimation
09-01 19:25:44.560 31560 31560 I Capacitor/Keyboard: onApplyWindowInsets: showing=true rawHeight=360 emit=0 shouldEmit=false key=1080x2424|com.touchtype.swiftkey/com.touchtype.KeyboardService
09-01 19:25:44.561 31560 31560 I Capacitor/Keyboard: onStart: showing=true rawHeight=360 emit=360 shouldEmit=true key=1080x2424|com.touchtype.swiftkey/com.touchtype.KeyboardService
09-01 19:25:44.863 31560 31560 I Capacitor/Keyboard: onEnd: showing=true rawHeight=360 emit=360 shouldEmit=true key=1080x2424|com.touchtype.swiftkey/com.touchtype.KeyboardService
09-01 19:25:46.356 31560 31560 I Capacitor/Keyboard: onApplyWindowInsets: showing=true rawHeight=316 emit=316 shouldEmit=true key=1080x2424|com.touchtype.swiftkey/com.touchtype.KeyboardService
09-01 19:25:47.299 31560 31560 I Capacitor/Keyboard: onApplyWindowInsets: showing=true rawHeight=360 emit=316 shouldEmit=false key=1080x2424|com.touchtype.swiftkey/com.touchtype.KeyboardService
09-01 19:25:48.360 31560 31560 I Capacitor/Keyboard: onApplyWindowInsets: showing=true rawHeight=316 emit=316 shouldEmit=false key=1080x2424|com.touchtype.swiftkey/com.touchtype.KeyboardService
09-01 19:25:49.062 31560 31560 I Capacitor/Keyboard: onApplyWindowInsets: showing=true rawHeight=360 emit=316 shouldEmit=false key=1080x2424|com.touchtype.swiftkey/com.touchtype.KeyboardService
09-01 19:25:49.848 31560 31560 I Capacitor/Keyboard: onApplyWindowInsets: showing=true rawHeight=316 emit=316 shouldEmit=false key=1080x2424|com.touchtype.swiftkey/com.touchtype.KeyboardService

Switching between different height (different input types) triggers willShow update only once. After it, no events being triggered.
Screen recording:

screen-20260901-192814-1788283685056.mp4

Logs:
09-01 19:28:06.653 31560 31560 I Capacitor/Keyboard: onPrepareAnimation
09-01 19:28:06.657 31560 31560 I Capacitor/Keyboard: onApplyWindowInsets: showing=true rawHeight=316 emit=0 shouldEmit=false key=1080x2424|com.touchtype.swiftkey/com.touchtype.KeyboardService
09-01 19:28:06.658 31560 31560 I Capacitor/Keyboard: onStart: showing=true rawHeight=316 emit=316 shouldEmit=true key=1080x2424|com.touchtype.swiftkey/com.touchtype.KeyboardService
09-01 19:28:06.959 31560 31560 I Capacitor/Keyboard: onEnd: showing=true rawHeight=316 emit=316 shouldEmit=true key=1080x2424|com.touchtype.swiftkey/com.touchtype.KeyboardService
09-01 19:28:07.710 31560 31560 I Capacitor/Keyboard: onApplyWindowInsets: showing=true rawHeight=272 emit=272 shouldEmit=true key=1080x2424|com.touchtype.swiftkey/com.touchtype.KeyboardService
09-01 19:28:08.587 31560 31560 I Capacitor/Keyboard: onApplyWindowInsets: showing=true rawHeight=316 emit=272 shouldEmit=false key=1080x2424|com.touchtype.swiftkey/com.touchtype.KeyboardService
09-01 19:28:11.034 31560 31560 I Capacitor/Keyboard: onApplyWindowInsets: showing=true rawHeight=272 emit=272 shouldEmit=false key=1080x2424|com.touchtype.swiftkey/com.touchtype.KeyboardService
09-01 19:28:11.920 31560 31560 I Capacitor/Keyboard: onApplyWindowInsets: showing=true rawHeight=316 emit=272 shouldEmit=false key=1080x2424|com.touchtype.swiftkey/com.touchtype.KeyboardService

Focusing input with bigger keyboard layout and closing (no matter if quick or not) and then focusing input with smaller keyboard layout causes update to not trigger.
Screen recording:

screen-20260901-192959-1788283791565.mp4

Logs:
09-01 19:30:13.157 484 484 I Capacitor/Keyboard: onPrepareAnimation
09-01 19:30:13.162 484 484 I Capacitor/Keyboard: onApplyWindowInsets: showing=true rawHeight=316 emit=0 shouldEmit=false key=1080x2424|com.touchtype.swiftkey/com.touchtype.KeyboardService
09-01 19:30:13.164 484 484 I Capacitor/Keyboard: onStart: showing=true rawHeight=316 emit=316 shouldEmit=true key=1080x2424|com.touchtype.swiftkey/com.touchtype.KeyboardService
09-01 19:30:13.462 484 484 I Capacitor/Keyboard: onEnd: showing=true rawHeight=316 emit=316 shouldEmit=true key=1080x2424|com.touchtype.swiftkey/com.touchtype.KeyboardService
09-01 19:30:15.080 484 484 I Capacitor/Keyboard: onPrepareAnimation
09-01 19:30:15.086 484 484 I Capacitor/Keyboard: onApplyWindowInsets: showing=false rawHeight=0 emit=0 shouldEmit=false key=1080x2424|com.touchtype.swiftkey/com.touchtype.KeyboardService
09-01 19:30:15.087 484 484 I Capacitor/Keyboard: onStart: showing=false rawHeight=0 emit=0 shouldEmit=true key=1080x2424|com.touchtype.swiftkey/com.touchtype.KeyboardService
09-01 19:30:15.375 484 484 I Capacitor/Keyboard: onEnd: showing=false rawHeight=0 emit=0 shouldEmit=true key=1080x2424|com.touchtype.swiftkey/com.touchtype.KeyboardService
09-01 19:30:15.376 484 484 I Capacitor/Keyboard: onApplyWindowInsets: showing=false rawHeight=0 emit=0 shouldEmit=false key=1080x2424|com.touchtype.swiftkey/com.touchtype.KeyboardService
09-01 19:30:15.605 484 484 I Capacitor/Keyboard: onPrepareAnimation
09-01 19:30:15.610 484 484 I Capacitor/Keyboard: onApplyWindowInsets: showing=true rawHeight=272 emit=0 shouldEmit=false key=1080x2424|com.touchtype.swiftkey/com.touchtype.KeyboardService
09-01 19:30:15.612 484 484 I Capacitor/Keyboard: onStart: showing=true rawHeight=272 emit=316 shouldEmit=true key=1080x2424|com.touchtype.swiftkey/com.touchtype.KeyboardService
09-01 19:30:15.920 484 484 I Capacitor/Keyboard: onEnd: showing=true rawHeight=272 emit=272 shouldEmit=true key=1080x2424|com.touchtype.swiftkey/com.touchtype.KeyboardService
09-01 19:30:18.030 484 484 I Capacitor/Keyboard: onPrepareAnimation
09-01 19:30:18.034 484 484 I Capacitor/Keyboard: onApplyWindowInsets: showing=false rawHeight=0 emit=0 shouldEmit=false key=1080x2424|com.touchtype.swiftkey/com.touchtype.KeyboardService
09-01 19:30:18.035 484 484 I Capacitor/Keyboard: onStart: showing=false rawHeight=0 emit=0 shouldEmit=true key=1080x2424|com.touchtype.swiftkey/com.touchtype.KeyboardService
09-01 19:30:18.332 484 484 I Capacitor/Keyboard: onEnd: showing=false rawHeight=0 emit=0 shouldEmit=true key=1080x2424|com.touchtype.swiftkey/com.touchtype.KeyboardService
09-01 19:30:18.509 484 484 I Capacitor/Keyboard: onPrepareAnimation
09-01 19:30:18.517 484 484 I Capacitor/Keyboard: onApplyWindowInsets: showing=true rawHeight=272 emit=0 shouldEmit=false key=1080x2424|com.touchtype.swiftkey/com.touchtype.KeyboardService
09-01 19:30:18.518 484 484 I Capacitor/Keyboard: onStart: showing=true rawHeight=272 emit=272 shouldEmit=true key=1080x2424|com.touchtype.swiftkey/com.touchtype.KeyboardService
09-01 19:30:18.818 484 484 I Capacitor/Keyboard: onEnd: showing=true rawHeight=272 emit=272 shouldEmit=true key=1080x2424|com.touchtype.swiftkey/com.touchtype.KeyboardService
09-01 19:30:19.652 484 484 I Capacitor/Keyboard: onPrepareAnimation
09-01 19:30:19.657 484 484 I Capacitor/Keyboard: onApplyWindowInsets: showing=false rawHeight=0 emit=0 shouldEmit=false key=1080x2424|com.touchtype.swiftkey/com.touchtype.KeyboardService
09-01 19:30:19.658 484 484 I Capacitor/Keyboard: onStart: showing=false rawHeight=0 emit=0 shouldEmit=true key=1080x2424|com.touchtype.swiftkey/com.touchtype.KeyboardService
09-01 19:30:19.953 484 484 I Capacitor/Keyboard: onEnd: showing=false rawHeight=0 emit=0 shouldEmit=true key=1080x2424|com.touchtype.swiftkey/com.touchtype.KeyboardService
09-01 19:30:20.149 484 484 I Capacitor/Keyboard: onPrepareAnimation
09-01 19:30:20.152 484 484 I Capacitor/Keyboard: onApplyWindowInsets: showing=true rawHeight=272 emit=0 shouldEmit=false key=1080x2424|com.touchtype.swiftkey/com.touchtype.KeyboardService
09-01 19:30:20.153 484 484 I Capacitor/Keyboard: onStart: showing=true rawHeight=272 emit=272 shouldEmit=true key=1080x2424|com.touchtype.swiftkey/com.touchtype.KeyboardService
09-01 19:30:20.456 484 484 I Capacitor/Keyboard: onEnd: showing=true rawHeight=272 emit=272 shouldEmit=true key=1080x2424|com.touchtype.swiftkey/com.touchtype.KeyboardService
09-01 19:30:20.458 484 484 I Capacitor/Keyboard: onApplyWindowInsets: showing=true rawHeight=316 emit=316 shouldEmit=true key=1080x2424|com.touchtype.swiftkey/com.touchtype.KeyboardService
09-01 19:30:21.742 484 484 I Capacitor/Keyboard: onPrepareAnimation
09-01 19:30:21.747 484 484 I Capacitor/Keyboard: onApplyWindowInsets: showing=false rawHeight=0 emit=0 shouldEmit=false key=1080x2424|com.touchtype.swiftkey/com.touchtype.KeyboardService
09-01 19:30:21.748 484 484 I Capacitor/Keyboard: onStart: showing=false rawHeight=0 emit=0 shouldEmit=true key=1080x2424|com.touchtype.swiftkey/com.touchtype.KeyboardService
09-01 19:30:22.044 484 484 I Capacitor/Keyboard: onEnd: showing=false rawHeight=0 emit=0 shouldEmit=true key=1080x2424|com.touchtype.swiftkey/com.touchtype.KeyboardService
09-01 19:30:22.046 484 484 I Capacitor/Keyboard: onApplyWindowInsets: showing=false rawHeight=0 emit=0 shouldEmit=false key=1080x2424|com.touchtype.swiftkey/com.touchtype.KeyboardService
09-01 19:30:22.108 484 484 I Capacitor/Keyboard: onPrepareAnimation
09-01 19:30:22.124 484 484 I Capacitor/Keyboard: onApplyWindowInsets: showing=true rawHeight=272 emit=0 shouldEmit=false key=1080x2424|com.touchtype.swiftkey/com.touchtype.KeyboardService
09-01 19:30:22.124 484 484 I Capacitor/Keyboard: onStart: showing=true rawHeight=272 emit=316 shouldEmit=true key=1080x2424|com.touchtype.swiftkey/com.touchtype.KeyboardService
09-01 19:30:22.428 484 484 I Capacitor/Keyboard: onEnd: showing=true rawHeight=272 emit=272 shouldEmit=true key=1080x2424|com.touchtype.swiftkey/com.touchtype.KeyboardService

Focusing input with smaller keyboard layout and then focusing (with delay of 1-2s) input with bigger keyboard layout causes no willShow event to be fired.
Screen recording:

screen-20260901-193320-1788283992391.mp4

Logs:
09-01 19:33:12.805 1281 1281 I Capacitor/Keyboard: onPrepareAnimation
09-01 19:33:12.808 1281 1281 I Capacitor/Keyboard: onApplyWindowInsets: showing=true rawHeight=272 emit=0 shouldEmit=false key=1080x2424|com.touchtype.swiftkey/com.touchtype.KeyboardService
09-01 19:33:12.809 1281 1281 I Capacitor/Keyboard: onStart: showing=true rawHeight=272 emit=272 shouldEmit=true key=1080x2424|com.touchtype.swiftkey/com.touchtype.KeyboardService
09-01 19:33:13.109 1281 1281 I Capacitor/Keyboard: onEnd: showing=true rawHeight=272 emit=272 shouldEmit=true key=1080x2424|com.touchtype.swiftkey/com.touchtype.KeyboardService
09-01 19:33:14.458 1281 1281 I Capacitor/Keyboard: onPrepareAnimation
09-01 19:33:14.463 1281 1281 I Capacitor/Keyboard: onApplyWindowInsets: showing=false rawHeight=0 emit=0 shouldEmit=false key=1080x2424|com.touchtype.swiftkey/com.touchtype.KeyboardService
09-01 19:33:14.464 1281 1281 I Capacitor/Keyboard: onStart: showing=false rawHeight=0 emit=0 shouldEmit=true key=1080x2424|com.touchtype.swiftkey/com.touchtype.KeyboardService
09-01 19:33:14.758 1281 1281 I Capacitor/Keyboard: onEnd: showing=false rawHeight=0 emit=0 shouldEmit=true key=1080x2424|com.touchtype.swiftkey/com.touchtype.KeyboardService
09-01 19:33:16.841 1281 1281 I Capacitor/Keyboard: onPrepareAnimation
09-01 19:33:16.849 1281 1281 I Capacitor/Keyboard: onApplyWindowInsets: showing=true rawHeight=316 emit=0 shouldEmit=false key=1080x2424|com.touchtype.swiftkey/com.touchtype.KeyboardService
09-01 19:33:16.850 1281 1281 I Capacitor/Keyboard: onStart: showing=true rawHeight=316 emit=272 shouldEmit=true key=1080x2424|com.touchtype.swiftkey/com.touchtype.KeyboardService
09-01 19:33:17.149 1281 1281 I Capacitor/Keyboard: onEnd: showing=true rawHeight=316 emit=316 shouldEmit=true key=1080x2424|com.touchtype.swiftkey/com.touchtype.KeyboardService

Focusing input with smaller keyboard layout (e.g input type number) and closing keyboard and quickly press on bigger keyboard layout (e.g. input type text) it fires willShow two times and at the end updates correctly but still not smooth
Screen recording:
screen-20260901-193432-1788284062483
Logs:
09-01 19:40:24.184 1281 1281 I Capacitor/Keyboard: onPrepareAnimation
09-01 19:40:24.190 1281 1281 I Capacitor/Keyboard: onApplyWindowInsets: showing=true rawHeight=272 emit=0 shouldEmit=false key=1080x2424|com.touchtype.swiftkey/com.touchtype.KeyboardService
09-01 19:40:24.191 1281 1281 I Capacitor/Keyboard: onStart: showing=true rawHeight=272 emit=272 shouldEmit=true key=1080x2424|com.touchtype.swiftkey/com.touchtype.KeyboardService
09-01 19:40:24.492 1281 1281 I Capacitor/Keyboard: onEnd: showing=true rawHeight=272 emit=272 shouldEmit=true key=1080x2424|com.touchtype.swiftkey/com.touchtype.KeyboardService
09-01 19:40:24.997 1281 1281 I Capacitor/Keyboard: onPrepareAnimation
09-01 19:40:25.001 1281 1281 I Capacitor/Keyboard: onApplyWindowInsets: showing=false rawHeight=0 emit=0 shouldEmit=false key=1080x2424|com.touchtype.swiftkey/com.touchtype.KeyboardService
09-01 19:40:25.002 1281 1281 I Capacitor/Keyboard: onStart: showing=false rawHeight=0 emit=0 shouldEmit=true key=1080x2424|com.touchtype.swiftkey/com.touchtype.KeyboardService
09-01 19:40:25.295 1281 1281 I Capacitor/Keyboard: onEnd: showing=false rawHeight=0 emit=0 shouldEmit=true key=1080x2424|com.touchtype.swiftkey/com.touchtype.KeyboardService
09-01 19:40:25.361 1281 1281 I Capacitor/Keyboard: onPrepareAnimation
09-01 19:40:25.365 1281 1281 I Capacitor/Keyboard: onApplyWindowInsets: showing=true rawHeight=272 emit=0 shouldEmit=false key=1080x2424|com.touchtype.swiftkey/com.touchtype.KeyboardService
09-01 19:40:25.366 1281 1281 I Capacitor/Keyboard: onStart: showing=true rawHeight=272 emit=272 shouldEmit=true key=1080x2424|com.touchtype.swiftkey/com.touchtype.KeyboardService
09-01 19:40:25.662 1281 1281 I Capacitor/Keyboard: onEnd: showing=true rawHeight=272 emit=272 shouldEmit=true key=1080x2424|com.touchtype.swiftkey/com.touchtype.KeyboardService
09-01 19:40:25.664 1281 1281 I Capacitor/Keyboard: onApplyWindowInsets: showing=true rawHeight=316 emit=316 shouldEmit=true key=1080x2424|com.touchtype.swiftkey/com.touchtype.KeyboardService

@kosserin

kosserin commented Sep 1, 2026

Copy link
Copy Markdown

With the help of AI and your current solution, I managed to get it almost perfect.

On this repo on keyboard-input-context branch I patched your solution and it is almost working great. It is working in all cases except when I 3+ times really quickly re-trigger input, then footer jumps more than it should (keyboardWillShow fires bigger height) and then resizes accordingly).
https://github.com/kosserin/android-capacitor-keyboard-issue/tree/keyboard-input-context

Sorry for not providing you with forked version of your branch, I don't have time atm to do so and I wanted to inform you about what I managed to get.

Thank you for your time and looking forward on your feedback!

@lincolnthree

Copy link
Copy Markdown
Author

Thank you! No worries. This is a really tricky problem. It's just the one commit? kosserin/android-capacitor-keyboard-issue@1f3a416

Taking a look now.

With the help of AI and your current solution, I managed to get it almost perfect.

On this repo on keyboard-input-context branch I patched your solution and it is almost working great. It is working in all cases except when I 3+ times really quickly re-trigger input, then footer jumps more than it should (keyboardWillShow fires bigger height) and then resizes accordingly). https://github.com/kosserin/android-capacitor-keyboard-issue/tree/keyboard-input-context

Sorry for not providing you with forked version of your branch, I don't have time atm to do so and I wanted to inform you about what I managed to get.

Thank you for your time and looking forward on your feedback!

@lincolnthree

Copy link
Copy Markdown
Author

Wow, ok. So it looks like you went a step farther and provided JS/web code to assist in identifying the input type?

@lincolnthree

Copy link
Copy Markdown
Author

I thought about that but was hesitant to do things on the JS side due to developer burden/inconsistency, but using focusin is a good idea.

…IMEs (e.g. SwiftKey)

Includes automated focusin interception in JS bridge to relay input context to native. The JS listener is hardened to strictly penetrate ShadowDOM and deduplicate bridge traffic.
@lincolnthree

Copy link
Copy Markdown
Author

@kosserin This is really good. Thank you (and Claude). I've adapted the parts of your patch to the PR and made a few improvements:

The focusin listener is now built directly into the plugin's JS wrapper (src/index.ts), so a custom Angular keyboard.service.ts is no longer required. (It also uses composedPath to support ShadowDOM elements like Ionic inputs).
The listener deduplicates input types to minimize traffic over the JS/native bridge.
I added unit tests for the new Java code and the pendingHeightCheck runnable to lock in the behavior.

Try out the new patch and you should be able to delete your keyboard service now! We're getting there!

@lincolnthree

Copy link
Copy Markdown
Author

PS. Really good idea to use the runnable to query the OS for the height as a sanity check. I love that.

@kosserin

kosserin commented Sep 2, 2026

Copy link
Copy Markdown

Hey Lincoln,

I just checked quickly your changes. Everything works great! This is the smoothest one so far! Didn't find any issue yet.

I will play around with it and write back to you in a day or max two whether I've found some issues and I hope and believe it stays that.

@lincolnthree

Copy link
Copy Markdown
Author

YES. That's what I like to hear. Thank you for finding the missing piece of the puzzle.

@kosserin

kosserin commented Sep 5, 2026 •

Copy link
Copy Markdown

Hey Lincoln,

The only issue I have found is jump of footer by the value of ion-safe-area-bottom on keyboardWillHide if ion-footer contains ion-safe-area-bottom:

  • When keyboad is about to appear, ion-safe-area-bottom value is 0 and gets value updated back to nav bar height on keyboardWillHide. If ion-footer has any value that contains --ion-safe-area-bottom (e.g. padding-bottom: var(--ion-safe-area-bottom) that causes animation on keyboardWillHide to jump by the value of ion-safe-area-bottom because it was 0 and now on WillHide it has some value.

I hope screen recording will help you get my point:

With padding-bottom: var(--ion-safe-area-bottom) on ion-toolbar inside ion-footer:

screen-20260905-125236-1788605548311.mp4

Wit padding-bottom: 0 on ion-toolbar inside ion-footer (no glitch jump):

screen-20260905-125432-1788605662781.mp4

I'm not sure if this is related to this, as this might be a separate issue.

Your current solution solved all issues I had with incorrect/late keyboard height on keyboardWillShow so I'm totally satisfied with this solution.

@lincolnthree

Copy link
Copy Markdown
Author

AI Summary of the issue:

The jump is caused by how the underlying Chromium WebView handles CSS Environment variables during native animations.

  • When the keyboard is open, Chromium natively sets env(safe-area-inset-bottom) to 0px because the keyboard covers the bottom of the screen.
  • When you dismiss the keyboard, the Android OS immediately notifies Chromium of the new target WindowInsets (which includes the 34px navigation bar).
  • Chromium instantly sets env(safe-area-inset-bottom) from 0px to 34px on frame 1 of the close animation. Because CSS environment variables do not inherently animate, any DOM element relying on it (like Ionic's --ion-safe-area-bottom mapped in ion-footer) instantly resizes, causing a jarring 34px jump before the keyboard slides down.

I think you have a custom transition on your footer, right? The way I solved this was to not transition the element as the keyboard closes. The transition is on keyboardWillOpen only -- when the keyboard closes, the elements instantly drop back down to the bottom of the viewport. The other reason is the CSS animations often lag slightly behind the keyboard closing and it can look a little weird.

@kosserin

Copy link
Copy Markdown

@lincolnthree I would like to buy you a coffee for the help. You did a great job. Do you have a sponsor link by any chance?

@lincolnthree

Copy link
Copy Markdown
Author

@lincolnthree I would like to buy you a coffee for the help. You did a great job. Do you have a sponsor link by any chance?

Aw thanks, I appreciate that but no I don't. You helped get this over the line just as much as I did! Thanks to you we figured out the full puzzle.

You can check out my app though :) www.topdecked.com -- what are you building?

@kosserin

Copy link
Copy Markdown

Hey Lincoln,

I'm building a mobile hybrid payment app using Angular, Ionic and Capacitor.

Currently learning Next.js, BE and building fullstack projects in my spare time :)

@lincolnthree

lincolnthree commented Oct 2, 2026 •

Copy link
Copy Markdown
Author

Hey @kosserin - Is this still working well for you? It's fine here but just checking since it's been a while since we submitted the PR and I wanted to double check if anything else needs to be addresed.

Also the issue report I filed is here: #80

@kosserin

kosserin commented Oct 6, 2026

Copy link
Copy Markdown

Hey @lincolnthree , I tested it once again and I can confirm it is working properly, but didn't have time to push it to main branch as it's unfortunately not top priority at this moment. I believe in next week or so I will push it forward so others can report if something has to be addressed

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Inconsistent Keyboard Height on Android during WindowInsetsAnimation (Proposed Patch)

3 participants