Spec: Phase 7 — Offline downloads: into the cache the consumer opened, resumable, on a battery-aware schedule

Open
#237 0 comments 0 reactions 0 assignees View on GitHub

Maintainers usually reply within 1 day

Nobody has claimed this yet.

Assessment

Difficulty
5/5
Estimated time
Over a week
Newbie friendliness
18/100
Issue type
Feature
Clarity
Mostly clear
Activity status
Active
Tech stack
android, kotlin
Domain
mobile-dev

Research direction

This is an epic with no code change yet; begin with child #238, the ADR, reading ADR-0010, ADR-0012, PRD.md §3.5 and docs/modules.md for the dependency rule. Code starts after the harness prefactor #239, then the tracer bullet #240, which downloads clear content into the ContentKeyedCache in superplayer-offline and plays it through setMediaRequest. Done means the download plays with zero network requests and can be removed.

Written by the indexing model from the issue text.

Description

ready-for-agent

Problem Statement

Phase 6 made protected content play and made an offline licence something a consumer can acquire, hold, renew and release. Nothing persists the media: superplayer-offline is an empty placeholder, and PRD.md §3.5's download stack is unbuilt.

  • The storage is ready and nobody writes to it. ADR-0010 built a pinned region into ContentKeyedCache "so that Phase 7's downloads have somewhere eviction cannot reach", and records a standing obligation that superplayer-offline shares that cache and its key policy rather than opening a second one.
  • The licence half is ready and bound to nothing. ADR-0012 closes on the matching obligation: Phase 7's downloads read OfflineLicenceStore and its playback-versus-licence expiry split rather than keeping their own.
  • F4's battery requirement has no home. Background downloads draining battery is one of PRD.md Part 1's named problems; §3.5 answers it with WorkManager constraints (UNMETERED, battery not low, storage not low), and none exist.
  • The module's declared dependency is core alone, while the work needs the cache (phase 4) and, for protected content, the DRM module (phase 6). Both are allowed by docs/modules.md's rule; which of them a clear-only app pays for is a decision, not a default.

Solution

Ship superplayer-offline (PRD.md §3.5) on the seams earlier phases built, as Phases 3–6 shipped theirs.

One cache, still. A download writes into the ContentKeyedCache the consumer opened, under the same content keys a player reads, pinned for as long as the download exists. A player plays downloaded content through setMediaRequest with no network, because the bytes are where it already looks.

Storage and scheduling stay the consumer's. The consumer names the directory and the budget (ADR-0010 rule 1); the DownloadService subclass, its manifest entry and the foreground-service permissions are the app's (ADR-0007 rule 4's shape). What is correctness — resuming rather than refetching, stopping on a lapsed constraint rather than failing, failing one item on a full disk rather than the queue — is on for everyone.

The licence follows the download. A protected download acquires its offline licence as part of the download, plays with zero licence requests, reports an expired licence before playback, and releases it on delete.

Nothing here is testable unless the harness can drive a download first. Media3's DownloadManager loads on threads of its own, through a data source the harness does not own, and WorkManager constraints have no statement in DeviceStatement. That machinery is a prefactor, and it comes first.

Scope

# Issue What it lands
#238 ADR-0013 Module dependencies, the public surface, one cache, constraints as correctness or policy, the licence binding
#239 Harness downloads A download over the harness transport and clock; process death and WorkManager constraints stated
#240 Tracer bullet Download clear content, play it with zero requests, remove it
#241 Track selection A sensible rendition, declared audio languages and subtitles, not the whole ladder
#242 Resumable Across process death and network loss, without refetching what is held
#243 WorkManager policy Unmetered, battery not low, storage not low; survives reboot
#244 Storage full One item fails typed; the queue carries on
#245 Licence binding Acquire with the download, play offline, expiry before playback, release on delete
#246 Demo The consumer's recipe: service, permissions, a downloads screen
#247 Phase exit Process death, network loss, reboot and the battery profiler, on a device

Order: the ADR and the harness first; they gate everything. The clear-content download is the tracer bullet, and the rest hang off it. Each child names its blockers.

Exit criteria

Per PRD.md Part 4:

  • A download survives process death, network loss, and reboot.
  • Battery policy verified with a battery profiler.
  • No Media3 @UnstableApi type in the module's public API, and a player built without it registers and allocates nothing.
  • ./gradlew assemble check stays green.

Non-goals

  • A second cache or a second key policy. ADR-0010 settled where downloaded bytes live.
  • A second licence store. ADR-0012 settled where an offline licence lives.
  • Scheduling renewal. ADR-0012 rule 10: the library reports and the consumer calls.
  • Moving a benchmark number. Download throughput and battery cost are reported here; tuning them is Phase 10's.
  • TV-specific download UX. superplayer-tv is Phase 8.
Dominant language
Kotlin
Stars
1
Forks
0
Avg merge
3h 32m
Merged PRs (30d)
64

Getting set up

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. Open a pull request that references the issue number.

More from ramesh130/superplayer

All issues in ramesh130/superplayer

Similar issues

More Kotlin issues

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.