Search code examples
androidandroid-jetpack-composeandroid-media3

How I can use Android Media3 with Jetpack Compose?


The official documentation says that the best way to implement it is to separate MediaLibraryService and MediaController.

The official examples show how to get a MediaController and MediaBrowser for use inside an Activity:

val sessionToken = SessionToken(context, ComponentName(context, PlaybackService::class.java))
val controllerFuture = MediaController.Builder(context, sessionToken).buildAsync()
controllerFuture.addListener({
  // MediaController is available here with controllerFuture.get()
}, MoreExecutors.directExecutor())

I just can't find any examples of using the Jetpack Compose. The solution, which is to pass MediaController as a parameter of the parent function to all child elements, seems to me very bad.

Is it possible to use a MediaController instance and pass it possibly through a ViewModel to all the places I need?


Solution

  • You can have your MediaController instance inside your ViewModel. To create the MediaController future, you just need a SessionToken and a context.

    If you make your ViewModel extend AndroidViewModel, you can access the app context inside it.

    For example:

    class MyViewModel(application: Application) : AndroidViewModel(application) {
    
       lateinit var mediaControllerFuture: ListenableFuture<MediaController>
    
        fun onStart(sessionToken: SessionToken) {
            if (mediaControllerFuture.get().connectedToken != sessionToken) {
                MediaController.releaseFuture(mediaControllerFuture)
                val context = getApplication<Application>()
                mediaControllerFuture = MediaController.Builder(context, sessionToken).buildAsync()
                mediaControllerFuture.addListener({ setupPlayer() }, ContextCompat.getMainExecutor(context))
            }
        }
    }
    

    Then, on your composables you just need a MyViewModel instance, which you can get using the viewModel<MyViewModel>() composable.

    It probably doesn't solve the problem of passing the instance to your other composable. Maybe it masks it, as you can call the viewModels<MyViewModel>() everywhere to receive the same instance.

    Another option is to use the CompositionLocal API to add the MediaController to your composition tree. Make sure you get familiar with this API before using it. But I guess adding a CompositionLocal is not so harmful as replacing the foundation ones.