Two user-reported bugs in the post-wrap build:
1. Selection 'jumped' when scrolling: frameOf's chunked branch shadowed the
outer start/end with 'start, end, winLine := cb.VisibleByteRange(...)',
leaving IMEWindowStartByte at 0 for chunked files while scrolled, so the
window-relative selection highlight (and IME commits) mis-mapped near the
file top. Fix: declare winLine outside, assign. Mutation-verified
regression tests: real_file_scroll_selection_test.go.
2. Bottom bar hidden behind the keyboard: GioView extends SurfaceView, which
unconditionally marks the window FORMAT_TRANSLUCENT; translucent windows
are never IME-resized, so adjustResize is dead. Fix (build scripts):
smali-patch a PadInsetsListener OnApplyWindowInsetsListener onto the
GioView (shrinks it by the IME inset bottom) + setDecorFitsSystemWindows
(false) on API 30+ so insets are dispatched. targetSdk kept at 34.
3. (Found while fixing 2) Keyboard could not be dismissed: TextField.Draw
issued SoftKeyboardCmd{Show:true} every frame; with insets-driven
resizes, the hide animation triggered redraws that re-showed the keyboard
mid-animation. Fix: ShowIMESeq pulse from the logic layer (open/tap/
double-tap); renderer shows only on pulse change, re-arming on focus
loss — matching widget.Editor.
On-device (emulator): BACK dismisses and stays down; tap re-shows; typing
works; bottom bar above keyboard; word selection anchored across flick
scroll. go test -race ./... green. Phone APK rebuilt with the same patch.
Same pipeline as build_emu.sh (gogio -> apktool MANAGE_EXTERNAL_STORAGE
injection -> debug-sign) for a real phone: -arch arm64,arm (covers any
phone from the last decade; gogio accepts a comma-separated arch list)
and output cmd/pad/pad-phone.apk. Notes in the header: on Android 11+
the user must grant 'All files access' in system settings after
installing (MANAGE_EXTERNAL_STORAGE is app-restricted; the emulator AVD
had it granted, phones do not).
The coordinate-space confusion (screen px 1080x2400 for input tap/
screencap, display px 900x2000 for reading PNGs, app-local px offset by
the 128px status bar, pt at density 2.625) cost real debugging time.
Wrote it down in doc/README.md as the permanent reference:
- the four spaces + conversions (display x1.2 = screen; screen Y-128 =
app-local; /2.625 = pt)
- the practical tap rule: read (x,y) off the PNG, x1.2, input tap --
screenshot px ARE screen px, no 128 offset for tap-what-you-see
- fixed geometry (status/nav bars, Gboard rows, editor region in pt),
with re-measure guidance
- tap-test pitfalls (first-tap-swallowed, first input text can fail,
wrong-place text = cursor/selection suspect not tap math, don't
memorize drifting content positions)
All keyboard row values re-measured from a fresh screenshot and verified
live: tapping the Q key at (62, 1712) and backspace at (990, 2020) both
landed first try (file content confirmed). This also exposed that the
old notes had mixed display-px row values with screen-px key values in
the same list -- exactly the confusion this section eliminates.
Also fixed the stale Phase 6 'letterboxed window' note in
development_plan.md (window is full-screen; only the 128px status-bar
offset is real; content positions drift; the OpenFileChan logcat line
was removed) and pointed emu.sh's tap help at the x1.2 rule.
Replaces the ad-hoc adb/nohup sequences used throughout verification with
one self-contained script: up/down/status, app start|stop|restart, shot,
log, tap, type, cmd (one-shot editor debug command), perf on|off|pull,
push/pull of Notes test files. Encodes the machine-local paths
(Notes/, PadPerf/ profiler + cmd file) in one auditable place.
Reliability findings baked in (each hit during testing):
- The AVD auto-saves a 'default_boot' instant-boot snapshot on clean
shutdown; a corrupted one makes the emulator segfault during restore
(device never comes online). up() detects it (process death / no adb
device within PAD_ONLINE_TIMEOUT) and falls back to a cold boot with
-no-snapshot-load; the next clean down() re-saves a good snapshot, so
this self-heals. down() is the only sanctioned stop (clean kill saves
the snapshot; SIGKILL corrupts it).
- The emulator process identity is ambiguous (launcher vs
qemu-system-*-headless), and crashpad/netsimd children carry the AVD
name in their command lines. Liveness and kill therefore use a tight
signature: comm matches qemu-system-* AND cmdline contains ' -avd
<AVD>' (never a broad pkill -f pattern — one matched and killed the
calling shell during testing).
- Before any launch, orphaned qemu instances are cleared (they hold the
AVD lock and make new launches fail silently); before starting a
replacement, the old one must be fully gone (lingering device/lock
causes false success).
- Timeouts: short online window for the snapshot attempt, full
BOOT_TIMEOUT for the fallback cold boot (~20 s).
Tested: idempotent up, 10 s snapshot-restore boot, forced fallback
(30 s end-to-end, verified correct instance), orphan cleanup, perf/cmd/
push/pull/shot/log on-device. doc/README.md now documents it.
The Android build recipe lived at /tmp/build_pad.sh and depended on
/tmp/apktool.jar; /tmp gets wiped between sessions (both were lost once
mid-project and had to be rebuilt). Move the recipe into the repo as
scripts/build_emu.sh:
- checks prerequisites (java, gogio, apksigner, adb, debug keystore) with
actionable error messages
- auto-downloads apktool v3.0.3 to the stable ~/android-sdk/tools/ if
missing (never /tmp)
- uses a mktemp work dir cleaned via trap; no /tmp clutter
- --no-install flag; verifies an adb device is present before installing
- verifies the permission injection actually took effect
Tested end-to-end (build + install + app relaunch) and idempotent on
re-run. doc/README.md and development_plan.md now point here.