Skip to content

fix(android): keep a scroll's swipe out of the IME window - #2514

Open
thymikee wants to merge 1 commit into
fix/scroll-keyboard-viewport-2500from
fix/scroll-keyboard-viewport-android-2500
Open

fix(android): keep a scroll's swipe out of the IME window#2514
thymikee wants to merge 1 commit into
fix/scroll-keyboard-viewport-2500from
fix/scroll-keyboard-viewport-android-2500

Conversation

@thymikee

Copy link
Copy Markdown
Member

Summary

Second half of #2500, stacked on #2503 (the shared rule, the iOS runner clip, and the golden table land there). Android had no keyboard input into a scroll's plan at all: an adjustPan or adjustNothing activity reports window bounds that already run under the IME, so the swipe was planned into the keys, the surface never moved, and the edge loop reported a stuck container.

The helper now reports the largest input method window beside the application window, in the same absolute screen pixels, and scroll clips its band with the shared contracts/scroll-gesture rule or refuses with scroll_keyboard_occludes_surface when the keys own the surface. 10 files, 463 gross lines.

Depends on #2503. Do not merge before it.

Behaviour

  • An older helper returns no keyboard keys and a provider-supplied viewport has no IME channel. Both read as "nothing to avoid", which is what the shared rule already does with a missing frame; neither becomes a refusal.
  • keyboardAvoided and keyboardMinY appear only when the clip ran, matching the Apple side, so the two platforms answer with the same shape.
  • The keyboard is never dismissed: that drops focus and breaks a fill/scroll/fill loop. keyboard dismiss stays explicit.

Validation

pnpm check green on this branch.

Live on a Pixel 7 emulator, Contacts editor with sim={adjust=pan} (from dumpsys window windows), LatinIME bound:

  • helper reports the IME window [0,1517][1080,2400] while the application window keeps its full 1080x2400;
  • scroll down --jsonreferenceHeight: 1505, keyboardAvoided: true, keyboardMinY: 1517, swipe 1205 -> 301;
  • before this change the same command swiped 1920 -> 480, starting under the keys.

UiAutomation.getWindows() answers with an empty list until the service asks for interactive windows, so the viewport read applies the seam the tree capture already uses instead of trusting that a snapshot capture ran earlier in the same instrumentation. Without it the read sees no windows at all, fails open, and looks identical to "no keyboard on screen".

Regression proof: neutering the clip fails 2 scroll tests and restores green.

Harness note for the next agent

open activates agent-device's headless imehelper, which draws no keyboard, and the emulator defaults to no soft keyboard while a hardware keyboard is attached. Bind a real IME after open:

adb -s <serial> shell settings put secure show_ime_with_hard_keyboard 1
adb -s <serial> shell ime set com.google.android.inputmethod.latin/com.android.inputmethod.latin.LatinIME

Restore com.callstack.agentdevice.imehelper/.TestInputMethodService afterwards, or typing determinism in later sessions is silently different.

Size

10 files, 463 gross lines, one command family. The Android-shaped rows it satisfies (adjustPan window with only 88px of 900px left above the IME is refused) are already in the shared table landed by #2503.

Android is the case the clip exists for beyond iOS: an `adjustPan` or `adjustNothing` activity keeps a
window whose recorded bounds already run under the IME, so a plan built from them aims at the keys.
The helper now reports the largest input method window beside the application window, in absolute
screen pixels like the window next to it, and `scroll` clips its band with the shared rule or refuses
when the keyboard owns the surface.

An older helper reports no keyboard keys, and a provider-supplied viewport has no IME channel at all.
Both read as "nothing to avoid", which is what the shared rule already does with a missing frame;
neither turns into a refusal.

`UiAutomation.getWindows()` answers with an empty list until the service asks for interactive windows,
so the read applies the seam the tree capture already uses rather than depending on a snapshot capture
having run first in the same instrumentation; the one-shot fallback below it has no such neighbour.
Measured on a Pixel 7 emulator with an `adjust=pan` contact editor, the application window keeps its
full 2400px height while the IME window reports `[0,1517][1080,2400]`, and `scroll down` answers with
`referenceHeight: 1505`, `keyboardMinY: 1517`, `keyboardAvoided: true` and a swipe ending at 301
instead of starting at 1920 under the keys.
@github-actions

Copy link
Copy Markdown

Size Report

Metric Base Current Diff
Installed (including dependencies) 4.50 MB 4.50 MB +1.2 kB
Package (unpacked) 4.50 MB 4.50 MB +1.2 kB
Package (download) 1.33 MB 1.33 MB +702 B

Startup median (7 runs, lower is better):

Scenario Base Current Diff
CLI --version 18.4 ms 18.5 ms +0.1 ms
CLI --help 46.4 ms 50.2 ms +3.8 ms

@github-actions

Copy link
Copy Markdown
PR Preview Action v1.8.1

QR code for preview link

🚀 View preview at
https://callstack.github.io/agent-device/pr-preview/pr-2514/

Built to branch gh-pages at 2026-09-12 12:19 UTC.
Preview will be ready when the GitHub Pages deployment is complete.

@thymikee

Copy link
Copy Markdown
Member Author

The IME window selection can still leave a scroll inside the keyboard at f63e72f. GestureViewportReader.java:54-61 keeps only the largest input-method rectangle, even though the composer and key plane may be separate windows. A larger lower key plane discards a taller composer’s earlier top edge, so the computed swipe band can overlap that composer. Preserve the earliest top edge of the IME windows intersecting the swipe path and add a two-window regression. Current-head checks pass and the reported Pixel 7 adjustPan run covers the single-window case. This layer also depends on #2503.

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.

1 participant