Commit Graph
5 Commits
Author SHA1 Message Date
irisandClaude Fable 5.1 86cf4e8088 ui-trace: add holddrag, a press-hold-then-drag gesture
Neither existing action can produce "hold stationary for LONG_PRESS,
then move without lifting, then release": tap has no hold at all, and
swipe X1 Y1 X2 Y2 MS interpolates motion across its whole duration
starting at t=0, so a long swipe with a short first segment is still
continuous motion throughout, never a hold followed by a drag.

holddrag X1 Y1 X2 Y2 HOLD_MS MOVE_MS extends the same
MotionEvent/injectInputEvent mechanism swipe already uses: DOWN, sleep
HOLD_MS, then MOVE at the same fixed 10ms cadence swipe uses over
MOVE_MS, then UP -- one continuous touch. Additive; existing commands
unchanged.

Verified against a real device (this repo has no unit test harness, so
verification is driving a device, matching its existing posture):
`ui-trace record --do "holddrag 300 1850 300 2050 600 300"` against
ai-app-2's iris transcript screen produced a real long-press-then-drag
selection (confirmed by the app's own logcat and a screenshot showing
the resulting highlighted selection) that neither tap nor swipe could
reach. javac --release 17 -Xlint:all -Werror clean.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
2026-09-05 15:01:01 -04:00
irisandClaude Opus 5 b678bf3ccd Press things by name in ui-trace, not by coordinate
`--do "tap 'Save'"` finds whatever currently carries that label -- the text,
or the padded control around it, preferring the clickable one -- resolves its
box from the accessibility tree at the moment of the gesture, and presses its
centre. It is the name the control already has for assistive technology, so
there is nothing extra to keep in step with it.

`tap X Y` still works and is now the exception. A coordinate is a position
measured once by hand: anything that moves the control makes the tap land on
whatever now sits there, and the script then reports a number that was never
measured, which reads exactly like a result rather than like a failure.
ai-app's two benchmark scripts pressed a header button at `tap 723 205` and
that button has now moved; Iris asked on 2026-09-03 that the fix be in the
tool rather than a habit each script remembers.

Two things make the failure loud, which is the whole point. A label that is
not on screen ends the recording with `# error` and a non-zero exit, and
`record` now prints the error lines rather than the head of a trace that can
be thousands of frames long. And the sampling loop waits for the action
thread before exiting -- a script whose last step outlasted the recording
used to have its outcome discarded, including that error.

The label is looked for over three seconds rather than once: the
accessibility connection has no window at all for the first frames after it
is made, and a control revealed by the previous step arrives a frame or two
later. The wait is bounded and the failure is still loud.

Exercised against a running emulator: a tap by label, a tap that finds
nothing, and a failure that lands after the recording's own duration.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-03 23:52:43 -04:00
iris 69a22c54e7 Stop Gradle installing on every checkout's emulator at once
The adb wrapper can only aim a call that goes through it, and Gradle's
Android tasks do not: installDebug, uninstallDebug and connectedAndroidTest
ask the adb server for every attached device and act on all of them. One
session ran installDebug with two emulators up and replaced the app on both,
which Gradle reported as success and the other session read as its own build
never landing.

`emu check` answers the question the wrapper already answers for adb --
would a command that reaches every attached device reach a stranger's
emulator from here -- and install.sh links a Gradle init script into
~/.gradle/init.d so every build on this machine asks it, including checkouts
nobody has adopted it in.

It refuses narrowly: a lone emulator, a physical phone and a caller who
named a device are all fine. ANDROID_SERIAL naming another checkout's
emulator is not, because that is what a serial exported into a long-lived
shell decays into once an emulator restarts and another's takes the port.
2026-08-31 15:40:58 -04:00
irisandClaude Opus 5 1d71730b56 Render on the GPU with no display, through Xwayland
This VM sees the host's 7900 XT through virtio-gpu and renderD128 is
readable without the video group, so the software rasteriser was never
necessary. What was in the way is that the emulator's `-gpu host` renderer
speaks GLX: with no DISPLAY it cannot open a context at all
("GlxEnginegetDefaultDisplay: Failed to open display 0") and silently falls
back.

So `emu up` now starts a headless sway whose only job is to provide an
Xwayland, and Xwayland's GLX runs on the render node. The guest's GLES
reports `virgl (AMD Radeon RX 7900 XT)` afterwards. One compositor per
machine, on a named socket so a second `emu up` reuses it; no seat, no DRM
master, no card node.

Scrolling a list in the stock Settings app: 25.5% janky frames and 8 slow
UI-thread frames on software, 3.3% and 0 on the GPU. An emulator also costs
1.1 GB less resident (2.9 GB against 3.9 GB), which is real headroom on a
machine that only takes two.

The reason this is worth the machinery is measurement rather than speed.
Before it, frame timings from in here said nothing about an app at all --
ai-app scrolled *better* than Settings, because both were bounded by the
rasteriser rather than by anything either was doing.

Host Vulkan is off in `GPU_HOST_FEATURES`, and that one is a bug rather than
a preference: gfxstream asks Venus for a memory type for its ColorBuffers
that Venus does not offer, and the emulator dies before adb sees it. GLES is
unaffected. Retry it whenever mesa moves; the failure is loud.

A missing sway, or one that will not start, says so and falls back to
software exactly as before -- verified by moving the config aside.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-30 20:29:02 -04:00
irisandClaude Opus 5 5d77599f3f Collect the emulator tooling into one repo
ui-trace, the adb wrapper and its device recorder were loose files under
~/.local, and each Android checkout carried its own copy of the same
emulator boot sequence. This puts them together, with an install script that
symlinks them back so editing the repo is editing what runs.

Two things are new rather than moved. `emu` is the emulator lifecycle --
name, serial, list, up, down -- keyed on the AVD named after the enclosing
checkout, which is the rule that lets several sessions work here at once;
and `adb` now fills in `-s` from that same rule, because with two emulators
attached a bare `adb shell pm list packages` comes back empty rather than
failing, which reads as the app being uninstalled rather than the question
being ambiguous.

`emu up` refuses when the machine has no room. On 2026-08-30 an emulator
started with 2.8 GB available invoked the OOM killer, and what it took was
not the emulator that had just started: it walked the user slice and killed
pipewire, dbus-broker and another session's emulator first.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-30 13:06:17 -04:00