The Google TV emulator was launched by hand with -no-snapshot, which forces
a cold boot every time. tests/helpers/emulator.sh boots from the AVD's
default_boot snapshot (~7s) and refreshes that snapshot on stop, so the next
run stays fast. It also wraps the two things a test run needs afterwards:
installing an APK and pulling a screenshot into tests/runs/.
CONTEXT.md's example dialogue still described Xtream credentials entered
through the Remote, which ADR-0005 and ADR-0007 replaced with device flow and
phone-side onboarding.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
The club chooser was a flat list of 56 names in a generic picker
dialog, which is unreadable from a couch. It now has its own dialog
with a crest per row, the short label on the right, and section
headers per division. Enabled clubs in the settings card show their
crest too.
A search field would have been the obvious alternative, but text
entry on a remote is exactly what the Ten-Foot rule rules out, so
grouping plus crests carries the recognition instead.
Also adds tests/demo, a generator for neutral playlist and EPG data
so screenshots never carry real channel names.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
A three-hour window was empty most of the day, which is exactly when
someone wants to know whether their club plays tonight. The scan now runs
until midnight, never less than three hours, so a late-evening question
still covers the rest of the night.
A channel can carry several of the club's broadcasts in one evening — a
preview at 18:20 and the match at 21:45 on the same station. The row names
the next one and appends "+n weitere" instead of silently hiding the rest,
so the list stays a channel list where one click means one station.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
The club shortcut now offers every club of the first three German
divisions instead of two hard-coded ones. Up to three can be active at
once; benjamin and tobiasb still start with Hansa and the VfB switched on.
Also fixes a crash on the left jump out of the channel list: the jump
targeted a FocusRequester bound to the selected rail row, and a row that
is scrolled out of a LazyColumn is not composed, so requesting focus on it
threw. Jumps are now attempted and fall back to normal focus movement.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Adds an optional rail entry per club that lists every channel whose EPG
mentions the club within the next three hours, with the matching kick-off
instead of the usual Now/Next. Only sport and free-to-air groups are
scanned (NFL excluded), so the scan stays cheap and quiet.
Which clubs appear is a per-TV setting: the signed-in viewer's own club is
on by default, and every club can be switched on or off under Einstellungen
-> Vereinsmenüs, so a household can follow more than one.
Crests are trademarks, so the icon is a plain shield in the club colours.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Control server security and robustness:
- Socket read timeout, handshake deadline and a client cap so an idle or
hostile connection can no longer pin threads forever (#1)
- Per-address rate limiting that counts every failed hello, Origin
checking on the upgrade, and a separate revocable session token for
code-authenticated clients so the guessable path no longer yields the
QR credential (#2)
- Credentials excluded from cloud backup and device transfer, constant
time comparisons, and a pairing reset in the settings (#3)
- Playlist fetches restricted to http(s), capped at 24 MB and bounded by
an overall transfer deadline (#4)
- Port conflicts and MediaSession id collisions no longer crash the app;
the remote degrades to unavailable with a plain-language note (#11)
Player:
- Seeking no longer collapses to position 0 when the duration is unknown
(#5)
- Pause acts on playWhenReady, so pausing during a stall works and
playback cannot resume in the background after leaving the app (#6)
- Playback failures stay on screen with a retry action instead of
silently dropping back to the list (#7)
- Reconnects are spaced 1s/3s/8s and re-entering the channel just closed
waits out a short grace period, which is what the provider needs to
release the previous session (#12)
Channel list and remote:
- Leaving playback returns to the channel the viewer came from (#13)
- The remote only rebuilds its list when the data changed, never
overwrites a focused input and carries indices instead of scanning (#8)
- Pairing retry reconnects properly, resets its backoff and validates the
code before spending an attempt (#9)
- M3U parsing keeps commas in names, strips a BOM and rejects payloads
that are not playlists, covered by unit tests under tests/ (#10)
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>