Building a video player app in Android (Part 3 / 5)

With ExoPlayer, Playlists, MediaSession, Audio Focus, PIP

The goal of this series of articles is to get you started with using ExoPlayer to build a simple, but feature rich, video player app (that supports playlists, MediaSession, Audio Focus, and Picture in Picture).

This is the third part of a 5 part series that includes:

The previous article in the series covered the steps needed to create playlists by concatenating MediaSources, along details on customizing the ExoPlayer’s playback control UI. This article gets into the details of using the MediaSession connector extension to seamlessly use MediaSession capabilities in the app.

MediaSession Overview

MediaSession allows information in the player (which is running your media app) to be exposed to other parts of the Android OS.

  • Things like whether the player is currently paused, or playing, or what media is loaded are all broadcast via the MediaSession to other parts of the OS, to other apps or system components. This is how what’s currently playing and it’s playback state is sent to a MediaStyle notification, or to a Bluetooth device that’s interested. Or even the Google Assistant.
  • Other apps and the OS can also manipulate the player (running in your app) via MediaController.TransportControls that are bound to the MediaSession. This allows media buttons on Bluetooth headphones, the Google Assistant, and even other apps to be able to play, pause, or skip songs in a media app.

In summary, a MediaSession is used for two things: publishing metadata about the current media being played in your app, and allowing other components (the OS and other applications) to control playback (in your app). Metadata is used by Bluetooth devices, Android Auto and Wear, and even the Google Assistant. Playback control is used by the the same things as metadata, in addition to the the OS (so that button presses of wired headsets can be translated to commands that your app can use).

For more details on MediaSession here are some resources.

Using MediaSession Connector Extension

Make sure to add the gradle dependency to your build.gradle file.

ext { ver = “2.7.0” }
dependencies {

Then you can create the session and attach it to the player (in your VideoActivity) as shown below.

Note on Java varargs and Kotlin — ExoPlayer and the MediaSession connector extension are written in Java, and the setPlayer() method accepts varargs as the 3rd parameter. When calling this method from Kotlin, if you don’t pass a 3rd argument, then everything works as you would expect when using Java. However, if you pass null as the 3rd argument then this will thrown a NullPointerException. If you do have a list of arguments that you would like to pass, then you have to use the spread operator * in order to pass a list of arguments to the Java varargs parameter. Here’s an example of this.

val uriList = mutableListOf<MediaSource>()
val mediaSource = ConcatenatingMediaSource(*uriList.toTypedArray())

Doing this will enable the basic set of playback actions (ACTION_PLAY_PAUSE, ACTION_PLAY, ACTION_PAUSE, ACTION_SEEK_TO, ACTION_FAST_FORWARD, ACTION_REWIND, ACTION_STOP). This means the connector will be able to handle these actions that are generated via MediaSession from media buttons, such as those in your Bluetooth headphones which allow pause, play, and fast forward, and rewind.

However, if you want to provide control over the playlist then you also have to tell the connector to handle the ACTION_SKIP_* commands by adding a QueueNavigator. Here’s an example of what this might look like.

Using this example you can also change the code that creates your ExoPlayer and it’s playlist, so that the playlist is loaded from this same ‘MediaCatalog’ list object. The code to load the playlist might now look like this.

You can also tell the connector that you want to handle other MediaSession actions in addition to the ones described above. You can find more info about each of the following in this medium article.

  • MediaSessionConnector.PlaybackPreparer — This allows you to handle actions like ACTION_PREPARE_FROM_SEARCH and ACTION_PREPARE_FROM_URI which is required if you wanted to integrate with Android Auto or Google Assistant. For more information on allowing the Google Assistant to control your media app via MediaSession, please refer to this article on
  • MediaSessionConnector.PlaybackController — This allows you to handle basic playback controller actions such as ACTION_PLAY_PAUSE and ACTION_SEEK_TO. This is optional, and if you don’t use this to intercept such calls, a DefaultPlaybackController is used.
  • MediaSessionConnector.QueueEditor — This allows you to handle ACTION_SET_RATING and other MediaSessionCompat.FLAG_HANDLES_QUEUE_COMMAND commands. TimelineQueueEditor is an implementation of this that works together with a DynamicConcatenatingMediaSource.
  • MediaSessionConnector.CustomActionProvider — Handles any custom actions that you want to provide in your app such as providing a way to control the repeat mode in your app.

Source code on GitHub

By following these 5 articles, you should be able to create a video player app that is similar to this sample we have created (which you can get from Android Studio as well).

Resources for further learning


MediaSession, Audio Focus


Picture in Picture