v0.17.1: a library to break
Round 47 was verified by loading trav's real 21,531-track library. Nothing was written and nothing was at risk, but it's the wrong habit and he said so. The alternative is better for development anyway: reproducible, in-repo, and full of the awkward cases on purpose rather than by luck. scripts/make_dev_library.py builds a complete synthetic library — data dir and music tree — from ffmpeg sine waves in six containers, with art on some albums and not others, every playlist type (regular, folder, live/non-live/ unsupported/limited+nested smart, system, empty, duplicates), a play journal and a tombstone. ~4 MB, gitignored; the generator is the artifact worth keeping, not the sine waves. The edge cases are the point. A collision pair identical in artist/album/title/ number, slashes and colons in every name, a track with no artist, a multi-disc release, a compilation whose album_artist differs, a dangling location, a 208-character title, and non-ASCII plus an emoji all the way out to the m3u filename. CLAUDE.md now carries the rule: never develop against the real library, and when a feature needs a shape the fixture lacks, add it here. Using it found two bugs in it — a relative --out made every location resolve against the wrong root, and four-minute uncompressed clips made it 62 MB. andTunes is paused on a weak connection (the Android SDK is a ~1-1.5 GB one-time download; after that --offline builds need nothing). andtunes/ README.md now carries everything needed to resume cold: measured device facts, the dp trap, exact toolchain commands, locked design decisions, and the library.json contract. JDK 21 is installed and ticked off. Also answers the scroll wheel question: it reads as volume because the ROM's key layout maps the wheel's KEY_UP/KEY_DOWN to KEYCODE_VOLUME_UP/DOWN, but a focused activity sees key events first — so andTunes can claim it by consuming both those and DPAD_UP/DOWN, in onKeyDown and onKeyUp. No system file touched, no other app affected. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_015wrTys5U1fLb4pBD2LKWzV
This commit is contained in:
+20
-6
@@ -6,6 +6,12 @@ A music player for the Rabbit R1 that never scans anything: LinTunes writes
|
||||
|
||||
Legend: `[ ]` todo · `[~]` in progress · `[x]` done.
|
||||
|
||||
**Status: paused 2026-09-10** on a weak connection — the Android SDK is a
|
||||
~1–1.5 GB download. Everything needed to resume cold is in `README.md`:
|
||||
device facts, toolchain commands, locked design decisions, and the exact
|
||||
shape of the `library.json` LinTunes already writes. Phases are andTunes'
|
||||
own; which LinTunes round each lands in is decided when it starts.
|
||||
|
||||
## Device facts (measured 2026-09-06, `adb shell wm size` etc.)
|
||||
|
||||
- Panel **480 × 640 px**, physical density 320, **override density 160** — so
|
||||
@@ -20,11 +26,15 @@ Legend: `[ ]` todo · `[~]` in progress · `[x]` done.
|
||||
KEY_VOLUMEDOWN + KEY_POWER. Headset exposes KEY_PLAYPAUSE, so a MediaSession
|
||||
gets media keys for free.
|
||||
|
||||
## Round 48 — plays a song (v0.1.0)
|
||||
## Phase 1 — plays a song (v0.1.0)
|
||||
|
||||
- [ ] Toolchain: `sudo dnf install java-21-openjdk-devel`, Android
|
||||
commandline-tools + platform 33 + build-tools into `~/Android/Sdk`.
|
||||
(Java 25 is installed but AGP won't run on it.) Gradle via the wrapper.
|
||||
- [x] `java-21-openjdk-devel` — installed, `javac` on PATH. (Java 25 is also
|
||||
present; AGP won't run on it, so point the build at 21.)
|
||||
- [ ] **Android SDK — the one blocker.** commandline-tools + platform 33 +
|
||||
build-tools into `~/Android/Sdk`, then a first Gradle build to warm the
|
||||
wrapper and AGP caches. ~1–1.5 GB all told; after that
|
||||
`./gradlew --offline assembleDebug` needs no connection. Exact commands
|
||||
in `README.md`.
|
||||
- [ ] Gradle project, package `me.teafry.andtunes`, minSdk 26 / targetSdk 33.
|
||||
**No Compose, no androidx, no Media3** — framework `Activity`,
|
||||
`ListView`, `MediaPlayer`, `MediaSession`, `Notification.MediaStyle`,
|
||||
@@ -46,13 +56,17 @@ Legend: `[ ]` todo · `[~]` in progress · `[x]` done.
|
||||
- [ ] End of queue **pauses** unless repeat is on. Repeat: off / all / one.
|
||||
- [ ] Verify with `adb shell am start -W` — target: menu visible < 400 ms.
|
||||
|
||||
## Round 49 — the rest of the screens
|
||||
## Phase 2 — the rest of the screens
|
||||
|
||||
- [ ] Playlists, artists (→ albums + "All songs" + shuffle for that artist),
|
||||
albums, search.
|
||||
- [ ] Album art thumbs in album rows, full art on now playing.
|
||||
- [ ] The "Now playing" bar at the bottom of every list screen.
|
||||
- [ ] Scroll wheel → list scrolling, and volume on the now-playing screen.
|
||||
Consume `KEYCODE_VOLUME_UP/DOWN` **and** `DPAD_UP/DOWN` in both
|
||||
`onKeyDown` and `onKeyUp` — the ROM's key layout decides which arrives,
|
||||
and claiming them in a focused activity affects no other app. First
|
||||
check `/system/usr/keylayout/` on the device; see `README.md`.
|
||||
- [ ] Replaceable button images: load `Buttons/<name>.png` from the device
|
||||
folder when present, else the bundled drawable. LinTunes ships defaults
|
||||
into `Buttons/` only when the file is missing, so the user's own
|
||||
@@ -61,7 +75,7 @@ Legend: `[ ]` todo · `[~]` in progress · `[x]` done.
|
||||
the queue.
|
||||
- [ ] Resume last track + position on launch.
|
||||
|
||||
## Round 50 — install from LinTunes, counts coming back
|
||||
## Phase 3 — install from LinTunes, counts coming back
|
||||
|
||||
- [ ] `Connections → Install andTunes on Rabbit…`: `shutil.which("adb")`
|
||||
(the `ffmpeg_available()` runtime-tool pattern — never a pip dep), then
|
||||
|
||||
Reference in New Issue
Block a user