Custom ExoPlayer Controls in Jetpack Compose
Add a custom controls UI overlay for ExoPlayer
Introduction
Whenever some form of media needs to be played, ExoPlayer is often the go-to library. There are a lot of advantages of going with this library as it is open sourced, well tested, and extensively battle proved by most of the big tech companies, to name a few.
The ExoPlayer UI view is in traditional XML, and to use it in a Jetpack Compose screen, we wrap it in an AndroidView composable. If you want to provide your custom controls UI, you can do so by overriding the exo_styled_player_control_view.xml
. You can also override the drawables as described here.
If you are a compose enthusiast and want to avoid the XML route, follow along!
What Are We Building?
By the end of this article, we will have a complete custom controls overlay that has the following:
- Video title at the top
- Center player controls
- Bottom seek bar with buffer percentage, video duration, and full-screen toggle button
Setting up the UI
Let’s create a simple player view that occupies the full screen.
By default, the controls overlay is as follows:
Let’s divide the controls UI into three parts:
- Top control — title
- Center controls — play/pause, forward/rewind
- Bottom controls — seek bar, full-screen toggle, and video time
The default player controls can be disabled with the help of the following method: player.setUseController(false)
.
Center Controls
We can use a simple Row composable for displaying the center controls.
Previewing the above composable gives the following result:
We want to show the pause icon if the video is playing and the play icon once paused. Also, as you can see, the onClick
for each of these controls is not doing anything. Let’s add these functionalities.
The forward and replay can be achieved by calling the seekBack()
and seekForward()
methods of the ExoPlayer. For the player to know by what duration it has to seek back/forward, you will have to set it using the setSeekBackIncrementMs(long)
and setSeekForwardIncrementMs(long)
methods on the ExoPlayer.Builder(..)
.
Bottom Controls
To implement the seek bar, we can make use of the Slider
composable. We can wrap the timer and full-screen button in a Row
composable and finally wrap all the bottom controls in a Column
composable.
Previewing the above composable gives the following result:
We must replace the “Total video duration” text with the total video time. To update the seek bar with the current video time, we can get the value with the help of Player.Listener
interface. This listener gives the updates for all the changes in the player. Finally, if the user moves the seek bar, we can get the value in onValueChange
lambda and seek the player to the required time with the help of player.seekTo(long)
method.
If you also want to show the buffer percentage, you can use another Slider
composable for it and wrap the seek bar and buffer bar in a Box
composable.
The onEvents(...)
callback is used to get the current player state. The player.getDuration()
method gives the total duration of the content being played in milliseconds, and player.getCurrentPosition()
method gives the current position of the content being played in milliseconds. Finally, the player.getBufferedPercentage()
method gives the percentage of content to which data is buffered.
Now that we have most of the controls ready, we still have a few more things to do. Firstly, we want to show the controls overlay only if the user clicks on the player. To achieve this, we can simply wrap the controls inside an AnimatedVisibility
composable. We can also add some enter/exit animations to make the transition smoother.
Running the code now gives the following result:
Important things to note:
- We should observe the lifecycle state of the fragment/activity/composable. We want to do this because we would not want to continue playing the media if it’s in a STOPPED state.
- The video quality in the GIF is lower than the actual app.
Where to go From Here?
Now that you have reached so far, I would like you to challenge yourself and try and implement the following:
- Adding next/previous video control — when playing videos from a playlist
- Update current video time as the video plays
- Full-screen toggle
You can get the complete code of the snippets in the article from here.
If you want to see how this can be used in a real-world app, check out this repo.
Want to Connect?You can also connect with me on LinkedIn. I would love to see your custom overlays.We at ShareChat are constantly working on making our apps better across all our clients: Android, iOS and Web. If you are interested in building ShareChat/Moj or solving interesting problems, let us know by applying here!
Additional Resources
- ExoPlayer docs for a better understanding of all the components
- ExoPlayer javadoc to dive deeper into the different classes/methods
- Android Developers website to learn more about Jetpack Compose
- Android Developers website for important Jetpack Compose performance gotchas
- Medium article to explore more on playing media with ExoPlayer and Jetpack Compose