v0.19.0: andTunes plays music on the Rabbit

The Android half of andTunes had been paused on a 1 GB toolchain download
(Gradle wrapper, Android Gradle Plugin, Kotlin). None of that was needed:
platform 33 and build-tools 34 were already in ~/Android/Sdk, and an app that
links no libraries needs five SDK steps, not a build system. andtunes/build.py
runs aapt2 -> javac -> R8 -> zipalign -> apksigner and produces a 61 KiB APK.

andTunes 0.1.0 is framework-only Java: a six-tile menu that loads nothing, a
streaming library.json parse grouped into artists and albums in one pass, one
ListActivity for every list (songs and artist songs open with Shuffle all,
albums carry art thumbs, search as you type), a Now playing bar under every
list, and a white Now Playing screen with square art, a slim scrubber,
prev/play/next, shuffle and repeat, and no back button. A foreground
PlaybackService owns the queue: audio focus, becoming-noisy, MediaSession and
a MediaStyle notification, end of queue pauses, missing files skip, and the
last queue and position come back on launch. Black on white, and every size
doubled for the R1's override density. Cold start to the menu: 313-338 ms.

The scroll wheel turned out to be DPAD_UP/DOWN (stock Generic.kl), not
volume. The app takes both in dispatchKeyEvent: lists scroll a row per
detent, and the menu and Now Playing get volume.

LinTunes side: Connections > Install andTunes on Rabbit... installs the
committed APK over adb and grants all-files access, or copies it to Download/
over MTP when there's no adb. Sync fills Buttons/ with default artwork only
where a file is missing, so trav's own art is never overwritten.

Verified against the dev library only, synced to the R1 and driven with
adb input + screencap.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01KSsYtRn4WZx6xj4GEdVPt9
This commit is contained in:
2026-09-11 14:25:55 -05:00
co-authored by Claude Opus 5
parent 8188a5b389
commit 9483d8951b
51 changed files with 2963 additions and 98 deletions
+18
View File
@@ -325,6 +325,24 @@ persistence) → GUI (Qt widgets that read the manager and connect to its signal
`device_sync` (so it rides the Syncthing share and both machines agree);
keeping it off the `Playlist` keeps it out of the conflict-merge machinery,
and **unticking is the only way a playlist comes off the device**.
Since Round 50 the app exists: `andtunes/app/` is plain Java against the
Android framework, built by `andtunes/build.py` (aapt2 → javac → R8 →
zipalign → apksigner) — **no Gradle, no Kotlin**, because platform 33 +
build-tools 34 were already on disk and trav didn't want a 1 GB download
over cell for tools that make the same APK. `--ship` copies it to
`lintunes/android/andTunes.apk` with its version in `andTunes.json`;
**re-run `--ship` whenever the app changes**, since the committed APK is
what the self-updater carries and what `install.py` installs.
`andtunes/andtunes.keystore` is committed on purpose (both machines must
sign identically or `adb install -r` refuses the update). `install.py` is
Connections → Install andTunes on Rabbit…: adb as a runtime tool, then the
all-files + notification grants (a refused grant is not a failed install —
the app's first-run screen covers it), or an MTP copy to `Download/` with
no adb. Sync copies `buttons/*.png` (rendered by
`andtunes/tools/make_buttons.py`) into the device's `Buttons/` **only
where the file is missing** — those names are a contract with the app's
`Ui.button()`, which prefers the device copy. `proguard.pro` keeps no
debug attributes because R8 8.2 NPEs on javac 21's.
- **`lintunes/export/`** — `File → Export Playlist…` (also on a playlist's
right-click menu). A sibling of `device_sync`, reusing its filename helpers