Skip to content

soft-keyboard-geometry

Decides whether the Android soft keyboard is up, and where to touch to raise it — from geometry alone.

Nothing in the page reports the keyboard. innerHeight, visualViewport and the modal container all stay at their full height with the keyboard shown and dumpsys input_method reporting mInputShown=true — Obsidian Mobile keeps a full-screen container and lifts its contents inside it. The only signal is that the field moves, which is why the detection here is geometric rather than an API call. Two runs failed on that before a framebuffer dump said so, which is also why the diagnostic below exists.

The signal is a DELTA against a baseline read before the touch, not the field’s absolute offset from the bottom. The absolute offset was the original test and it is right only for a bottom-anchored field — a suggester, a command palette — where “clear of the bottom” and “lifted” are the same sentence. For a centred modal the condition is already true with no keyboard at all, so the test passed vacuously and raiseSoftKeyboard returned success having dispatched no touch (measured 2026-09-12 against a centred prompt modal: the field’s top read 352.4453125 before and after real taps and the check said the keyboard was up both times). A delta is the same public API for both shapes, and it costs a field that cannot move its verdict — see checkIsSoftKeyboardUp for the one case that trades away.

Pure and unit-tested; the touching and the capture live in soft-keyboard.

InterfaceDescription
BuildSoftKeyboardDiagnosticMessageParamsParameters for buildSoftKeyboardDiagnosticMessage.
CheckIsSoftKeyboardUpParamsParameters for checkIsSoftKeyboardUp.
ElementRectA rectangle as the renderer reports it, in CSS pixels.
ResolveSoftKeyboardTapPointsParamsParameters for resolveSoftKeyboardTapPoints.
SoftKeyboardTapPointA point on the device’s screen, in device pixels.
SoftKeyboardViewportSnapshotWhat the renderer knows about its own geometry.
FunctionDescription
buildSoftKeyboardDiagnosticMessageBuilds the message a failed raise reports. A bare “the keyboard did not come up” is unreadable — what the reader needs is what the page saw and what the device thought, side by side, because the two disagreeing is the whole diagnosis. The page’s half is the lift and the two tops it came from, since the lift is the verdict; a lift of zero gets a sentence of its own, being the one reading that an already-up keyboard also produces.
checkIsSoftKeyboardUpDecides whether the IME is up, from how far the field moved. The verdict is a delta: the field must have risen from where it sat before the touch, by at least minimumKeyboardHeightInPixels. Obsidian Mobile lifts whatever is on screen to make room for the IME, so a field that has not moved has had no keyboard arrive under it — whether it is a bottom-anchored suggester or a centred modal. The one case this gets wrong, deliberately: a keyboard that was already up before the baseline was read. The field has nowhere left to lift to, so the answer is false. No amount of geometry separates that from a centred modal with no keyboard — both read as “clear of the bottom and not moving” — and of the two possible wrong answers, a loud false is the one worth keeping: it fails a capture rather than silently returning a screenshot of a keyboard that is not there. Read the baseline with the keyboard down, which is what raiseSoftKeyboard does.
parseInputMethodStateReduces dumpsys input_method to the handful of fields that say whether the IME is showing.
resolveSoftKeyboardTapPointsWorks out where to touch, in the device pixels adb shell input tap speaks. Two candidates, not one: the WebView may or may not start at the top of the screen, and the page cannot tell which. Both land inside the field — it is taller than the offset — so a wrong guess costs one touch rather than a mis-tap on whatever sits below.
VariableDescription
DEFAULT_MINIMUM_KEYBOARD_HEIGHT_IN_PIXELSThe least a raised keyboard lifts the field by, so a stray rounding pixel is not read as one. Exported because it is the number both checkIsSoftKeyboardUp and raiseSoftKeyboard default to, and a suite tightening it wants to say so relative to this rather than in the abstract.