You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
I have integrated androidx.media3.cast.MediaRouteButton into my Compose UI. The problem is that my UI hides when user does not interact with it (traditional video controls). When user taps on a cast icon then cast dialog is shown. If user does not interact with it then video controls disappear including the dialog. Currently there is no way to tell if dialog is shown since showDialog it is hidden in lib function.
However, the problem was in how we set up the "disappearing" condition. See 31f6653 for the first commit. The controls would disappear 3 seconds after the pointer would be up, i.e. the user would stop interacting with the Player composable.
This works for all composables on the screen (e.g. ProgressSlider dragging, toggling ShuffleButton/RepeatButton/MuteButton and just non-stop touching the video surface). But since PlaybackSpeedButton opens a ModalBottonSheet with more speed controls to interact with (slider, +/- buttons, preset speeds), we don't inherit the pointer tracking from the parent node that we used to open this.
That means that when we are in that ModalBottomSheet, trying to pick a new speed, the 3 second timeout for fading the control already begins! And if the user is too slow, it will disappear right under the user's fingers.
The solution to this has been to expose the Modifier not just of the sheet, but the content of the sheet - and keep track of pointer input there - cfb6b45. That meant that we could add "interacting with the sheet" to "pointerDown" logic - 3bb3edf.
For this case, it would mean for MediaRouteButton to take in both modifier for the IconButton and then dialogModifier for the MediaRouteDialog that it will be "opening". That is, only if you need to be tracking the finger/stylus interaction.
So back to my original question - what, for you, determines that the controls should disappear?
I'm using composition local to force controls to appear. In my AutoHide composable I hold my autoHideState.
classAutoHideState {
var forceShow :Boolean by mutableStateOf(false)
}
@Composable
funrememberAutoHideState() = remember { AutoHideState() }
valLocalAutoHide= compositionLocalOf { AutoHideState() }
@Composable
funAutoHide(
dimColor:Color?,
timeout:Duration = 3.seconds,
enabled:Boolean = true,
state:AutoHideState = rememberAutoHideState(),
content: @Composable () ->Unit
) {
var displayControls by remember { mutableStateOf(true) }
var resetTimerTrigger by remember { mutableLongStateOf(0L) }
LaunchedEffect(resetTimerTrigger) {
if (displayControls) {
delay(timeout)
displayControls =false
}
}
LaunchedEffect(state.forceShow) {
// reset timer when controls can hide once againif (!state.forceShow) {
displayControls =true
resetTimerTrigger =System.currentTimeMillis()
}
}
val onInteraction = {
displayControls =true
resetTimerTrigger =System.currentTimeMillis()
}
val dimModifier =if (dimColor) {
Modifier.fillMaxSize().background(dimColor)
} else {
Modifier
}
Box(
Modifier.fillMaxSize().pointerInput(Unit) {
awaitEachGesture {
// 1. Wait for the initial touch down
awaitFirstDown(requireUnconsumed =false)
onInteraction()
// 2. While the finger is still touching/moving, keep resetting the timerdo {
val event = awaitPointerEvent()
val anyPressed = event.changes.any { it.pressed }
if (anyPressed) {
onInteraction()
}
} while (anyPressed)
}
}
) {
CompositionLocalProvider(LocalAutoHide provides state) {
Crossfade(displayControls ||!enabled || state.forceShow) { controls ->if (controls) {
Box(dimModifier) {
content()
}
}
}
}
}
}
Then I have a UI component that might trigger dialog. When a dialog is shown then I just update forceShow. This approach is pretty simple and does not support multiple forceShow requestors. AutoHideState can be adjusted accordingly (replace boolean with a counter).
@Composable
@OptIn(UnstableApi::class)
funCaptionsButton(
controller:MediaController,
modifier:Modifier = Modifier,
enabled:Boolean = true,
) {
val autoHide =LocalAutoHide.current
val state = rememberClosedCaptionsSelectorState(controller) // out of scope of this questionvar showSheet by rememberSaveable { mutableStateOf(false) }
LaunchedEffect(showSheet) {
autoHide.forceShow = showSheet
}
IconButton(
onClick = { showSheet =true },
enabled = state.captions.isNotEmpty() && enabled,
) {
Icon(
painter = painterResource(R.drawable.ic_player_cc),
contentDescription = stringResource(R.string.player_captions),
)
}
if (showSheet) {
val sheetState = rememberModalBottomSheetState()
// ...
}
}
Activity
AquilesCanta commentedon Apr 13, 2026
Hey thanks for reporting. We are tracking this internally in b/502148085. We'll do our best to share updates soon.
oceanjules commentedon Apr 14, 2026
Hi Michal,
While @AquilesCanta is looking at the issue, can I ask you how your choose to make the controls disappear?
We had a very similar problem in https://github.com/androidx/media/tree/release/demos/compose, but instead of a
Button-> opens ->Dialog, we have aButton-> opens ->BottomSheet.However, the problem was in how we set up the "disappearing" condition. See 31f6653 for the first commit. The controls would disappear 3 seconds after the pointer would be up, i.e. the user would stop interacting with the Player composable.
This works for all composables on the screen (e.g.
ProgressSliderdragging, togglingShuffleButton/RepeatButton/MuteButtonand just non-stop touching the video surface). But sincePlaybackSpeedButtonopens aModalBottonSheetwith more speed controls to interact with (slider, +/- buttons, preset speeds), we don't inherit the pointer tracking from the parent node that we used to open this.That means that when we are in that
ModalBottomSheet, trying to pick a new speed, the 3 second timeout for fading the control already begins! And if the user is too slow, it will disappear right under the user's fingers.The solution to this has been to expose the Modifier not just of the sheet, but the content of the sheet - and keep track of pointer input there - cfb6b45. That meant that we could add "interacting with the sheet" to "pointerDown" logic - 3bb3edf.
For this case, it would mean for
MediaRouteButtonto take in bothmodifierfor theIconButtonand thendialogModifierfor theMediaRouteDialogthat it will be "opening". That is, only if you need to be tracking the finger/stylus interaction.So back to my original question - what, for you, determines that the controls should disappear?
mroczis commentedon Apr 15, 2026
I'm using composition local to force controls to appear. In my
AutoHidecomposable I hold myautoHideState.Then I have a UI component that might trigger dialog. When a dialog is shown then I just update
forceShow. This approach is pretty simple and does not support multiple forceShow requestors.AutoHideStatecan be adjusted accordingly (replace boolean with a counter).Hoist the MediaRouteButton dialog state
oceanjules commentedon Jul 23, 2026
Hi @mroczis, please use MediaRouteButtonState