Skip to content

fix(android): account for edge-to-edge inset in keyboard avoidance - #190

Open
Qiiks wants to merge 1 commit into
dzianisv:mainfrom
Qiiks:fix/android-keyboard-insets
Open

fix(android): account for edge-to-edge inset in keyboard avoidance#190
Qiiks wants to merge 1 commit into
dzianisv:mainfrom
Qiiks:fix/android-keyboard-insets

Conversation

@Qiiks

@Qiiks Qiiks commented Aug 17, 2026

Copy link
Copy Markdown

Closes #156

Context

This is a focused extraction of the keyboard portion of blocked PR #182. It preserves the original fix and author credit from commit 38544db, while leaving the unrelated SSE, auto-scroll, text-selection, and reconnect changes out of this PR.

Root cause

Under Expo edge-to-edge Android, KeyboardAvoidingView measures its layout frame in window coordinates while the keyboard reports screen coordinates. The origins differ by the top status-bar inset, so the computed padding is short by exactly that inset and the composer remains behind the keyboard.

Change

  • Add a pure keyboardVerticalOffset(platform, insetTop) helper.
  • Keep iOS's existing empirical offset of 90.
  • Use the Android top safe-area inset as the offset, clamped to zero for invalid negative values.
  • Apply the helper to the session screen's KeyboardAvoidingView.

Verification

  • Focused keyboard tests: 5 pass, 0 fail.
  • Expanded repository tests: 325 pass, 0 fail.
  • TypeScript typecheck: clean.
  • Version parity check: 0.4.15 / Android versionCode 42 aligned.
  • RED proof against upstream main: the new test fails with ERR_MODULE_NOT_FOUND because upstream main has no keyboard-offset implementation; the branch then passes all 5 focused tests.
  • Diff contains exactly three files: app/session/[id].tsx, src/lib/keyboard-offset.ts, and src/lib/keyboard-offset.test.ts.

Device verification

The reporter is using Android 17, but this Windows host currently has no Android SDK, emulator, ADB binary, or connected device. Therefore no Android 17 rendering claim is made here. The original commit documents on-device verification on a Pixel 3 XL / Android 12; please validate this focused PR on Android 17 hardware before merging.

Related work

…#156)

The composer (text field, attachment buttons, mic) is completely hidden
behind the keyboard on Android. Reproduced on an Android 12 emulator on
current main, i.e. with 6c103ac's behavior="padding" already applied.

The cause is a coordinate-space mismatch rather than the behavior prop.
KeyboardAvoidingView computes

    padding = frame.y + frame.height - (keyboardFrame.screenY - offset)

`frame` comes from its own onLayout, in **window** coordinates (origin
below the status bar). `keyboardFrame.screenY` is in **screen**
coordinates (true top of the display). Before Expo's mandatory
edge-to-edge those origins coincided and the OS also resized the window,
so the difference was invisible. Under edge-to-edge the window spans the
whole display, the two spaces disagree by exactly the status-bar inset,
and the computed padding lands short by that amount.

Measured on an Android 12 emulator (scale 3.5), keyboard open on the
session screen:

    screen height     845.71 dp      window height  748.86 dp
    insets.top         48.86 dp      insets.bottom      48 dp
    keyboard screenY  511.71 dp      height            286 dp

    computed padding = 748.86 - 511.71 = 237.14 dp
    required padding =                   286.00 dp
    shortfall        =                    48.86 dp   === insets.top

48.86dp x 3.5 = 171px, which is exactly the composer row.

This also explains why the problem keeps returning under new issue
numbers: dzianisv#53/dzianisv#70, dzianisv#147/dzianisv#148 and the closed dzianisv#74 each only changed the
`behavior` value, so none of them addressed the mismatch.

Add keyboardVerticalOffset(platform, insetTop): iOS keeps its existing
empirical 90 (it has no such mismatch), Android returns insets.top,
clamped at 0 so a bogus inset can never push content downward. Pure and
unit-tested, including a guard asserting the corrected arithmetic lands
exactly on the real keyboard height.

Verified on device: composer, attachment and mic controls all visible
and usable above the keyboard, in portrait and at 1.5x font scale.
Landscape is not applicable since app.json locks portrait orientation.
Copilot AI lite review requested due to automatic review settings August 17, 2026 04:02

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copilot was unable to review this pull request because the user who requested the review has reached their quota limit.

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.

Unable to see the input box

2 participants