Files
Ignacy Łątka 496b38c94d fix(keyboard): don't delete a field's own placeholder as clear residue
uiautomator renders an empty field's hint into the same `text` attribute it
uses for a value, and `measureFocusedTextLength` reads that attribute raw.
That was harmless while the reading was only a COUNT — over-measuring costs
a few no-op backspaces — but the select-all's read-back turned it into a
PREDICATE, where an over-measurement is a delete run against a field the
clear already emptied. Reproduced on the exact shape this file documents for
API 36 (`text="Search settings" … hint="Search settings"`, on a level that
HAS `keycombination`, so squarely on this path): a clear that fully worked
emitted `input keyevent 123` plus 23 backspaces into the empty box. Past
MAX_DELETE_COUNT it is worse than wasteful — a 171-character placeholder
turned the same successful clear into a 400 with the `{ clear, text }` call's
text never sent.

`hint` is the discriminator and `parseUiAutomatorXml` already surfaces it, so
the read-back now counts a `text` equal to its own `hint` as empty. Only the
read-back: the sizing read wants the opposite error, since under-measuring
there truncates the field, and the levels it serves emit no `hint` anyway.
That made three rules that all fork the same way, so `maxDumps` and
`unmeasurableIsUnreadable` collapse into one `readBack` flag naming the
caller rather than each rule.

Also, from the same pass:

- The tool description promised "a rejected request never changes the field …
  a 400 leaves the field exactly as it was". The rescue arm's over-length
  refusal is a 400 raised after a select-all and a DEL have gone out, which
  the refusal's own text admits. The paragraph now names that one exception.
- The refusal said the DEL "removes one character where it lands" — true only
  where the chord was swallowed. A chord that took makes the same DEL remove
  the whole selection; both outcomes are named now.
- The `cleared` docstring listed three escapes from the repair. There are
  four, and one is wider than stated: an unmeasurable focused editable
  ANYWHERE on screen discards the reading, not only one in the target field.
- `devtoolsHierarchyReader` and `AndroidClearOptions.readHierarchy` still
  described a legacy-only consumer and the wrong failure mode for the modern
  one, which is now this reader's dominant caller.
- The per-device chain's rationale said a character arriving after the DEL
  "replaces the whole selection". The DEL consumes the selection; what a
  character landing in the read-back window actually gets is measured as
  residue and backspaced away.

Mutation-checked: dropping the placeholder rule, applying it to the sizing
read, matching on `hint`'s presence instead of its value, and each of
`readBack`'s three rules all fail their own tests and nothing else.
2026-09-07 19:06:07 +02:00
..