On 15 September 2026 the second commit in the Yonto Player repo was called "Add source-agnostic content layer with adapter pattern and unit tests". It came about an…

On 15 September 2026 the second commit in the Yonto Player repo was called "Add source-agnostic content layer with adapter pattern and unit tests". It came about an hour after the initial commit. Its message says the UI and view models depend only on a repository and the domain models, never on a concrete provider, and that a new integration is added by implementing the same interface. Seven weeks later that sentence is still true, and it is the rule the rest of the app leans on.

The first post in this series sketched the interface in three calls. This one goes underneath: what the models look like, how failure is spelled, what the interface costs, and how it makes the app easy to test. What the screens are allowed to know Everything above the interface works in a small set of plain Kotlin data classes: a category, a summary (a card), a detail (a title page), a playback option, a stream. None of them says where it came from.

The shape of a card, simplified: data class MediaSummary( val id: String, // the source's own id, opaque to the app val sourceId: String, val title: String, val type: MediaType = MediaType.MOVIE, val posterUrl: String? = null, val year: String? = null, ) Almost everything past title is optional with a default, because sources know different amounts. A media server knows cast, ratings and episode lists, and a folder of files knows a file name.

The models say "I may not know this" in the type, so a screen has to handle the absence once, in one place, rather than every screen branching on the kind of source. The one that took the most thought is how a title says what to play.

An option's source is a sealed type with four cases: a plain address that plays as it stands, a share the app opens itself, an opaque string only the source that issued it can redeem, and a file in the viewer's own storage. A sealed type makes every place that reads one decide what it does with each case, once, and the compiler checks that none was forgotten.

Source disclosure

This is a source-linked briefing. Read the original report from Dev Community .