Commit Graph
3 Commits
Author SHA1 Message Date
KeelrLeek a44b4422fd android: the same SIGPIPE race, in the two focus checks it has not lost yet
deploy.sh and dev.sh both ask which app is on screen with
`echo "$FOCUS" | grep -qi`, under the same `set -o pipefail`. $FOCUS is one
line, so echo has always finished writing before grep -q exits and the race
has never been lost — but it is the identical shape that made verify-icon.sh
report a missing file in an APK that contained it. Herestrings, so there is no
writer to kill.
2026-09-15 07:48:09 -05:00
KeelrLeek 4ebc38d5f3 android: the falcon on the launcher — a real adaptive icon, with the monochrome layer
The phone has been wearing tauri's logo. `tauri android init` writes the
generated project's `res/` from its own template and never reads
`src-tauri/icons/android/`, which has carried the falcon since 50afa6a, and the
detour build runs cargo and Gradle directly — so nothing ever copied it across:
the two dominant colours in the packaged `mipmap-xxxhdpi/ic_launcher.png` are
`#24c8db` and `#ffc131`, tauri's own brand cyan and amber. `src-tauri/android/res/`
is the app module's res now, tracked, and `build.sh` and `dev.sh` copy the whole
tree into `gen/android` before Gradle, because that project is regenerated and
gitignored and an asset that only lives there is an asset that disappears.

It is a real adaptive icon: the kit's foreground and its **monochrome** layer at
five densities, the legacy mipmaps under it, and the background as one
`#FF05080B` colour resource rather than five identical flat PNGs, so the gradient
being mocked separately replaces one file. The kit's own XML could not ship — it
points every layer at `@drawable/` while shipping them as PNGs under `mipmap-*`,
and in the generated project two of those three references resolve to Android
Studio's template robot rather than failing — so the overlay's XML says
`@mipmap/` and `@color/`, and its colour file takes the same name tauri's does,
because two files declaring one colour is a hard aapt2 error.

Losing the copy would be silent — the APK still builds, still installs, and wears
the old logo with nothing in the log — so `verify-icon.sh` opens every APK both
scripts make and reads the resources back out: the two `mipmap-anydpi-v26` XMLs
present, and four PNG names at five densities byte for byte from the overlay. A
release APK keeps neither the bytes nor the names — AGP re-encodes every PNG and
shortens every resource path — so that path asks `aapt2 dump resources` what each
name points at and compares pixel dimensions instead; 72×72 at hdpi still tells
the kit's icon from tauri's 49×49. No walk step: a launcher icon is not reachable
from inside the app, and the masking and the themed layer want a minute with the
phone's home screen.
2026-09-15 07:22:00 -05:00
KeelrLeek bb828ff2dc android: the phone, on the real core — the mockup shell, playback, the media card, the catalog, the resolver, Save, the dev flavour
The same Tauri 2 project builds for Android: mobile.html as a third Vite
entry (src/mobile/, the mockup on real data), tauri.android.conf.json, the
mobile stubs (updater, lyric alignment, vocal separation), rustls reqwest
on Android, the AAudio buffer, and a hand-driven detour build
(src-tauri/android/{env,build,deploy,dev}.sh) while Developer Mode is off.
ANDROID.md is the recipe.

Playback: the media-session plugin crate (lock-screen card, MediaStyle
notification, audio focus, headset buttons, a foreground service that
promotes itself first — Android 14 kills the app otherwise), session
restore across reload and restart, solid chrome after measuring the
backdrop-filter lag.

The catalog: Search is the desktop's top bar (the top-result rule, artist
cards), the artist page and the record page are the desktop's, Home is the
desktop's landing page, Gaps per followed shelf. Playing goes through the
desktop's own source rules, moved verbatim from state.svelte.js into
src/lib/sources.js so both apps share one resolver; the phone readies the
next three tracks and crosses boundaries gapless. Save fetches a record or
a track into the library by the download rules; Transfers shows the
burst. The raw Soulseek search stays, offered on request, a peer's copy
streaming with the lane rising as the bytes arrive. Community is the
Soulseek side (rooms, chat, buddies); the private layer waits.

The opener and storage plugins (social links, the picked library folder,
all-files access), the volume normalization switch, and the harness:
tests/mobile/smoke.mjs walks the mock (a fake network behind
src/lib/events.js) and the real phone over CDP, perf.mjs measures frames.
2026-09-13 09:30:06 -05:00