testsAndMisc/python_pkg/android_ui/DOCS-android-ui-rationale.md
Krzysztof kuhy Rudnicki ed9fb48c51
Some checks are pending
dependency freshness / dependencies on newest stable (push) Waiting to run
md-naming / markdown naming convention (push) Waiting to run
Pre-commit checks / pre-commit (${{ matrix.group }}) (mypy, mypy) (push) Waiting to run
Pre-commit checks / pre-commit (${{ matrix.group }}) (pylint, pylint) (push) Waiting to run
Pre-commit checks / pre-commit (${{ matrix.group }}) (rest, ) (push) Waiting to run
Pre-commit checks / pre-commit (${{ matrix.group }}) (shellcheck, shellcheck) (push) Waiting to run
Python tests / test (push) Waiting to run
repo-contract / repo contract (push) Waiting to run
Shell tests / shell-tests (push) Waiting to run
Shell tests / lib-tests (push) Waiting to run
Shell tests / arch-tests (push) Waiting to run
Move TODOs to todo app
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-11-02 11:29:12 +01:00

42 lines
2.0 KiB
Markdown

# Android UI automation: rationale and Flutter pitfalls
## why this is not a nicety
Observed in one session (2026-08-10, workout_app Firebase verification):
- **Silent mis-taps.** A tap at `(540, 1705)` intended for the password field
landed with the field unfocused. The typed password went nowhere. Nothing
errored — the next screenshot just showed an empty box. A no-op and a hit are
indistinguishable without dumping the tree and diffing.
- **Layout shift invalidates every coordinate.** Opening the soft keyboard moved
the email field from y=1572 to y=1319. Coordinates captured one step earlier
were already wrong.
- **Screenshot scaling is a manual multiply.** The harness renders 1080x2400 at
900x2000, so every read coordinate needs `* 1.20`. One forgotten multiply is
an off-screen tap.
- **Screenshots are expensive.** A full-page PNG per step burns far more context
than the equivalent element list, and can't be grepped or asserted on.
`uiautomator dump` already solves this and is on every device — no new
dependency:
```bash
adb shell uiautomator dump /sdcard/ui.xml
adb pull /sdcard/ui.xml
# -> <node text="Connect Firebase" class="android.widget.Button"
# bounds="[32,1808][1048,1878]" .../>
```
This is how the same session recovered: dumping the tree found both `EditText`
nodes by class and gave exact centers, and the retry worked first time.
- **Observed 2026-08-10, must be handled:** while a Flutter `TextField` holds
focus, the dump returns ONLY the `EditText` nodes — the rest of the tree
(including the button you are about to tap) is missing, and it stays missing
across retries, so a naive retry loop never converges. Worse, `KEYCODE_BACK`
to dismiss the keyboard is treated by Flutter as a route pop: it navigated
out of Settings and discarded the typed credentials, twice. The tool must
unfocus without popping the route (tap dead space, or `dumpsys input_method`
to check `mInputShown` first) and must distinguish "tree is partial" from
"element is genuinely absent" rather than reporting a false negative.