Blog Infos
Author
Published
Topics
, , , ,
Author
Published

 

Media3’s 1.6.0 version introduced a new module media3-ui-compose, which provides state classes linked to the player, that we can use to build compose-based user interfaces (UIs).

Here is a list of state classes available for developing custom UIs based on them. 👇

https://developer.android.com/media/media3/ui/compose

 

Current state of Android Development — Player integration
  • To integrate player-related functionality in our compose-based applications, we need AndroidView + PlayerView

 

@Composable
fun ExoPlayerComposable(
    videoUri: String,
) {
    val context = LocalContext.current
    val lifecycleOwner by rememberUpdatedState(LocalLifecycleOwner.current)
    val lifecycle = lifecycleOwner.lifecycle
    
    val exoPlayer = remember {
        ExoPlayer.Builder(context)
            .build()
            .also { exoPlayer ->
                val mediaItem = MediaItem.Builder()
                    .setUri(videoUri)
                    .build()
                exoPlayer.setMediaItem(mediaItem)
                exoPlayer.prepare()
                exoPlayer.addListener()
            }
    }
    DisposableEffect(key1 = lifecycleOwner) {
        val observer = LifecycleEventObserver { _, event ->
            when (event) {
                Lifecycle.Event.ON_PAUSE -> {
                    exoPlayer.pause()
                }

                Lifecycle.Event.ON_RESUME -> {
                    exoPlayer.play()
                }

                else -> {}
            }
        }
        lifecycle.addObserver(observer)
        onDispose {
            exoPlayer.release()
            lifecycle.removeObserver(observer)
        }
    }

    AndroidView(
        factory = {
            PlayerView(context).apply {
                resizeMode = AspectRatioFrameLayout.RESIZE_MODE_FIT
                player = exoPlayer
            }
        },
        modifier = Modifier
            .fillMaxWidth()
            .aspectRatio(aspectRatio)
    )
}

 

Using new state classes — Implementation + Demo 🧑‍💻
  • PlayerSurface — It is a Composable that represents a dedicated drawing surface for showing videos 🎥

Connects to Player, but does not contain playback controls.

  • State classes — provide different states, such as play, pause, next, and previous ⏭, ⏮, ⏯
Implementation

 

@Composable
fun ExoPlayerComposableBasedOnNewModule(
    lifecycle: Lifecycle,
    lifecycleOwner: LifecycleOwner,
    videoUri: String,
) {
    val context = LocalContext.current
    var showControls by remember { mutableStateOf(true) }

    val exoPlayer = remember {
        ExoPlayer.Builder(context)
            .build()
            .also { exoPlayer ->
                val mediaItem = MediaItem.Builder()
                    .setUri(videoUri)
                    .build()
                exoPlayer.setMediaItem(mediaItem)
                exoPlayer.prepare()
            }
    }
    DisposableEffect(key1 = lifecycleOwner) {
        val observer = LifecycleEventObserver { _, event ->
            when (event) {
                Lifecycle.Event.ON_PAUSE -> {
                    exoPlayer.pause()
                }

                Lifecycle.Event.ON_RESUME -> {
                    exoPlayer.play()
                }

                else -> {}
            }
        }
        lifecycle.addObserver(observer)
        onDispose {
            exoPlayer.release()
            lifecycle.removeObserver(observer)
        }
    }

    Box(modifier = Modifier.fillMaxSize()) {

        PlayerSurface(player = exoPlayer)

        if (showControls) {
            PlayPauseButton(exoPlayer)
        }
    }
}

 

⏯️ Play/Pause button UI based on The PlayPauseButtonState

@Composable
fun PlayPauseButton(player: Player, modifier: Modifier = Modifier) {
    val state = rememberPlayPauseButtonState(player)
    val icon = if (state.showPlay) Icons.Default.PlayArrow else Icons.Default.Pause
    val contentDescription =
        if (state.showPlay) stringResource(R.string.lbl_play_state) else stringResource(
            R.string.lbl_pause_state
        )
    val graySemiTransparentBG = Color.Gray.copy(alpha = 0.1f)
    val btnModifier =
        modifier
            .size(100.dp)
            .background(graySemiTransparentBG, CircleShape)
    Row(
        modifier = modifier.fillMaxWidth(),
        horizontalArrangement = Arrangement.SpaceEvenly,
        verticalAlignment = Alignment.CenterVertically,
    ) {
        IconButton(
            onClick = state::onClick,
            modifier = btnModifier,
            enabled = state.isEnabled
        ) {
            Icon(icon, contentDescription = contentDescription)
        }
    }
}

PlayPauseButtonState — class gives us two properties, isEnabled and showPlay, that we can use in our code to update the content description and UI state of the Play/Pause button ⏯

Limitations
  • This is just the beginning of first-class support for developing Media3-related features in Android applications based on compose UIs
  • Currently, 🛑 Previews don’t work —

 

@Preview
@Composable
private fun PreviewMyVideoPlayer() {
    val player  = ExoPlayer.Builder(LocalContext.current).build().apply {
        setMediaItem(MediaItem.fromUri("uri.mp4"))
        playWhenReady = true
        prepare()
    }
    PlayPauseButton(player = player)

}

 

Job Offers

Job Offers

There are currently no vacancies.

OUR VIDEO RECOMMENDATION

,

Reimagining Android Dialogs with Jetpack Compose

Traditional Android dialogs are hard to test, easy to leak, and painful to customize — and in a world of Compose-first apps, they’re overdue for an upgrade.
Watch Video

Reimagining Android Dialogs with Jetpack Compose

Keith Abdulla
Staff Android Engineer, Design Systems
Block

Reimagining Android Dialogs with Jetpack Compose

Keith Abdulla
Staff Android Engine ...
Block

Reimagining Android Dialogs with Jetpack Compose

Keith Abdulla
Staff Android Engineer, D ...
Block

Jobs

  • 🛑 Player instantiation fails during previews
  • So I hope we get these sorts of issues fixed in the upcoming release of this module.
Demo 🎥
Demo — : Play/Pause State

 

  • In this article, we only covered the PlayPauseButtonState . Feel free to check the complete sample code for other states in this repository 👇
References

This article was previously published on proandroiddev.com.

Menu