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

, ,

Cutting-Edge-to-Edge in Android 15: Using Previews and Testing in Jetpack Compose to Manage Insets.

With the advent of Android 15, edge-to-edge design has become the default configuration. Consequently, applications must be capable of accommodating window insets, including the system status bar and navigation bar, as well as supporting drawing…
Watch Video

Cutting-Edge-to-Edge in Android 15: Using Previews and Testing in Jetpack Compose to Manage Insets.

Timo Drick
Lead Android developer
Seven Principles Mobility GmbH

Cutting-Edge-to-Edge in Android 15: Using Previews and Testing in Jetpack Compose to Manage Insets.

Timo Drick
Lead Android develop ...
Seven Principles Mob ...

Cutting-Edge-to-Edge in Android 15: Using Previews and Testing in Jetpack Compose to Manage Insets.

Timo Drick
Lead Android developer
Seven Principles Mobility ...

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