ci(android): install libdbus, cache cargo and the staged libmpv

Every Android run since August failed in :app:buildRustHost: the host
build of pstr-android reaches libdbus-sys through keyring's Secret
Service store, and the runner had no libdbus-1-dev. With no successful
run, nothing was ever cached, so each run rebuilt from nothing.

- libdbus-1-dev in both jobs
- one shared, save-on-failure Rust cache for both jobs
- the staged libmpv tree cached on the build script's hash; the script
  stamps its output with the revision and its own hash and skips the
  libmpv build when the stamp matches

PLAN.md records the Android parity and UI work that follows.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
nvrlandClaude Opus 5.5 committed 2026-10-03 03:06:53 +02:00
1 parent 55ee412a1b
commit cf065fc5fe
3 files changed
+200 -4

No files matched your search

+34 -4
View File
@@ -94,8 +94,9 @@ jobs:
run: | run: |
sudo apt-get update sudo apt-get update
sudo apt-get install --no-install-recommends \ sudo apt-get install --no-install-recommends \
autoconf automake build-essential cmake curl git gperf libtool \ autoconf automake build-essential cmake curl git gperf libdbus-1-dev \
meson nasm ninja-build pkg-config python3 unzip wget xz-utils yasm libtool meson nasm ninja-build pkg-config python3 unzip wget xz-utils \
yasm
- name: Install Android SDK and NDK - name: Install Android SDK and NDK
run: >- run: >-
@@ -109,7 +110,21 @@ jobs:
toolchain: '1.96' toolchain: '1.96'
targets: aarch64-linux-android,x86_64-linux-android targets: aarch64-linux-android,x86_64-linux-android
# One cache for both jobs: the signed build compiles the same crates for
# the same targets. Saved on failure too, so a run that breaks late does
# not throw away everything it compiled before the break.
- uses: Swatinem/rust-cache@v2.9.1 - uses: Swatinem/rust-cache@v2.9.1
with:
shared-key: android
cache-on-failure: true
# libmpv and its dependency tree are the slowest part of the build, and
# depend only on the pinned revision and the build script, which stamps
# its output with both and skips the build when the stamp matches.
- uses: actions/cache@v6
with:
path: android/app/build/generated/mpv
key: libmpv-${{ hashFiles('scripts/build-libmpv-android.sh') }}
- uses: gradle/actions/setup-gradle@v4 - uses: gradle/actions/setup-gradle@v4
with: with:
@@ -167,8 +182,9 @@ jobs:
run: | run: |
sudo apt-get update sudo apt-get update
sudo apt-get install --no-install-recommends \ sudo apt-get install --no-install-recommends \
autoconf automake build-essential cmake curl git gperf libtool \ autoconf automake build-essential cmake curl git gperf libdbus-1-dev \
meson nasm ninja-build pkg-config python3 unzip wget xz-utils yasm libtool meson nasm ninja-build pkg-config python3 unzip wget xz-utils \
yasm
- name: Install Android SDK and NDK - name: Install Android SDK and NDK
run: >- run: >-
@@ -182,7 +198,21 @@ jobs:
toolchain: '1.96' toolchain: '1.96'
targets: aarch64-linux-android,x86_64-linux-android targets: aarch64-linux-android,x86_64-linux-android
# One cache for both jobs: the signed build compiles the same crates for
# the same targets. Saved on failure too, so a run that breaks late does
# not throw away everything it compiled before the break.
- uses: Swatinem/rust-cache@v2.9.1 - uses: Swatinem/rust-cache@v2.9.1
with:
shared-key: android
cache-on-failure: true
# libmpv and its dependency tree are the slowest part of the build, and
# depend only on the pinned revision and the build script, which stamps
# its output with both and skips the build when the stamp matches.
- uses: actions/cache@v6
with:
path: android/app/build/generated/mpv
key: libmpv-${{ hashFiles('scripts/build-libmpv-android.sh') }}
- uses: gradle/actions/setup-gradle@v4 - uses: gradle/actions/setup-gradle@v4
with: with:
+150
View File
@@ -0,0 +1,150 @@
# Android plan
Where the Android client stands against the desktop app, and what to change to
make it feel like a modern streaming app rather than a settings form with a
player attached. Written 2026-10-03 from a source review and four screenshots
(Library, Title, Shares, Settings) taken on a phone running v0.5.0.
Android last caught up with the desktop on 2026-08-10 (`4ef6225`); the desktop
gained about twenty features on 2026-10-02 that Android does not have.
## 0. CI (done, unverified)
Every `Android` workflow run since August failed in `:app:buildRustHost`:
`pstr-core` pulls `keyring` → `dbus-secret-service` → `libdbus-sys`, which needs
`libdbus-1-dev` on the host. Because no run ever succeeded, no cache was ever
saved, so each run started from nothing.
- `libdbus-1-dev` added to both jobs' apt install.
- `Swatinem/rust-cache` shares one key between the two jobs and saves on failure.
- `android/app/build/generated/mpv` is cached on the hash of
`scripts/build-libmpv-android.sh`; the script writes a `STAMP` (revision plus
its own hash) last and skips the whole libmpv build when the stamp matches.
Verify with `gh workflow run android.yml -f signed_release=true`, then run it a
second time and check that the libmpv step finishes in seconds.
## 1. Desktop parity
Ordered by value on a phone.
1. **Open share links from outside the app.** The manifest has only the
launcher intent. Add an `ACTION_VIEW` filter for `drive.proton.me/urls/*` and
an `ACTION_SEND` (`text/plain`) filter; both open the add-share dialog with
the link filled in. Desktop: `4a54af3`, `53a1d0a`.
2. **Streaming cache budget.** Android runs on the fixed 4 GiB default
(`DiskCacheConfig::DEFAULT_BUDGET_BYTES`), large for a phone. Expose
`DiskCache::set_budget` through `pstr-android`, add a 1–16 GiB choice with a
usage readout under Storage. Desktop: `a432a93`.
3. **Hardware-decoding switch.** `pstr_mpv.cpp` hard-codes
`hwdec=auto-copy-safe`; a device with green frames has no way out. A
Settings toggle, applied from the next file.
4. **Episode list in the player.** A bottom sheet (portrait) or side panel
(landscape) with the title's episodes, opened on the playing season and
scrolled to the playing episode. Desktop: `7e734db`.
5. **Long-press menu on tiles.** Play/resume, mark watched or unwatched,
download, change match, remove from Continue watching, in a
`ModalBottomSheet`. Desktop: `c5808a5`.
6. **Undo on snackbars** for marking a title and leaving Continue watching.
`AppUiState.message` is a bare string today; it needs an action. Desktop:
`79c3f68`.
7. **History tab.** Needs a bridge call over the `pstr-core::library` query
the desktop page uses. Desktop: `e7eeaa5`.
8. **Buffered range on the seek bar** from mpv's `demuxer-cache-time`.
9. **Up-next card** with the next episode's still and a countdown ring instead
of "Up next in 7s" text.
10. **Featured banner** at the top of the library. Desktop: `7d03736`.
11. **Bundled Inter** in `ui/theme/Type.kt`, so both clients read the same.
Desktop: `558446b`.
## 2. UI recommendations
### Everywhere
- **Colours stay the theme's.** The red is the Persona theme's accent and the
card tint comes from the shared palette, so the redesign changes layout and
button hierarchy, not colours.
- **One button hierarchy.** At most one filled accent button per screen;
secondary actions tonal or outlined; destructive actions behind a menu and a
confirmation, never a filled red button in a list row.
- **Edge-to-edge, per-screen titles.** The "proton-stream" app bar on every
page wastes a band of height. Library uses a collapsing large title; Title
pages draw the backdrop under the status bar; other tabs use their name.
- **Skeletons instead of spinners** for library and title loading, and
generated placeholder art (gradient from the accent plus the title's
initials) instead of a black card with a play triangle (Akira).
- **Pull-to-refresh** (`PullToRefreshBox`) replaces the top-bar refresh icon,
with crawl progress shown in the indicator area.
- **Split `ui/ProtonStreamApp.kt`** (1665 lines) into one file per screen
before the work below; every item touches it.
### Library
- Poster grid (2:3, `posterUrl`) at three columns on a phone, title under the
poster with no card body. The current 210 dp cropped backdrops fit two titles
per screen.
- Continue watching: 16:9 still with the progress bar laid over the bottom
edge of the image and "S03E11 · 12 min left" under it.
- Search becomes an icon in the app bar that expands into an M3 `SearchBar`,
instead of a permanent field above everything.
- Fix "1 episodes" (line ~507; the season header already pluralises).
### Title page
- Full-bleed backdrop with a gradient scrim, poster, title and meta chips
(year, score, genres) over it, collapsing as the page scrolls.
- Synopsis clamped to three lines with "More".
- One full-width primary button, "Resume S03E11" or "Play S01E01", then a row
of icon actions: download, mark watched, and a ⋮ menu holding Start over,
Change match and More on AniList. Today five buttons of three styles wrap
over three lines.
- Drop the "Preferred tracks" form and its Save button from this page. The
player already remembers tracks by name per title (`d3c5f07`); expose any
override from the player's track chooser.
- Season picker as a dropdown chip or tabs, not stacked accordions; a 40
episode title is one long scroll today.
- Episode rows: 16:9 still with progress overlay, "1 · Mother and Children"
(provider name, else "Episode 1") rather than the raw file name
`[Judas] Oshi no Ko - S01E01.mkv`, runtime or air date as supporting text,
watched and download state as small trailing icons. The red `S01E01` label
above each row goes.
### Shares
- `ListItem` rows: name, "12 titles · crawled 2 h ago" or the failure, and a ⋮
menu with Refresh, Re-enter link and Remove (confirmed). The current row
wraps "Custom password stored securely" over three lines beside three
differently styled actions.
- "Add share" as an extended FAB, which is also where the share-link intent
from parity item 1 lands.
### Settings
- Grouped `ListItem` rows (headline, one line of supporting text, trailing
switch), with the long explanations moved into the supporting line or an
info dialog. Today each toggle carries a paragraph.
- Split into sub-pages once storage and decoding settings land: Playback,
Downloads & storage, Appearance, Metadata, About.
- Language fields become a picker dialog over a language list, not free text.
- Storage shows a usage bar (cache, offline episodes) above the budget choice.
### Robustness
- Screenshot tests (Roborazzi, JVM, no device) for each screen in light and
dark, phone and tablet, at font scale 1.0 and 1.5 — the redesign changes
every screen, and `docs/TESTING.md` has no visual layer today.
- 48 dp minimum touch targets and content descriptions on the new icon-only
actions.
- Tablet: `ListDetailPaneScaffold` for library + title, since
`NavigationSuiteScaffold` already switches to a rail there.
- Shared-element transition from a poster to its title page, and predictive
back.
## Suggested order
1. CI green (section 0).
2. Split `ProtonStreamApp.kt`; add screenshot tests for the current screens.
3. Parity 1–3 (small, phone-specific fixes).
4. Button hierarchy and Inter — every later screen inherits them.
5. Title page, then Library, then Shares and Settings.
6. Parity 4–10.
+16
View File
@@ -7,6 +7,20 @@ source_dir="${PSTR_MPV_SOURCE_DIR:-$repo_root/.build/mpv-android}"
revision=20a3fa526fac6d3fe267aee0d4c349893fee65a3 revision=20a3fa526fac6d3fe267aee0d4c349893fee65a3
destination="$repo_root/android/app/build/generated/mpv" destination="$repo_root/android/app/build/generated/mpv"
# The staged tree depends only on the pinned revision and on this script, which
# also pins the NDK. A tree stamped with both is reused as is: that is what lets
# CI restore it from a cache instead of rebuilding FFmpeg and every other
# dependency from source on each run.
stamp="$revision $(sha256sum "${BASH_SOURCE[0]}" | cut -d' ' -f1)"
if [[ "$(cat "$destination/STAMP" 2>/dev/null)" == "$stamp" &&
-f "$destination/jniLibs/arm64-v8a/libmpv.so" &&
-f "$destination/jniLibs/x86_64/libmpv.so" &&
-f "$destination/mpv-android-source-$revision.tar.gz" ]]; then
echo "libmpv $revision is already staged in $destination"
exit 0
fi
rm -f -- "$destination/STAMP"
if [[ ! -d "$source_dir/.git" ]]; then if [[ ! -d "$source_dir/.git" ]]; then
git clone https://github.com/mpv-android/mpv-android.git "$source_dir" git clone https://github.com/mpv-android/mpv-android.git "$source_dir"
fi fi
@@ -191,3 +205,5 @@ tar \
-czf "$destination/mpv-android-source-$revision.tar.gz" \ -czf "$destination/mpv-android-source-$revision.tar.gz" \
. .
printf '%s\n' "$revision" > "$destination/REVISION" printf '%s\n' "$revision" > "$destination/REVISION"
# Last, so an interrupted run never leaves a stamp over a partial tree.
printf '%s\n' "$stamp" > "$destination/STAMP"