diff --git a/demos/compose/src/main/java/androidx/media3/demo/compose/MainActivity.kt b/demos/compose/src/main/java/androidx/media3/demo/compose/MainActivity.kt index 472b4e32fd..85423664bc 100644 --- a/demos/compose/src/main/java/androidx/media3/demo/compose/MainActivity.kt +++ b/demos/compose/src/main/java/androidx/media3/demo/compose/MainActivity.kt @@ -114,17 +114,21 @@ private fun MediaPlayerScreen(player: Player, modifier: Modifier = Modifier) { val presentationState = rememberPresentationState(player) val scaledModifier = Modifier.resizeWithContentScale(contentScale, presentationState.videoSizeDp) + // Only use MediaPlayerScreen's modifier once for the top level Composable Box(modifier) { + // Always leave PlayerSurface to be part of the Compose tree because it will be initialised in + // the process. If this composable is guarded by some condition, it might never become visible + // because the Player will not emit the relevant event, e.g. the first frame being ready. PlayerSurface( player = player, surfaceType = SURFACE_TYPE_SURFACE_VIEW, modifier = scaledModifier.noRippleClickable { showControls = !showControls }, ) - if (!presentationState.showSurface) { - // hide the surface that is being prepared behind a shutter - // TODO: picking scaledModifier here makes the shutter invisible - Box(modifier.background(Color.Black)) + if (presentationState.coverSurface) { + // Cover the surface that is being prepared with a shutter + // Do not use scaledModifier here, makes the Box be measured at 0x0 + Box(Modifier.matchParentSize().background(Color.Black)) } if (showControls) { diff --git a/libraries/ui_compose/src/main/java/androidx/media3/ui/compose/state/PresentationState.kt b/libraries/ui_compose/src/main/java/androidx/media3/ui/compose/state/PresentationState.kt index e043ec851a..89c72d1d34 100644 --- a/libraries/ui_compose/src/main/java/androidx/media3/ui/compose/state/PresentationState.kt +++ b/libraries/ui_compose/src/main/java/androidx/media3/ui/compose/state/PresentationState.kt @@ -57,8 +57,9 @@ fun rememberPresentationState(player: Player): PresentationState { * [Density.toPx]. Note that for cases where `pixelWidthHeightRatio` is not equal to 1, the * rescaling will be down, i.e. reducing the width or the height to achieve the same aspect ratio * in square pixels. - * @property[showSurface] set to true when the Player emits [Player.EVENT_RENDERED_FIRST_FRAME] and - * reset to false on [Player.EVENT_TRACKS_CHANGED] depending on the number and type of tracks. + * @property[coverSurface] set to false when the Player emits [Player.EVENT_RENDERED_FIRST_FRAME] + * and reset back to true on [Player.EVENT_TRACKS_CHANGED] depending on the number and type of + * tracks. * @property[keepContentOnReset] whether the currently displayed video frame or media artwork is * kept visible when tracks change. Defaults to false. */ @@ -67,7 +68,7 @@ class PresentationState(private val player: Player) { var videoSizeDp: Size? by mutableStateOf(getVideoSizeDp(player)) private set - var showSurface by mutableStateOf(false) + var coverSurface by mutableStateOf(true) private set var keepContentOnReset: Boolean = false @@ -86,7 +87,8 @@ class PresentationState(private val player: Player) { } } if (events.contains(Player.EVENT_RENDERED_FIRST_FRAME)) { - showSurface = true + // open shutter, video available + coverSurface = false } if (events.contains(Player.EVENT_TRACKS_CHANGED)) { maybeHideSurface(player) @@ -112,17 +114,17 @@ class PresentationState(private val player: Player) { player.isCommandAvailable(Player.COMMAND_GET_TRACKS) && !player.currentTracks.isEmpty if (!shouldKeepSurfaceVisible(player)) { if (!keepContentOnReset && !hasTracks) { - showSurface = false + coverSurface = true } if (hasTracks && !hasSelectedVideoTrack()) { - showSurface = false + coverSurface = true } } } private fun shouldKeepSurfaceVisible(player: Player): Boolean { // Suppress the shutter if transitioning to an unprepared period within the same window. This - // is necessary to avoid closing the shutter (i.e hiding the surface) when such a transition + // is necessary to avoid closing the shutter (i.e covering the surface) when such a transition // occurs. See: https://github.com/google/ExoPlayer/issues/5507. val timeline = if (player.isCommandAvailable(Player.COMMAND_GET_TIMELINE)) player.currentTimeline