# ADR 0015 — The Android terminal's input path leaves the WebView - Status: proposed — **Stage 1 built, unverified on a device** - Date: 2026-08-25 - Builds on: [`docs/android-port.md`](../android-port.md) §9, [ADR 0011](0011-android-distribution.md) - Does not settle: whether the renderer stays xterm.js. That is [Stage 3](#stage-3--the-renderer-deliberately-unsettled), and it is deliberately left open. ## Context The phone got the desktop's terminal, and the reasons were good ones. `TerminalDataPlane` already served the page and a binary protocol over a loopback WebSocket, the xterm.js bundles were embedded resources and platform-neutral, and `Avalonia.Controls.WebView` shipped a `net10.0-android36.0` target. `docs/android-port.md` called it *"better news than expected"*, and for rendering it was. **For typing it was not, and typing is the complaint.** Not scrollback, not colour, not throughput — the act of putting characters into a shell. That narrows the problem usefully, because the input path and the render path are separable, and only one of them is wrong. ### Three things sit between a finger and a byte, and the middle one is the fault **1. Avalonia and the WebView fight over native focus.** `AvaloniaView.DispatchTouchEvent` ends every handled touch — DOWN and UP alike — with a `RequestFocus()` for Avalonia's own view. The WebView's input connection dies with its focus, so the keyboard swaps layouts and the inset churn can park it over the accessory row that was tapped. `Platform/TerminalFocus.cs` exists to hand focus back, posted to the next main-looper message so the platform has taken its turn first. It works. It is also a counter-move in a fight that only exists because the thing holding the input connection is not the thing the framework thinks is focused. **2. The keyboard is a prose keyboard, and no DOM attribute makes it stop.** This is the one that was worth checking rather than assuming, and the check inverted the obvious conclusion. The vendored xterm.js *already* sets every hint the web platform has on its helper textarea — `autocapitalize="off"`, `autocorrect="off"`, `spellcheck="false"`. They are set, and Gboard shows the suggestion strip and predictive text anyway. So this is not a missing attribute; it is a ceiling. Termux's `TerminalView.onCreateInputConnection` says what the ceiling is. It sets: ``` InputType.TYPE_TEXT_VARIATION_VISIBLE_PASSWORD | InputType.TYPE_TEXT_FLAG_NO_SUGGESTIONS ``` **`VISIBLE_PASSWORD` is the load-bearing half.** `NO_SUGGESTIONS` alone is advisory and most keyboards ignore it — a point made in Avalonia's own Android keyboard PR discussion, not only in Termux's. What actually turns off autocorrect, the suggestion strip and the predictive composing layer is the *variation*: a field the keyboard believes is a visible password gets no dictionary. That is an `EditorInfo.inputType` on a native `InputConnection`, and a page inside a WebView has no way to reach it — Chromium computes the `EditorInfo` from the focused DOM node, and no combination of attributes on a `