From ef408cbf4c5f33fc22d43b528cf41fcfdf363c10 Mon Sep 17 00:00:00 2001 From: AbdallahMehiz Date: Sat, 14 Sep 2024 09:41:17 +0100 Subject: [PATCH] refactor: some player ui adjustments make enter animation faster make player controls buttons' clickable area larger add shadow to the seek gesture indicator (for bright videos) make controls fade after 4 seconds (was 3) add gradients at the top and bottom (better than darkening the entire screen) for the 5th or 6th time. fix crash caused by seekbar chapters not starting at 0 this should overall make using the player a smoother experience --- .../ui/player/controls/PlayerControls.kt | 94 +++++++++++++------ .../controls/components/ControlsButton.kt | 20 +++- .../ui/player/controls/components/Seekbar.kt | 14 ++- 3 files changed, 93 insertions(+), 35 deletions(-) diff --git a/app/src/main/java/live/mehiz/mpvkt/ui/player/controls/PlayerControls.kt b/app/src/main/java/live/mehiz/mpvkt/ui/player/controls/PlayerControls.kt index a4c0a5d..5e9152a 100644 --- a/app/src/main/java/live/mehiz/mpvkt/ui/player/controls/PlayerControls.kt +++ b/app/src/main/java/live/mehiz/mpvkt/ui/player/controls/PlayerControls.kt @@ -1,9 +1,10 @@ package live.mehiz.mpvkt.ui.player.controls import androidx.compose.animation.AnimatedVisibility -import androidx.compose.animation.animateColorAsState +import androidx.compose.animation.core.FastOutSlowInEasing import androidx.compose.animation.core.FiniteAnimationSpec import androidx.compose.animation.core.LinearOutSlowInEasing +import androidx.compose.animation.core.animateFloatAsState import androidx.compose.animation.core.tween import androidx.compose.animation.fadeIn import androidx.compose.animation.fadeOut @@ -43,7 +44,9 @@ import androidx.compose.runtime.setValue import androidx.compose.runtime.staticCompositionLocalOf import androidx.compose.ui.Modifier import androidx.compose.ui.draw.clip +import androidx.compose.ui.graphics.Brush import androidx.compose.ui.graphics.Color +import androidx.compose.ui.graphics.Shadow import androidx.compose.ui.platform.LocalLayoutDirection import androidx.compose.ui.res.stringResource import androidx.compose.ui.text.font.FontWeight @@ -103,14 +106,14 @@ fun PlayerControls( resetControls, ) { if (controlsShown && !paused && !isSeeking) { - delay(3_000) + delay(4_000) viewModel.hideControls() } } - val transparentOverlay by animateColorAsState( - Color.Black.copy(if (controlsShown && !areControlsLocked) 0.5f else 0f), - animationSpec = tween(500), - label = "", + val transparentOverlay by animateFloatAsState( + if (controlsShown && !areControlsLocked) .9f else 0f, + animationSpec = playerControlsExitAnimationSpec(), + label = "controls_transparent_overlay", ) GestureHandler() CompositionLocalProvider( @@ -124,7 +127,15 @@ fun PlayerControls( ConstraintLayout( modifier = modifier .fillMaxSize() - .background(transparentOverlay) + .background( + Brush.verticalGradient( + Pair(0f, Color.Black), + Pair(.2f, Color.Transparent), + Pair(.7f, Color.Transparent), + Pair(1f, Color.Black), + ), + alpha = transparentOverlay, + ) .padding(horizontal = MaterialTheme.spacing.medium), ) { val (topLeftControls, topRightControls) = createRefs() @@ -151,8 +162,12 @@ fun PlayerControls( } AnimatedVisibility( isBrightnessSliderShown, - enter = slideInHorizontally(playControlsAnimationSpec()) { it } + fadeIn(playControlsAnimationSpec()), - exit = slideOutHorizontally(playControlsAnimationSpec()) { it } + fadeOut(playControlsAnimationSpec()), + enter = slideInHorizontally(playerControlsEnterAnimationSpec()) { it } + fadeIn( + playerControlsEnterAnimationSpec(), + ), + exit = slideOutHorizontally(playerControlsExitAnimationSpec()) { it } + fadeOut( + playerControlsExitAnimationSpec(), + ), modifier = Modifier.constrainAs(brightnessSlider) { end.linkTo(parent.end, spacing.medium) top.linkTo(parent.top) @@ -162,8 +177,12 @@ fun PlayerControls( AnimatedVisibility( isVolumeSliderShown, - enter = slideInHorizontally(playControlsAnimationSpec()) { -it } + fadeIn(playControlsAnimationSpec()), - exit = slideOutHorizontally(playControlsAnimationSpec()) { -it } + fadeOut(playControlsAnimationSpec()), + enter = slideInHorizontally(playerControlsEnterAnimationSpec()) { -it } + fadeIn( + playerControlsEnterAnimationSpec(), + ), + exit = slideOutHorizontally(playerControlsExitAnimationSpec()) { -it } + fadeOut( + playerControlsExitAnimationSpec(), + ), modifier = Modifier.constrainAs(volumeSlider) { start.linkTo(parent.start, spacing.medium) top.linkTo(parent.top) @@ -177,7 +196,7 @@ fun PlayerControls( mpvVolume = mpvVolume, range = 0..viewModel.maxVolume, boostRange = if (boostCap > 0) 0..audioPreferences.volumeBoostCap.get() else null, - displayAsPercentage = displayVolumeAsPercentage + displayAsPercentage = displayVolumeAsPercentage, ) } @@ -192,8 +211,8 @@ fun PlayerControls( } AnimatedVisibility( currentPlayerUpdate != PlayerUpdates.None, - enter = fadeIn(playControlsAnimationSpec()), - exit = fadeOut(playControlsAnimationSpec()), + enter = fadeIn(playerControlsEnterAnimationSpec()), + exit = fadeOut(playerControlsExitAnimationSpec()), modifier = Modifier.constrainAs(playerUpdates) { linkTo(parent.start, parent.end) linkTo(parent.top, parent.bottom, bias = 0.2f) @@ -222,8 +241,8 @@ fun PlayerControls( } AnimatedVisibility( visible = (controlsShown && !areControlsLocked || gestureSeekAmount != null) || isLoading, - enter = fadeIn(playControlsAnimationSpec()), - exit = fadeOut(playControlsAnimationSpec()), + enter = fadeIn(playerControlsEnterAnimationSpec()), + exit = fadeOut(playerControlsExitAnimationSpec()), modifier = Modifier.constrainAs(playerPauseButton) { end.linkTo(parent.absoluteRight) start.linkTo(parent.absoluteLeft) @@ -243,7 +262,9 @@ fun PlayerControls( Utils.prettyTime(abs(gestureSeekAmount!!.second)), Utils.prettyTime(gestureSeekAmount!!.first + gestureSeekAmount!!.second), ), - style = MaterialTheme.typography.headlineMedium, + style = MaterialTheme.typography.headlineMedium.copy( + shadow = Shadow(Color.Black, blurRadius = 5f) + ), fontWeight = FontWeight.Bold, textAlign = TextAlign.Center, ) @@ -266,8 +287,10 @@ fun PlayerControls( } AnimatedVisibility( visible = (controlsShown || seekBarShown) && !areControlsLocked, - enter = slideInVertically(playControlsAnimationSpec()) { it } + fadeIn(playControlsAnimationSpec()), - exit = slideOutVertically(playControlsAnimationSpec()) { it } + fadeOut(playControlsAnimationSpec()), + enter = slideInVertically(playerControlsEnterAnimationSpec()) { it } + + fadeIn(playerControlsEnterAnimationSpec()), + exit = slideOutVertically(playerControlsExitAnimationSpec()) { it } + + fadeOut(playerControlsExitAnimationSpec()), modifier = Modifier.constrainAs(seekbar) { bottom.linkTo(parent.bottom, spacing.medium) }, @@ -293,8 +316,10 @@ fun PlayerControls( } AnimatedVisibility( controlsShown && !areControlsLocked, - enter = slideInHorizontally(playControlsAnimationSpec()) { -it } + fadeIn(playControlsAnimationSpec()), - exit = slideOutHorizontally(playControlsAnimationSpec()) { -it } + fadeOut(playControlsAnimationSpec()), + enter = slideInHorizontally(playerControlsEnterAnimationSpec()) { -it } + + fadeIn(playerControlsEnterAnimationSpec()), + exit = slideOutHorizontally(playerControlsExitAnimationSpec()) { -it } + + fadeOut(playerControlsExitAnimationSpec()), modifier = Modifier.constrainAs(topLeftControls) { top.linkTo(parent.top, spacing.medium) start.linkTo(parent.start) @@ -305,8 +330,10 @@ fun PlayerControls( // Top right controls AnimatedVisibility( controlsShown && !areControlsLocked, - enter = slideInHorizontally(playControlsAnimationSpec()) { it } + fadeIn(playControlsAnimationSpec()), - exit = slideOutHorizontally(playControlsAnimationSpec()) { it } + fadeOut(playControlsAnimationSpec()), + enter = slideInHorizontally(playerControlsEnterAnimationSpec()) { it } + + fadeIn(playerControlsEnterAnimationSpec()), + exit = slideOutHorizontally(playerControlsExitAnimationSpec()) { it } + + fadeOut(playerControlsExitAnimationSpec()), modifier = Modifier.constrainAs(topRightControls) { top.linkTo(parent.top, spacing.medium) end.linkTo(parent.end) @@ -315,8 +342,10 @@ fun PlayerControls( // Bottom right controls AnimatedVisibility( controlsShown && !areControlsLocked, - enter = slideInHorizontally(playControlsAnimationSpec()) { it } + fadeIn(playControlsAnimationSpec()), - exit = slideOutHorizontally(playControlsAnimationSpec()) { it } + fadeOut(playControlsAnimationSpec()), + enter = slideInHorizontally(playerControlsEnterAnimationSpec()) { it } + + fadeIn(playerControlsEnterAnimationSpec()), + exit = slideOutHorizontally(playerControlsExitAnimationSpec()) { it } + + fadeOut(playerControlsExitAnimationSpec()), modifier = Modifier.constrainAs(bottomRightControls) { bottom.linkTo(seekbar.top) end.linkTo(seekbar.end) @@ -325,8 +354,10 @@ fun PlayerControls( // Bottom left controls AnimatedVisibility( controlsShown && !areControlsLocked, - enter = slideInHorizontally(playControlsAnimationSpec()) { -it } + fadeIn(playControlsAnimationSpec()), - exit = slideOutHorizontally(playControlsAnimationSpec()) { -it } + fadeOut(playControlsAnimationSpec()), + enter = slideInHorizontally(playerControlsEnterAnimationSpec()) { -it } + + fadeIn(playerControlsEnterAnimationSpec()), + exit = slideOutHorizontally(playerControlsExitAnimationSpec()) { -it } + + fadeOut(playerControlsExitAnimationSpec()), modifier = Modifier.constrainAs(bottomLeftControls) { bottom.linkTo(seekbar.top) start.linkTo(seekbar.start) @@ -341,7 +372,12 @@ fun PlayerControls( } } -fun playControlsAnimationSpec(): FiniteAnimationSpec = tween( - durationMillis = 200, +fun playerControlsExitAnimationSpec(): FiniteAnimationSpec = tween( + durationMillis = 300, + easing = FastOutSlowInEasing, +) + +fun playerControlsEnterAnimationSpec(): FiniteAnimationSpec = tween( + durationMillis = 100, easing = LinearOutSlowInEasing, ) diff --git a/app/src/main/java/live/mehiz/mpvkt/ui/player/controls/components/ControlsButton.kt b/app/src/main/java/live/mehiz/mpvkt/ui/player/controls/components/ControlsButton.kt index c2aec62..53b52ff 100644 --- a/app/src/main/java/live/mehiz/mpvkt/ui/player/controls/components/ControlsButton.kt +++ b/app/src/main/java/live/mehiz/mpvkt/ui/player/controls/components/ControlsButton.kt @@ -2,6 +2,7 @@ package live.mehiz.mpvkt.ui.player.controls.components import androidx.compose.foundation.ExperimentalFoundationApi import androidx.compose.foundation.combinedClickable +import androidx.compose.foundation.indication import androidx.compose.foundation.interaction.MutableInteractionSource import androidx.compose.foundation.layout.Box import androidx.compose.foundation.layout.padding @@ -24,6 +25,7 @@ import androidx.compose.ui.unit.dp import live.mehiz.mpvkt.ui.player.controls.LocalPlayerButtonsClickEvent import live.mehiz.mpvkt.ui.theme.spacing +@Suppress("ModifierClickableOrder") @OptIn(ExperimentalFoundationApi::class) @Composable fun ControlsButton( @@ -39,7 +41,6 @@ fun ControlsButton( val clickEvent = LocalPlayerButtonsClickEvent.current Box( modifier = modifier - .clip(CircleShape) .combinedClickable( onClick = { clickEvent() @@ -47,7 +48,12 @@ fun ControlsButton( }, onLongClick = onLongClick, interactionSource = interactionSource, - indication = ripple(), + indication = null, + ) + .clip(CircleShape) + .indication( + interactionSource, + ripple() ) .padding(MaterialTheme.spacing.medium), ) { @@ -60,6 +66,7 @@ fun ControlsButton( } } +@Suppress("ModifierClickableOrder") @OptIn(ExperimentalFoundationApi::class) @Composable fun ControlsButton( @@ -74,7 +81,6 @@ fun ControlsButton( val clickEvent = LocalPlayerButtonsClickEvent.current Box( modifier = modifier - .clip(CircleShape) .combinedClickable( onClick = { clickEvent() @@ -82,7 +88,13 @@ fun ControlsButton( }, onLongClick = onLongClick, interactionSource = interactionSource, - indication = ripple(), + indication = null, + + ) + .clip(CircleShape) + .indication( + interactionSource, + ripple() ) .padding(MaterialTheme.spacing.medium), ) { diff --git a/app/src/main/java/live/mehiz/mpvkt/ui/player/controls/components/Seekbar.kt b/app/src/main/java/live/mehiz/mpvkt/ui/player/controls/components/Seekbar.kt index 8890000..c57f9e2 100644 --- a/app/src/main/java/live/mehiz/mpvkt/ui/player/controls/components/Seekbar.kt +++ b/app/src/main/java/live/mehiz/mpvkt/ui/player/controls/components/Seekbar.kt @@ -38,8 +38,8 @@ fun SeekbarWithTimers( timersInverted: Pair, positionTimerOnClick: () -> Unit, durationTimerOnCLick: () -> Unit, + chapters: ImmutableList, modifier: Modifier = Modifier, - chapters: ImmutableList? = null, ) { val clickEvent = LocalPlayerButtonsClickEvent.current Row( @@ -62,7 +62,16 @@ fun SeekbarWithTimers( onValueChange = onValueChange, onValueChangeFinished = onValueChangeFinished, readAheadValue = readAheadValue, - segments = chapters?.filter { it.start in 0f..duration } ?: persistentListOf(), + segments = chapters + .filter { it.start in 0f..duration } + .let { + // add an extra segment at 0 if it doesn't exist. + if (it.isNotEmpty() && it[0].start != 0f) { + persistentListOf(Segment("", 0f)) + it + } else { + it + } + it + }, modifier = Modifier.weight(1f), colors = SeekerDefaults.seekerColors( progressColor = MaterialTheme.colorScheme.primary, @@ -118,5 +127,6 @@ private fun PreviewSeekBar() { Pair(false, true), {}, {}, + persistentListOf() ) }