This commit is contained in:
ponzischeme89
2026-08-22 15:32:51 +12:00
parent b3e3f62c54
commit 9858e44710
31 changed files with 1213 additions and 594 deletions
+32 -40
View File
@@ -1776,7 +1776,7 @@ preserve:
the search row instead of on "Off", which is the state the viewer is already in. Where
no provider can be asked it says that instead, once, rather than leaving somebody
looking for an option that is not there. `SubtitleMenuScreenshotTest` covers both.
- **Client-side it is a second screen, not a third section.** The drop-up is 344dp by about
- **Client-side it is a second screen, not a third section.** The drop-up is 440dp by about
a third of a 720p screen; stacking a track list, the size chips and search results
squeezed the tracks to one visible row. `SubtitleDownloadState.expanded` swaps
`player_subtitle_main_section` out, and Back steps out of the download half before it
@@ -1791,12 +1791,13 @@ preserve:
**Changing it mid-film is a drop-up**, `player_subtitle_overlay.xml`, anchored over the
subtitle button it opens from rather than the full-height panel it used to be. It sits on
somebody's film, so there is no scrim and the panel is near-black: the option under focus is
the only fill on it (accent green), the choice in force is a quiet grey plate with a green
label, and every other row has no background at all. The margins are measured off the
somebody's film, so there is no scrim and the panel is near-black. It uses the shared
`TvSettingsComponents` rows: focus is a raised control with a white ring, the choice in force
keeps an accent wash and a CURRENT badge after focus moves, and every colour comes from the
active Memby palette. Every other row has no background. The margins are measured off the
transport row — 44dp end, 112dp bottom to clear its 72dp strip and the controls' 28dp
padding — and the top margin is what caps the track list before its `ScrollView` (which
takes the overflow via `layout_weight`) starts scrolling instead of growing up the screen.
padding — and the top margin is what caps the track list before its `ScrollView` starts
scrolling instead of growing up the screen.
`bindSubtitleMenu` in `ui/player/SubtitleMenu.kt` fills it from plain
`SubtitleMenuEntry` lists so `SubtitleMenuScreenshotTest` can render the real menu with no
player, server or decoder → `build/screenshots/subtitles-menu/`; deciding *what* the entries
@@ -2104,42 +2105,33 @@ failure fallback; and the preroll player is stopped and parked at hand-off so it
hardware decoder while HEVC content plays. Returning to Home prepares the same instance again
in the next idle window rather than constructing one per title.
**The same clip plays behind the cold-start screen** (`ui/LaunchPreroll.kt`). It is the one
screen every launch shows and the clip was the one thing Memby owns that nobody ever saw
there. It borrows the *same* cached instance — no second decoder, no second copy of the
file — and hands it back on dispose, so the next playback still opens on a prepared player.
**It now gates the launcher**, which is the whole point of it: it was decoration, uncovered
whenever the app happened to be ready, and on a warm start that was a fraction of a second —
so the one thing Memby owns was in practice never seen. The clip plays once from the
beginning, its last frame is held for `LAUNCH_INTRO_HOLD_MS` (2s), and only then is the home
screen composed. Things to preserve: **it gates but it can never trap** — no player, a
decoder error, or no rendered frame within three seconds all report finished immediately,
and `LAUNCH_INTRO_MAX_MS` (9s, the clip's four plus the hold plus room for a weak box) is
`AppRoot`'s outer bound on top of that, because a viewer must never be held on a black
screen by a branding clip; **`LaunchIntro.played` is process-scoped, not saved**, so an
activity Android recreates behind somebody is not a second launch and a profile switch does
not replay it; the pulsing mark and the welcome line fade *out* under the clip and back in
if the app is still opening when it ends, since the waiting screen and a four-second sting
said two things at once; and it no longer loops — looping existed so a slow cold start never
froze on the last frame, which the hold and the fade-out do instead, and a clip that never
ends cannot gate anything. It is still muted, because it now runs on every single app open,
where the pre-roll before a programme is audible and must *end*`PrerollPreloader.acquire`
/`recycle` normalise volume and repeat mode so a borrower cannot leave the next one wedged.
It is acquired **after** `withFrameNanos`, because a cold start has nothing cached and
constructing an ExoPlayer inside the first composition of the screen that must appear
immediately is the cost the idle-handler prepare exists to avoid. And
`AppRoot` calls `MembyLoadingScreen` from **one** call site: the three states meaning "still
opening" were three, and Compose identifies a composable by where it is called from, so
moving between them disposed the screen and rebuilt it — which now means returning and
re-borrowing the player twice during the busiest stretch of a launch.
**Cold start is a local poster snapshot, never a live grid.**
`data/StartupPosterSnapshotCache.kt` takes a balanced sample of films and series from the
active viewer's Continue Watching, favourites, server-composed recommendation rows and
latest films *after* Home's refresh has settled. On `Dispatchers.IO` it asks Coil for bounded
220×330 poster requests, composes them into one 1280×720 angled wall and atomically writes a
small JPEG under `filesDir/startup_posters/`, keyed by server, Emby user and Memby viewer.
The next cold start performs only a file existence check and one asynchronous local decode;
no URL is ever handed to `StartupPosterBackdrop`, and no list of poster bitmaps exists in
composition. Snapshots refresh at most daily and the directory retains eight profiles.
**What it says while it is opening is `ui/WelcomeQuotes.kt`.** Twenty-five lines per tone
plus eight headlines, because this is the most-read copy in the app and five per tone meant
a household saw the same sentence roughly every fifth time they switched the set on.
The bundled `drawable-nodpi/startup_poster_fallback.png` is a rights-safe wall of fictional
artwork. `Theme.Memby.Launcher` draws it as MainActivity's window background before Compose
exists, while `MembyLoadingScreen` uses the same resource as Coil's placeholder and gently
crossfades a personalised file over it. One ten-second value supplies a tiny pan and scale
inside `graphicsLayer`, then holds its final frame; frame-rate reads invalidate the layer
rather than recomposing the wall. It has no focus targets and no launch hold: `AppRoot`
calls it from one opening call site and uncovers the next screen the instant update, session
and onboarding state allow it. The local video pre-roll remains playback-only; startup
never borrows its
decoder and never holds the launcher for branding.
**Welcome copy lives in `ui/WelcomeQuotes.kt`.** Twenty-five lines per tone, because five
per tone meant a household saw the same sentence roughly every fifth time it appeared.
`WelcomeQuotesTest` counts the distinct lines a pool yields: a pool that shrank back, or
gained a duplicate on a copy-paste, looks identical to one that did not. The headline is
kept apart from the quotes and is not keyed on the tone — it names what the app is *doing*,
and rerolling it when the settings flow arrives would change the line mid-launch.
gained a duplicate on a copy-paste, looks identical to one that did not. The greeting is
assembled separately so the authenticated name is trimmed once; the player also draws from
the same tone-specific pool while it prepares a programme.
**Next up / auto-advance.** 30 s before an episode ends, `PlayerActivity` slides up
`player_next_up_banner.xml` and rolls into the next episode when it reaches zero (Settings