Commit Graph
204 Commits
Author SHA1 Message Date
nvrl 046cfe62b8 fix(android): paint the accent button's gradient inside its container
The gradient sat on the outer modifier, which Material pads out to the
48dp minimum touch target, so the primary button drew 8dp taller than
the 40dp tonal button beside it on the library hero.
2026-10-04 16:45:25 +02:00
nvrl e3dcbec1f2 chore(version): bump to 1.1.1
Patch, not minor. Nothing a viewer uses changes: the Android build moves to
NDK r30 and mpv-android 687c7fd, which brings newer harfbuzz, fribidi,
unibreak, fontconfig, libxml2 and curl into the bundled libmpv. The catalog
schema and on-disk data are unchanged from 1.1.0.
v1.1.1
2026-10-04 02:54:21 +02:00
nvrl 99abc8f205 build(android): move to NDK r30 and mpv-android 687c7fd
Upstream mpv-android moved its build to NDK r30 (30.0.16248370), and each
mpv-android revision pins its own NDK, so the two move together. The new
revision also updates harfbuzz to 14.5.0, unibreak to 8.0, fribidi to
1.0.17, libxml2 to 2.15.4, fontconfig to 2.18.3 and curl to 8.22.0, and
builds libass with assembly on x86_64 (nasm, already installed in CI).

r30 brings an LLVM update with codegen and LTO fixes and sysroots up to
API 37. It is also the NDK current SDK managers install, so a fresh setup
no longer needs r29 installed beside it.

The script change invalidates the cached libmpv tree, so CI rebuilds it
once. The staged libraries are the same set as before, all 16 KiB aligned.
2026-10-04 02:42:53 +02:00
nvrl 3638e0e5df feat(core,app,android): sort, fold and shelve the Android library
Bring the phase 2 library to Android. Search, sorting and franchise folding
move from the desktop app into pstr_core::browse, so both clients find,
fold and order the same titles the same way.

The bridge gains AndroidEngine::arrangement, which returns the grid's tiles
and up to three shelves as keys into the library's titles. Each TitleRecord
now carries its display name, wide art, format, season, studios, directors,
tags, airing state and franchise. The library bar has a sort menu with the
franchise toggle, folded tiles show a +N badge, and shelves sit above the
grid. The title page shows the same facts, tags and franchise row as the
desktop. Settings -> Appearance gains Title names.

The default title name is now English, which is what Android always showed.
A folded franchise now sorts by its lead's displayed name rather than by its
alphabetically first member.
v1.1.0
2026-10-04 02:04:15 +02:00
nvrl aef182e7e8 chore(version): bump to 1.1.0
Minor, not patch. This release adds features and changes on-disk data:

- ani.zip episode names and fanart.
- Franchise grouping, shelves, poster grid and new sorts.
- Two catalog migrations (v9, v10). Catalogs are forward-only, so 1.0.x
  refuses to open a catalog 1.1.0 has upgraded.

The first match run after upgrading fetches every title's details and
episode list again. It refetches by id and does not search, so
hand-picked matches are kept.
2026-10-04 01:27:40 +02:00
nvrl 6853725ca0 feat(core,meta,app,android,cli): sort, group and shelve the library
The AniList query now keeps the start date, season, banner, popularity,
status, next airing episode, tags, main studios, director and story
relations, as TitleDetails in one JSON column (schema v10). Matches
stored without them are fetched again by id, never searched, so
hand-picked matches stay; refreshing the same entry keeps its episodes
and fanart. Enrichment records the entries it walked. node_first_seen
remembers when each file appeared, across recrawls.

pstr_core::franchise groups titles that share a provider id. The grid
folds each franchise into its lead with a +N badge, and the title page
lists the whole franchise in release order. pstr_core::shelves picks
rows by airing status, director, studio and genre. A row needs three
franchises, so one franchise's titles never make a row on their own.

Desktop:
- Covers are the default grid (Settings > Appearance > Library tiles).
- The hero and title band use fanart or banner decoded at 1920 px.
- New sorts: recently added, newest, highest rated, most popular.
- Titles can show the share's, English or romaji name; search matches
  all of them.

Android carries TitleDetails through a hand-picked match; its screens
are unchanged.
2026-10-04 01:26:21 +02:00
nvrl 4c80076762 fix(core,meta,app,android,cli): number episodes the way releases do
AniList's per-season lookup searched for "<title> 2nd Season" and the
scorer picked the base entry again, so Attack on Titan seasons 2-4 and
Jigokuraku season 2 were captioned with season one's names. Episode
names came from streamingEpisodes, which is partial and numbered however
a streaming site counted. Bleach S17 and Mushishi S02/S03 had none.

AniList now walks SEQUEL relations from the match and takes each
entry's episodes from ani.zip, which carries TVDB's season, number and
absolute number. An episode row keeps all three plus its place in the
AniList entry. EpisodeGuide::get matches whichever numbering a file
uses and never answers from another season.

Episodes and fanart are stored as one enrichment. The episode list is
replaced even when empty, and the enrichment is recorded as asked, so a
film is not re-asked every run. Storing a different match drops the old
entry's episode rows. AniList's 4.75:1 bannerImage is no longer stored
as the 16:9 backdrop; ani.zip's 1920x1080 fanart is used instead.

Schema v9 adds the columns and deletes the AniList episode rows and
banners. Matches are kept. Desktop, Android and the CLI now share
service::plan and MetadataService::work. The settings text names
ani.zip. Audit and roadmap in docs/METADATA.md; bug entry B61.
2026-10-04 00:47:41 +02:00
nvrl 52d27ec996 chore(version): bump to 1.0.1 v1.0.1 2026-10-03 23:00:34 +02:00
nvrl 66eb1b49a1 ci(android): keep the cached libmpv and Rust target trees
Gradle deletes the declared outputs of a task it has no history for, so
on every fresh checkout buildLibmpvAndroid threw away the libmpv tree just
restored from the cache and rebuilt it from source (~14 min per job). The
task no longer declares outputs; the script's STAMP decides.

rust-cache treats target/android/<triple> as a profile directory and
deletes its release tree before saving, so both cargo-ndk ABIs were
rebuilt from scratch on every run (~6 min per job). It now caches only the
registry and helpers; the target directory goes through actions/cache,
keyed on toolchain and lockfile and restoring from the last lockfile.
2026-10-03 23:00:19 +02:00
nvrl 22425e0bad fix(android): centre the launcher glyph optically in its mask 2026-10-03 23:00:19 +02:00
nvrl 50481e6aed fix(android): lint on the predictive-back opt-in and the shared-art modifier
enableOnBackInvokedCallback only exists from API 33 and the app runs from
31, which lint reports as UnusedAttribute; tools:targetApi says that is
intended, since older releases ignore it. sharedArt becomes an extension
on Modifier, as Compose lint asks of modifier factories.
v1.0.0
2026-10-03 14:53:09 +02:00
nvrl bedbe0ef52 fix(android): pin the version in the settings screenshot
The Settings index shows the installed version, read from the package,
so the committed image held 0.5.1 and the 1.0.0 bump failed the
screenshot check. SettingsScreen takes the version as a parameter that
defaults to the package's, and the test passes a fixed one.
2026-10-03 14:24:32 +02:00
nvrl 7914cf4dc1 chore(version): bump to 1.0.0 2026-10-03 13:35:36 +02:00
nvrl 39a7f3cc38 docs: PLAN.md status and the screenshot list after the second Android pass 2026-10-03 13:34:28 +02:00
nvrl 630f07a462 feat(android): library and title side by side on tablets, a poster that becomes the page, and predictive back
From 840 dp wide, the library and the open title share the window, so
moving between titles is one tap and the grid stays where it was; with
nothing open, the right pane says to choose one. On narrower windows, a
poster tapped in the grid grows into the title page's backdrop and
shrinks back on the way out.

The app opts into predictive back. Dragging back on a title page shrinks
the page as the gesture goes and restores it if the gesture is
abandoned.
2026-10-03 13:34:04 +02:00
nvrl 9cba54df14 feat(android): light-palette and tablet screenshots, and a title page that fits a tablet
The screenshot tests now also capture the library and a title in
Catppuccin Latte, and a title and the history page on a tablet. The
tablet capture showed the title page opening on nothing but its
backdrop: a 16:9 picture the width of a landscape tablet is taller than
the window. The backdrop is now no taller than about half the window,
and the rest of the page keeps a phone's reading width, centred.
2026-10-03 13:27:19 +02:00
nvrl 4ff8ae303c feat(android): Change match from a library tile's long press
The tile menu now offers the same provider search as the title page, so
a title wearing the wrong poster can be fixed from the grid that shows
it, without opening the title first.
2026-10-03 13:15:59 +02:00
nvrl 6c2ec83cb1 feat(android): a downloads page of rows under each show
Saved episodes are list rows with their still, the episode's name rather
than its file, and its size, under a heading per show; the page opens
with how much is on the device. Downloads still arriving come first, with
pause or resume and cancel as icons beside the progress. Deleting a saved
episode now asks first, since getting it back is a download.
2026-10-03 13:13:41 +02:00
nvrl 677ab2d928 feat(android): settings as a page per area, and a language picker
The first Settings page lists Playback, Downloads and storage,
Appearance, Metadata and About, each with a line saying where it stands,
and each opens its own page with a way back. On one long page, every
setting added pushed the rest further from reach.

Audio and subtitle language are chosen from a list of languages by name
instead of typed as "jpn or eng". The list stores the three-letter tag a
Matroska file carries, reads a stored tag in either ISO form, and still
takes any other tag typed in.
2026-10-03 13:07:11 +02:00
nvrl fb32d6c850 feat(android): per-page app bars, edge to edge, and search behind an icon
The single "proton-stream" bar over every page is gone. The library has a
large title that folds away as the grid scrolls, with search and refresh as
icons; search opens into a field in the bar that takes the keyboard at once,
and back closes and clears it. History, Shares, Downloads and Settings each
name themselves in a bar of their own, drawn in the page's background until
content scrolls under it. The title page now draws its backdrop under the
status bar, with the back button below it.

A search with no results says so instead of claiming the library is empty.
2026-10-03 12:56:03 +02:00
nvrl 110f97caf9 feat(android): generated placeholder art and a skeleton library
A title without artwork shows the accent as a gradient with its initials
instead of a black card with a play triangle, so a library of unmatched
titles is no longer a grid of identical cards. Art with the name already
over it, and episode stills, get the gradient without initials. While
artwork loads, and while the catalog first loads, the page shows pulsing
shapes where the posters will be rather than a spinner.

The screenshot tests turn artwork loading off, so whether a failed fetch
lands before the capture no longer decides what the image shows.
2026-10-03 12:45:34 +02:00
nvrl 0563c91b9e fix(PKGBUILD) v0.5.1 2026-10-03 12:39:17 +02:00
nvrl 3b9f4b549c chore(version) bumped to 0.5.1 2026-10-03 12:37:59 +02:00
nvrl d5c819ee94 docs: PLAN.md status after the Android parity and redesign pass 2026-10-03 05:25:45 +02:00
nvrl 153859a735 feat(android): a featured title at the top of the library
The desktop's rule: the title watched last, or with nothing part watched,
one of the titles the provider has a backdrop for, turning over once a
day. Large art fading to black behind the name, rating, genres and three
lines of synopsis, with Resume or Play and More info.
2026-10-03 05:25:33 +02:00
nvrl 1c61aadb22 feat(android): a history page
History sits second in the navigation, as on the desktop: every played
episode, newest first, under Today, Yesterday, a weekday or a date. The
still plays the episode, the text opens its title, and the cross takes
it off the page — forgetting its position and watched mark — with Undo.

The list is the desktop's Library::history rule over the records the
library already holds, so no bridge call is added; HistoryTest pins the
rule and the day headings.
2026-10-03 05:23:26 +02:00
nvrl 8d40b211ed feat(android,player): the title's episodes from inside the player
An Episodes button in the controls opens a sheet of the title's episodes,
scrolled to the one playing, each with its still, its name, and whether
it is playing, watched or part way. A tap switches to it the way
previous and next do, saving the outgoing position first.
2026-10-03 05:20:14 +02:00
nvrl 6229a59561 feat(android,player): an up-next card with the next still and a countdown ring
The card shows the next episode's still, or Proton's thumbnail of it, with
a ring over it that runs down to the switch and plays it at once on a tap.
The provider's episode name heads it where there is one. "Watch till the
end" stays under it.
2026-10-03 05:18:30 +02:00
nvrl cfd654b267 feat(android,player): the read-ahead on the seek bar
pstr_mpv observes demuxer-cache-time and hands it over with the rest of
the state, and the seek bar shades from the start to where the demuxer
has read, lighter than the played part. A jump that lands in the shaded
run costs no fetch, as on the desktop.
2026-10-03 05:17:03 +02:00
nvrl 0bc7cba428 feat(android): long-press menus on library tiles, and Undo
- a long press on a title opens a sheet: Play or Resume, Open, Download
  all, and Mark watched or unwatched for the whole title
- a long press on a Continue watching card offers Resume, Open, and
  Remove from Continue watching, which forgets the position as the
  desktop does
- marking a title and leaving Continue watching both say what they did
  in a snackbar with Undo, which puts every changed watch state back
- the "next up" episode is one shared function, used by the title page
  and the menu alike
2026-10-03 05:14:34 +02:00
nvrl 65ecc099b6 feat(android): settings as grouped rows
Playback, Downloads and storage, Appearance, Metadata and About, each a
group of rows with one line of explanation, and the whole row as the
target. The paragraphs under the switches are gone; what they said is
either the row's supporting line or a code comment.

Storage shows the cache's use against its budget as a bar over the
budget chips. Clearing the cache and deleting offline episodes are rows
that say how much they free, and the delete still asks first. Licence
and notices are rows under About.
2026-10-03 05:10:12 +02:00
nvrl 65a6ae9373 feat(android): shares as list rows with a menu, and Add as a button at the thumb
- each share is a row with its name and how it is unlocked; Refresh,
  Re-enter link and Remove sit behind a menu instead of three differently
  styled controls that squeezed the name into a column a word wide
- Remove asks first, saying that offline episodes and watch progress go
  with it
- Add share is an extended floating button, where the share sheet and a
  tapped link land too
- an empty page says what to do instead of showing a bare heading
2026-10-03 05:03:22 +02:00
nvrl 2fbbd0e090 feat(android): a poster library with pull to refresh
- titles are 2:3 posters three across on a phone, named under the poster
  instead of in a card body; cropped backdrops at full width fitted two
  titles to a screen
- a title's caption says "Film" or "1 episode" rather than "1 episodes",
  and wraps at large font scales instead of being cut to "2023 · 7"
- Continue watching cards carry the resume bar on the still and say how
  much is left: "S03E11 · 12 min left"
- search is a filled pill with a clear button
- pull to refresh crawls the shares, as the bar's refresh icon does
2026-10-03 04:58:35 +02:00
nvrl eee45bc8f8 feat(android): a title page built around one Play button
- the backdrop runs edge to edge and fades into the page, with the back
  button over it
- one full-width Play that names the episode it plays ("Resume S02E01")
- Start over, Download, Match and the provider link as one row of
  labelled icons instead of four buttons in three styles over three lines
- the synopsis is clamped to three lines and opens on a tap; the watched
  count joins the year, rating and genres
- seasons are chips showing one season at a time, opened on the season
  Play lands in, instead of stacked accordions
- episode rows head with "1. Mother and Children", or "Episode 1", rather
  than the filename; numbering, size and air date sit under it, and the
  resume bar lies on the still's lower edge
- the per-title track form is gone: the player's track chooser already
  stores each title's choice, and Settings holds the global default
2026-10-03 04:53:59 +02:00
nvrl 1c8df80c27 feat(android): Inter, the desktop's typeface
The type ramp now draws in Inter Regular and SemiBold, the same two files
the desktop bundles, so the clients read as one app. Inter is declared
above the ramp: top-level properties initialise in file order, and the
ramp built first would have captured it as null and silently used the
system face. Its OFL text ships in the licences beside the notices.
Screenshots re-recorded.
2026-10-03 04:48:21 +02:00
nvrl 5673a44310 fix(android): mark the share-link filter autoVerify="false"
Lint's AppLinkWarning failed the gate: a BROWSABLE https filter reads as an
App Link missing autoVerify. It can never be one, since drive.proton.me is
Proton's domain, so the attribute now says so.
2026-10-03 04:48:21 +02:00
nvrl c63f08ac91 test(android): screenshot tests for every screen
Roborazzi draws Library (phone, tablet, font scale 1.5, empty), a title,
Shares, Settings and Downloads from fixed records under Robolectric, and
build-android.sh check compares them with the committed images in
app/src/test/screenshots. The redesign changes every screen; a layout
regression now fails the gate and shows up in review as a changed image.
2026-10-03 04:34:46 +02:00
nvrl f2e69dc852 feat(android): streaming cache budget and a hardware decoding switch
- Settings > Storage chooses the streaming cache's budget, 1 to 16 GiB,
  and shows what it holds against it. The bridge gains
  set_stream_cache_budget, which the next connection is built with and
  an open one evicts down to at once. Android keeps the choice in its own
  settings, like the desktop keeps it in desktop.json, and tells the
  engine as it is created. Unset stays at the shared 4 GiB default.
- Settings > Playback can turn hardware decoding off for a device whose
  decoder shows green or torn frames. pstr_mpv sets hwdec before each
  loadfile, so it applies from the next episode.
2026-10-03 04:12:20 +02:00
nvrl 8a1807c262 feat(android): open share links from a browser or the share sheet
A drive.proton.me/urls/ link opened from a browser, or text containing
one shared to the app, switches to Shares and opens the Add form with
the link filled in, fragment included. A playing episode moves to the
mini transport instead of covering the form. A recreated activity does
not offer the same link again.

The view filter is not autoVerify, since the domain is Proton's, so
Android only routes browser links here once "Open supported links" is
allowed in the app's settings; sharing works without that.
2026-10-03 03:59:41 +02:00
nvrl 47b33cdb91 ci(android): keep the built libmpv when Gradle fails, OldTargetApi informational
- libmpv is built in its own step and saved with actions/cache/save right
  after, so a failure later in the job no longer discards a 12 minute
  build the way a post-job save did
- lint's OldTargetApi fails the gate whenever a new platform ships, like
  the dependency freshness checks already marked informational; raising
  targetSdk is a deliberate change with its own device pass
2026-10-03 03:55:07 +02:00
nvrl 2d7d895dda feat(app,android): a new icon, and a launcher label that fits
The icon follows proton-drive-linux's logo: a violet gradient tile with
a darker lower edge, and a white play mark with a soft shadow, cut into
the blocks a revision is fetched in. The SVG, the window PNG and the
Windows .ico are rendered from one source.

On Android it is an adaptive icon with a monochrome layer for themed
icons, and the notification glyph is the same mark. The app is named
"Proton Stream"; the launcher shows "Stream", since "proton-stream" was
cut to "proton-st…".
2026-10-03 03:45:50 +02:00
nvrl 0d33799199 refactor(android): one file per screen
ProtonStreamApp.kt had grown to 1665 lines holding every screen, and
every planned UI change touches it. The shell, Library, Title, Shares,
Downloads and Settings screens now each have their own file, plus
Common.kt for the empty state and byte formatting they share. Code is
moved unchanged; declarations used across files went from private to
internal.
2026-10-03 03:45:50 +02:00
nvrlandClaude Opus 5.5 2ac906cf37 ci(android): build libmpv with meson 1.12.1 from PyPI
The fontconfig that the pinned mpv-android revision builds requires
meson >= 1.11, and ubuntu-24.04's apt meson is 1.3.2, so buildLibmpvAndroid
failed configuring fontconfig.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-03 03:27:21 +02:00
nvrlandClaude Opus 5.5 cf065fc5fe 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>
2026-10-03 03:06:53 +02:00
nvrl 55ee412a1b ci(android): stop setup-android requesting the removed tools package 2026-10-03 02:53:16 +02:00
nvrl 2275df40a8 ci(android,release): publish the signed APK with each release 2026-10-03 02:45:13 +02:00
nvrlandClaude Opus 5.5 78ffafe21a ci(android): build the signed release on tags and on request
The signing secrets are now configured, so the job no longer needs to be
switched off. It runs for v* tags and for a manual dispatch with
signed_release set.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-03 02:06:59 +02:00
nvrl 6e09129d0c fixed font loading v0.5.0 2026-10-03 02:03:24 +02:00
nvrl 476fec8c04 chore(VER): bumped 0.5.0 2026-10-02 23:42:13 +02:00
nvrl 7af0969e38 fix(stream): fetch a block again after a retriable failure
The SDK retries a request that fails before its response starts, but not a
block body cut off part way through. That error stopped playback or failed
a download, and the SDK's new keep-alive pings make it the way a dead
connection now ends: seconds in, rather than at the storage timeout.

A block is now fetched up to three times, 250 ms and then 500 ms apart,
while ProtonError::is_retriable says a replay could succeed. A permanent
refusal still fails on the first attempt.
2026-10-02 23:42:12 +02:00