Container

Container is where the media playback is represented to the end user. It can be a PlayerView (ExoPlayer UI component), or it can be a FrameLayout that will contain the PlayerView which plays the content. The plan is to make Container an abstraction and allow really flexible representation of the media playback. For example, having an ImageView as the container for an audio playback, or a floating window to contain a picture-in-picture Video playback.

Playable

Playable is an object that maintains the actual media playback logic. A Playable can be played on different Container. If there is no Container to play on, a Playable should be scheduled for destruction.

Once bound to a Container, there is a Playback (see next) created to allow the client and the library to control the Playable.

A Playable must be managed by up to one PlayableManager at a time. The scenarios when there is no such PlayableManager are right before the Playable is bound to a Container, and right after the current binding (if exists) is destroyed. In the later, the Playable should be scheduled for destruction.

Playback (or the Binding of Playable & Container)

A Playback is an object represents the binding of a Playable to a Container. A Playback holds a reference to a Playable, but it must be aware that the Playable can be rebound to a different Container any time. Once that happens, the Playback must not access the Playable, and it is likely to be removed soon.

Lifecycle of a Playback

When the Playback is initiated, it is CREATED.

The Playback is ADDED after the Bind request is executed successfully.

When the Container becomes available (e.g. a View is attached to the Window), the Playback is ATTACHED. It is DETACHED otherwise.

When the Container is active (e.g. a View has at least one pixel visible to the end user), the Playback is ACTIVATED. It is DEACTIVATED otherwise.

If the Playback is unbound, it will be removed from the manager. In this moment, it is REMOVED.

Binding to a Container

The Binding request must be executed only if the Lifecycle that hosts the Container is Started, and the Container itself is available. In case of a View container, it is when the View is attached to the Window:

suspend fun Lifecycle.bind(container: Any) {
    awaitStarted()
    container.awaitAvailable()
    val request = requests.remove(container)
    request.onBind()
}

At most one request is bound to a Container at a time.