moved tasks to projects board
This commit is contained in:
@@ -0,0 +1,28 @@
|
||||
# andTunes — device facts
|
||||
|
||||
Measured facts about the Rabbit R1 that the app is built around.
|
||||
(Moved out of the old andtunes/TASKS.md board on 2026-09-24.)
|
||||
|
||||
|
||||
- Panel **480 × 640 px**, physical density 320, **override density 160** — so
|
||||
the app sees a 480 × 640 **dp** canvas on a 2.88" screen (~278 real ppi).
|
||||
- **One dp is about half its usual physical size here.** Double every stock
|
||||
value: body text 28–32 sp, list rows ≥ 88 dp, menu tiles ~213 dp. A 48 dp
|
||||
row would be 4.4 mm tall. Do not "fix" this by changing the device density —
|
||||
trav likes it where it is.
|
||||
- Android 13 / API 33, `gsi_r1-userdebug`, arm64-v8a, 65 GB free.
|
||||
- **The wheel can only ever give us detents.** `getevent -pl` (2026-09-14):
|
||||
`och1970_holl_key` advertises `KEY (0001): KEY_UP KEY_DOWN` and *nothing
|
||||
else* — no `EV_REL`/`REL_WHEEL`, no `EV_ABS`, no resolution. The `och1970`
|
||||
is a Hall latch, so the driver quantises the magnet into clicks and hands
|
||||
userspace one key press each. There is no sub-detent position to read
|
||||
without a kernel driver change, so every bit of nuance has to be derived
|
||||
from the *timing* between detents (see `WheelScroll`).
|
||||
- Scroll wheel = input device `och1970_holl_key` → **KEY_UP / KEY_DOWN**,
|
||||
mapped by the stock `Generic.kl` (checked 2026-09-11 via `dumpsys input`)
|
||||
to **`KEYCODE_DPAD_UP` / `DPAD_DOWN`** — not volume. Side/PTT button sits
|
||||
on `mtk-kpd` with KEY_VOLUMEDOWN + KEY_POWER. Headset exposes
|
||||
KEY_PLAYPAUSE, so a MediaSession gets media keys for free.
|
||||
- USB: set to come up as **`mtp,adb`** whenever the screen is unlocked
|
||||
(`adb shell svc usb setScreenUnlockedFunctions mtp`, 2026-09-11), so the
|
||||
gvfs mount sync uses and the adb install uses are both there at once.
|
||||
+2
-2
@@ -209,6 +209,6 @@ push onto any Android device or emulator with `adb push`. That tree includes
|
||||
the awkward cases on purpose — an emoji album name, a 208-character title, two
|
||||
tracks that differ only by id, a track with no artist.
|
||||
|
||||
## Board
|
||||
## Device facts
|
||||
|
||||
`TASKS.md` in this directory.
|
||||
`DEVICE.md` in this directory: screen density, wheel keycodes, USB modes.
|
||||
|
||||
@@ -1,120 +0,0 @@
|
||||
# andTunes — task board
|
||||
|
||||
A music player for the Rabbit R1 that never scans anything: LinTunes writes
|
||||
`Music/andTunes/library.json` during sync and the app just reads it. See
|
||||
`../andTunes spec.txt` for the original brief.
|
||||
|
||||
Legend: `[ ]` todo · `[~]` in progress · `[x]` done.
|
||||
|
||||
**Status: phases 1–3 done (2026-09-11, LinTunes rounds 50–51, andTunes
|
||||
0.2.0); 0.2.1 (LinTunes round 54, 2026-09-14) fixed the grouping crash —
|
||||
`Library.group()` keyed albums case-folded but artists raw-case, so one artist
|
||||
spelled two ways on one album ("RJD2" / "Rjd2") took the whole library down
|
||||
with `Couldn't read library.json`. Both maps fold case now, and `Artist` has a
|
||||
`key` the way `Album` always did. 0.2.2 (round 55, 2026-09-14) reversed the
|
||||
wheel in lists and made it scroll smoothly (`WheelScroll` — a pixel debt paid
|
||||
off per frame, not a row per detent); volume on the other screens is
|
||||
unchanged.** What's left is the *Parked* list, pulled in
|
||||
as trav finds he needs it. Built with
|
||||
`python3 andtunes/build.py` — plain Java, no Gradle, nothing downloaded (see
|
||||
`README.md`, *Toolchain*). 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
|
||||
the app sees a 480 × 640 **dp** canvas on a 2.88" screen (~278 real ppi).
|
||||
- **One dp is about half its usual physical size here.** Double every stock
|
||||
value: body text 28–32 sp, list rows ≥ 88 dp, menu tiles ~213 dp. A 48 dp
|
||||
row would be 4.4 mm tall. Do not "fix" this by changing the device density —
|
||||
trav likes it where it is.
|
||||
- Android 13 / API 33, `gsi_r1-userdebug`, arm64-v8a, 65 GB free.
|
||||
- **The wheel can only ever give us detents.** `getevent -pl` (2026-09-14):
|
||||
`och1970_holl_key` advertises `KEY (0001): KEY_UP KEY_DOWN` and *nothing
|
||||
else* — no `EV_REL`/`REL_WHEEL`, no `EV_ABS`, no resolution. The `och1970`
|
||||
is a Hall latch, so the driver quantises the magnet into clicks and hands
|
||||
userspace one key press each. There is no sub-detent position to read
|
||||
without a kernel driver change, so every bit of nuance has to be derived
|
||||
from the *timing* between detents (see `WheelScroll`).
|
||||
- Scroll wheel = input device `och1970_holl_key` → **KEY_UP / KEY_DOWN**,
|
||||
mapped by the stock `Generic.kl` (checked 2026-09-11 via `dumpsys input`)
|
||||
to **`KEYCODE_DPAD_UP` / `DPAD_DOWN`** — not volume. Side/PTT button sits
|
||||
on `mtk-kpd` with KEY_VOLUMEDOWN + KEY_POWER. Headset exposes
|
||||
KEY_PLAYPAUSE, so a MediaSession gets media keys for free.
|
||||
- USB: set to come up as **`mtp,adb`** whenever the screen is unlocked
|
||||
(`adb shell svc usb setScreenUnlockedFunctions mtp`, 2026-09-11), so the
|
||||
gvfs mount sync uses and the adb install uses are both there at once.
|
||||
|
||||
## Phase 1 — plays a song (v0.1.0) ✔
|
||||
|
||||
- [x] `java-21-openjdk-devel` — installed, `javac` on PATH. (Java 25 is also
|
||||
present; `build.py` points at 21.)
|
||||
- [x] Android SDK: platform 33 + build-tools 34.0.0 were already in
|
||||
`~/Android/Sdk`. **No Gradle, no Kotlin** — `build.py` runs aapt2 →
|
||||
javac → R8 → zipalign → apksigner directly (trav, 2026-09-11: don't
|
||||
pull a GB over cell if it isn't needed; it isn't).
|
||||
- [x] Project, package `me.teafry.andtunes`, minSdk 26 / targetSdk 33,
|
||||
framework only: `Activity`, `ListView`, `MediaPlayer`, `MediaSession`,
|
||||
`Notification.MediaStyle`, R8. The APK is 61 KiB.
|
||||
- [x] `MANAGE_EXTERNAL_STORAGE` + a first-run screen that sends the user to
|
||||
the toggle (LinTunes' installer grants it over adb, so it rarely shows).
|
||||
- [x] Library loader: `android.util.JsonReader` streaming parse off the main
|
||||
thread into a process singleton; artists/albums grouped in one pass;
|
||||
an unknown `format` gets a readable message; reloads when
|
||||
`library.json` changes (a re-sync) the next time the menu resumes.
|
||||
- [x] `MenuActivity`: six tiles, 2 × 3, **loads nothing**. The sixth reads
|
||||
"Now Playing" or "Shuffle All" depending on the service.
|
||||
- [x] `ListActivity` in songs mode + "Shuffle all" header row.
|
||||
- [x] `PlaybackService` (foreground) + `NowPlayingActivity`: white
|
||||
background, square art, slim scrubber, ◀ ⏯ ▶, shuffle, repeat.
|
||||
**No back button.**
|
||||
- [x] Audio focus, ACTION_AUDIO_BECOMING_NOISY, MediaSession + notification.
|
||||
- [x] End of queue **pauses** unless repeat is on. Repeat: off / all / one.
|
||||
- [x] `adb shell am start -W`: **313–338 ms** cold to the menu (551 ms on the
|
||||
very first launch after install, which includes dex verification).
|
||||
|
||||
## Phase 2 — the rest of the screens ✔
|
||||
|
||||
- [x] Playlists, artists (→ "All songs" + albums; all songs has its own
|
||||
shuffle), albums, search (artists, albums and songs as you type).
|
||||
- [x] Album art thumbs in album rows, full art on now playing.
|
||||
- [x] The "Now playing" bar at the bottom of every list screen; the playing
|
||||
song is drawn inverted in any list it appears in.
|
||||
- [x] Scroll wheel → list scrolling (one row per detent), volume on the menu
|
||||
and now-playing screens. Taken in `dispatchKeyEvent`, DPAD *and*
|
||||
VOLUME codes, down *and* up, so the search box can't swallow it.
|
||||
- [x] Replaceable button images: `Buttons/<name>.png` beats the bundled
|
||||
drawable, for the six menu tiles plus ◀ ▶ ⏯. LinTunes ships the
|
||||
defaults into `Buttons/` only where the file is missing.
|
||||
- [x] Missing file (synced then deleted) skips with a toast, doesn't stop
|
||||
the queue.
|
||||
- [x] Resume last queue + track + position on launch (paused).
|
||||
|
||||
## Phase 3 — install from LinTunes, counts coming back
|
||||
|
||||
- [x] `Connections → Install andTunes on Rabbit…` (LinTunes 0.19.0):
|
||||
`adb install -r` + the two permission grants; says Install / Update /
|
||||
Reinstall from the version on the device; with no adb, copies the APK
|
||||
to `Download/` over MTP for a manual tap. APK committed at
|
||||
`lintunes/android/andTunes.apk` (+ `andTunes.json`, its version).
|
||||
- [x] Format gate (LinTunes 0.20.0): sync reuses
|
||||
`export/web_support.conversion_for` outright, because Android's
|
||||
MediaPlayer decodes the same set a browser does. Protected AAC is
|
||||
refused and reported; ALAC, AIFF and anything else unplayable becomes
|
||||
**FLAC** (never a lossy re-encode). Conversions are cached per track, so
|
||||
they're converted once and size-diffed after that.
|
||||
- [x] Play counts back (andTunes 0.2.0): a natural finish counts a play, as
|
||||
it does in LinTunes. The app keeps `plays/andtunes-<install id>.json` in
|
||||
the per-machine-totals shape of `storage/play_journal.py`. Sync folds it
|
||||
into `<data_dir>/plays/` with the per-track max *before* copying
|
||||
anything, so the R1 is one more machine in the Round 38 model and there
|
||||
are no new merge rules.
|
||||
- [x] Retired `Sync Playlist to Rabbit (Auxio)` from the menu. (trav's R1
|
||||
has no old `Music/<Playlist>/` folders, so no cleanup was needed.)
|
||||
|
||||
## Parked
|
||||
|
||||
- Gapless, crossfade, EQ, ratings, folder browsing, sleep timer, Android Auto.
|
||||
- Deleting the old per-playlist folders automatically. They're outside the
|
||||
andTunes root and the containment guard makes them unreachable on purpose;
|
||||
removing them should be an explicit, separately-confirmed action.
|
||||
Reference in New Issue
Block a user