From 06b49d96afbcc3fbd10ef4035ec92cc4c548f510 Mon Sep 17 00:00:00 2001 From: Thalys Matias Carrara Date: Sat, 3 Jun 2023 16:47:44 -0400 Subject: [PATCH] feat: create a PlayerControlsScaffold * Create a PlayerControlScaffold to define the playerOverlay layout * Divide player components "MiddleControls", "BottomControls" into different files to make it more organized and easier to read * Remove the customization modifier because it can be confusing and each of your composable icons can have its own modifier set by whoever creates it Closes #32 --- .../test/EnhancedVideoPlayerTest.kt | 3 +- .../test/PlayerControlsTest.kt | 4 +- .../components/EnhancedVideoPlayer.kt | 4 +- .../components/PlayerControls.kt | 117 ------------------ .../playerOverlay/BottomControls.kt | 42 +++++++ .../playerOverlay/MiddleControls.kt | 52 ++++++++ .../playerOverlay/PlayerControls.kt | 69 +++++++++++ .../playerOverlay/PlayerControlsScaffold.kt | 39 ++++++ .../{ => playerOverlay}/PlayerIcons.kt | 2 +- 9 files changed, 210 insertions(+), 122 deletions(-) delete mode 100644 androidenhancedvideoplayer/src/main/java/com/profusion/androidenhancedvideoplayer/components/PlayerControls.kt create mode 100644 androidenhancedvideoplayer/src/main/java/com/profusion/androidenhancedvideoplayer/components/playerOverlay/BottomControls.kt create mode 100644 androidenhancedvideoplayer/src/main/java/com/profusion/androidenhancedvideoplayer/components/playerOverlay/MiddleControls.kt create mode 100644 androidenhancedvideoplayer/src/main/java/com/profusion/androidenhancedvideoplayer/components/playerOverlay/PlayerControls.kt create mode 100644 androidenhancedvideoplayer/src/main/java/com/profusion/androidenhancedvideoplayer/components/playerOverlay/PlayerControlsScaffold.kt rename androidenhancedvideoplayer/src/main/java/com/profusion/androidenhancedvideoplayer/components/{ => playerOverlay}/PlayerIcons.kt (96%) diff --git a/androidenhancedvideoplayer/src/androidTest/java/com/profusion/androidenhancedvideoplayer/test/EnhancedVideoPlayerTest.kt b/androidenhancedvideoplayer/src/androidTest/java/com/profusion/androidenhancedvideoplayer/test/EnhancedVideoPlayerTest.kt index acf92db7..de3d7491 100644 --- a/androidenhancedvideoplayer/src/androidTest/java/com/profusion/androidenhancedvideoplayer/test/EnhancedVideoPlayerTest.kt +++ b/androidenhancedvideoplayer/src/androidTest/java/com/profusion/androidenhancedvideoplayer/test/EnhancedVideoPlayerTest.kt @@ -26,6 +26,7 @@ class EnhancedVideoPlayerTest { .assertIsDisplayed() .performClick() - composeTestRule.onNodeWithTag("PlayerControlsParent").assertIsDisplayed() + composeTestRule.onNodeWithTag("PlayerControlsParent", useUnmergedTree = true) + .assertIsDisplayed() } } diff --git a/androidenhancedvideoplayer/src/androidTest/java/com/profusion/androidenhancedvideoplayer/test/PlayerControlsTest.kt b/androidenhancedvideoplayer/src/androidTest/java/com/profusion/androidenhancedvideoplayer/test/PlayerControlsTest.kt index 76df1806..bbf074ec 100644 --- a/androidenhancedvideoplayer/src/androidTest/java/com/profusion/androidenhancedvideoplayer/test/PlayerControlsTest.kt +++ b/androidenhancedvideoplayer/src/androidTest/java/com/profusion/androidenhancedvideoplayer/test/PlayerControlsTest.kt @@ -5,8 +5,8 @@ import androidx.compose.ui.test.assertIsDisplayed import androidx.compose.ui.test.junit4.createComposeRule import androidx.compose.ui.test.onNodeWithTag import androidx.compose.ui.test.performClick -import com.profusion.androidenhancedvideoplayer.components.ControlsCustomization -import com.profusion.androidenhancedvideoplayer.components.PlayerControls +import com.profusion.androidenhancedvideoplayer.components.playerOverlay.ControlsCustomization +import com.profusion.androidenhancedvideoplayer.components.playerOverlay.PlayerControls import org.junit.Rule import org.junit.Test diff --git a/androidenhancedvideoplayer/src/main/java/com/profusion/androidenhancedvideoplayer/components/EnhancedVideoPlayer.kt b/androidenhancedvideoplayer/src/main/java/com/profusion/androidenhancedvideoplayer/components/EnhancedVideoPlayer.kt index d2ce81bf..f105d613 100644 --- a/androidenhancedvideoplayer/src/main/java/com/profusion/androidenhancedvideoplayer/components/EnhancedVideoPlayer.kt +++ b/androidenhancedvideoplayer/src/main/java/com/profusion/androidenhancedvideoplayer/components/EnhancedVideoPlayer.kt @@ -22,6 +22,8 @@ import androidx.media3.common.util.UnstableApi import androidx.media3.exoplayer.ExoPlayer import androidx.media3.ui.AspectRatioFrameLayout import androidx.media3.ui.PlayerView +import com.profusion.androidenhancedvideoplayer.components.playerOverlay.ControlsCustomization +import com.profusion.androidenhancedvideoplayer.components.playerOverlay.PlayerControls import com.profusion.androidenhancedvideoplayer.utils.setLandscape import com.profusion.androidenhancedvideoplayer.utils.setPortrait @@ -120,7 +122,7 @@ fun EnhancedVideoPlayer( } }, customization = controlsCustomization, - modifier = Modifier.matchParentSize() + modifier = Modifier.matchParentSize().testTag("PlayerControlsParent") ) } } diff --git a/androidenhancedvideoplayer/src/main/java/com/profusion/androidenhancedvideoplayer/components/PlayerControls.kt b/androidenhancedvideoplayer/src/main/java/com/profusion/androidenhancedvideoplayer/components/PlayerControls.kt deleted file mode 100644 index a668a8af..00000000 --- a/androidenhancedvideoplayer/src/main/java/com/profusion/androidenhancedvideoplayer/components/PlayerControls.kt +++ /dev/null @@ -1,117 +0,0 @@ -package com.profusion.androidenhancedvideoplayer.components - -import androidx.compose.animation.AnimatedVisibility -import androidx.compose.animation.fadeIn -import androidx.compose.animation.fadeOut -import androidx.compose.foundation.background -import androidx.compose.foundation.layout.Arrangement -import androidx.compose.foundation.layout.Box -import androidx.compose.foundation.layout.Column -import androidx.compose.foundation.layout.Row -import androidx.compose.foundation.layout.absolutePadding -import androidx.compose.foundation.layout.fillMaxSize -import androidx.compose.foundation.layout.fillMaxWidth -import androidx.compose.material3.IconButton -import androidx.compose.runtime.Composable -import androidx.compose.ui.Alignment -import androidx.compose.ui.Modifier -import androidx.compose.ui.graphics.Color -import androidx.compose.ui.platform.testTag -import androidx.compose.ui.tooling.preview.Preview -import androidx.compose.ui.unit.dp - -class ControlsCustomization( - val previousIconContent: @Composable () -> Unit = { PreviousIcon() }, - val playIconContent: @Composable () -> Unit = { PlayIcon() }, - val pauseIconContent: @Composable () -> Unit = { PauseIcon() }, - val replayIconContent: @Composable () -> Unit = { ReplayIcon() }, - val nextIconContent: @Composable () -> Unit = { NextIcon() }, - val fullScreenIconContent: @Composable () -> Unit = { FullScreenIcon() }, - val exitFullScreenIconContent: @Composable () -> Unit = { ExitFullScreenIcon() }, - val modifier: Modifier = Modifier -) - -@Composable -fun PlayerControls( - isVisible: Boolean, - isPlaying: Boolean, - isFullScreen: Boolean, - hasEnded: Boolean, - onPreviousClick: () -> Unit, - onPauseToggle: () -> Unit, - onNextClick: () -> Unit, - onFullScreenToggle: () -> Unit, - customization: ControlsCustomization, - modifier: Modifier = Modifier -) { - AnimatedVisibility( - visible = isVisible, - enter = fadeIn(), - exit = fadeOut(), - modifier = modifier.testTag("PlayerControlsParent") - ) { - Column( - modifier = customization.modifier - .fillMaxSize() - .background(Color.Black.copy(alpha = 0.6f)) - ) { - Row( - horizontalArrangement = Arrangement.SpaceEvenly, - verticalAlignment = Alignment.CenterVertically, - modifier = Modifier - .fillMaxWidth() - .weight(1f) - ) { - IconButton(onClick = onPreviousClick) { - customization.previousIconContent() - } - IconButton( - onClick = onPauseToggle, - modifier = Modifier.testTag("PauseToggleButton") - ) { - when { - hasEnded -> customization.replayIconContent() - isPlaying -> customization.pauseIconContent() - else -> customization.playIconContent() - } - } - IconButton(onClick = onNextClick) { - customization.nextIconContent() - } - } - Box( - modifier = Modifier - .fillMaxWidth() - .absolutePadding(bottom = 16.dp, right = 16.dp) - ) { - IconButton( - onClick = onFullScreenToggle, - modifier = Modifier - .align(Alignment.CenterEnd) - .testTag("FullScreenToggleButton") - ) { - when (isFullScreen) { - true -> customization.exitFullScreenIconContent() - false -> customization.fullScreenIconContent() - } - } - } - } - } -} - -@Preview(showBackground = true) -@Composable -private fun PreviewPlayerControls() { - PlayerControls( - isVisible = true, - isPlaying = true, - hasEnded = false, - isFullScreen = false, - onPreviousClick = {}, - onPauseToggle = {}, - onNextClick = {}, - onFullScreenToggle = {}, - customization = ControlsCustomization() - ) -} diff --git a/androidenhancedvideoplayer/src/main/java/com/profusion/androidenhancedvideoplayer/components/playerOverlay/BottomControls.kt b/androidenhancedvideoplayer/src/main/java/com/profusion/androidenhancedvideoplayer/components/playerOverlay/BottomControls.kt new file mode 100644 index 00000000..3765240f --- /dev/null +++ b/androidenhancedvideoplayer/src/main/java/com/profusion/androidenhancedvideoplayer/components/playerOverlay/BottomControls.kt @@ -0,0 +1,42 @@ +package com.profusion.androidenhancedvideoplayer.components.playerOverlay + +import androidx.compose.foundation.layout.Box +import androidx.compose.foundation.layout.IntrinsicSize +import androidx.compose.foundation.layout.absolutePadding +import androidx.compose.foundation.layout.fillMaxWidth +import androidx.compose.foundation.layout.height +import androidx.compose.material3.IconButton +import androidx.compose.runtime.Composable +import androidx.compose.ui.Alignment +import androidx.compose.ui.Modifier +import androidx.compose.ui.platform.testTag +import androidx.compose.ui.unit.dp + +private val BOTTOM_CONTROLS_HEIGHT = 50.dp + +@Composable +fun BottomControls( + modifier: Modifier = Modifier, + isFullScreen: Boolean, + onFullScreenToggle: () -> Unit, + customization: ControlsCustomization +) { + Box( + modifier = modifier + .fillMaxWidth() + .height(IntrinsicSize.Min) + .absolutePadding(4.dp) + ) { + IconButton( + onClick = onFullScreenToggle, + modifier = Modifier + .align(Alignment.CenterEnd) + .testTag("FullScreenToggleButton") + ) { + when (isFullScreen) { + true -> customization.exitFullScreenIconContent() + false -> customization.fullScreenIconContent() + } + } + } +} diff --git a/androidenhancedvideoplayer/src/main/java/com/profusion/androidenhancedvideoplayer/components/playerOverlay/MiddleControls.kt b/androidenhancedvideoplayer/src/main/java/com/profusion/androidenhancedvideoplayer/components/playerOverlay/MiddleControls.kt new file mode 100644 index 00000000..e332862e --- /dev/null +++ b/androidenhancedvideoplayer/src/main/java/com/profusion/androidenhancedvideoplayer/components/playerOverlay/MiddleControls.kt @@ -0,0 +1,52 @@ +package com.profusion.androidenhancedvideoplayer.components.playerOverlay + +import androidx.compose.foundation.layout.Arrangement +import androidx.compose.foundation.layout.Box +import androidx.compose.foundation.layout.Row +import androidx.compose.foundation.layout.fillMaxWidth +import androidx.compose.material3.IconButton +import androidx.compose.runtime.Composable +import androidx.compose.ui.Alignment +import androidx.compose.ui.Modifier +import androidx.compose.ui.platform.testTag + +@Composable +fun MiddleControls( + modifier: Modifier = Modifier, + isPlaying: Boolean, + hasEnded: Boolean, + customization: ControlsCustomization, + onPreviousClick: () -> Unit, + onNextClick: () -> Unit, + onPauseToggle: () -> Unit +) { + Box( + modifier = modifier + .fillMaxWidth(), + contentAlignment = Alignment.Center + ) { + Row( + horizontalArrangement = Arrangement.SpaceEvenly, + verticalAlignment = Alignment.CenterVertically, + modifier = Modifier + .fillMaxWidth() + ) { + IconButton(onClick = onPreviousClick) { + customization.previousIconContent() + } + IconButton( + onClick = onPauseToggle, + modifier = Modifier.testTag("PauseToggleButton") + ) { + when { + hasEnded -> customization.replayIconContent() + isPlaying -> customization.pauseIconContent() + else -> customization.playIconContent() + } + } + IconButton(onClick = onNextClick) { + customization.nextIconContent() + } + } + } +} diff --git a/androidenhancedvideoplayer/src/main/java/com/profusion/androidenhancedvideoplayer/components/playerOverlay/PlayerControls.kt b/androidenhancedvideoplayer/src/main/java/com/profusion/androidenhancedvideoplayer/components/playerOverlay/PlayerControls.kt new file mode 100644 index 00000000..d5029352 --- /dev/null +++ b/androidenhancedvideoplayer/src/main/java/com/profusion/androidenhancedvideoplayer/components/playerOverlay/PlayerControls.kt @@ -0,0 +1,69 @@ +package com.profusion.androidenhancedvideoplayer.components.playerOverlay + +import androidx.compose.runtime.Composable +import androidx.compose.ui.Modifier +import androidx.compose.ui.platform.testTag +import androidx.compose.ui.tooling.preview.Preview + +class ControlsCustomization( + val previousIconContent: @Composable () -> Unit = { PreviousIcon() }, + val playIconContent: @Composable () -> Unit = { PlayIcon() }, + val pauseIconContent: @Composable () -> Unit = { PauseIcon() }, + val replayIconContent: @Composable () -> Unit = { ReplayIcon() }, + val nextIconContent: @Composable () -> Unit = { NextIcon() }, + val fullScreenIconContent: @Composable () -> Unit = { FullScreenIcon() }, + val exitFullScreenIconContent: @Composable () -> Unit = { ExitFullScreenIcon() } +) + +@Composable +fun PlayerControls( + isVisible: Boolean, + isPlaying: Boolean, + isFullScreen: Boolean, + hasEnded: Boolean, + onPreviousClick: () -> Unit, + onPauseToggle: () -> Unit, + onNextClick: () -> Unit, + onFullScreenToggle: () -> Unit, + customization: ControlsCustomization, + modifier: Modifier = Modifier +) { + PlayerControlsScaffold( + modifier = modifier.testTag("PlayerControlsParent"), + isVisible = isVisible, + topContent = { /* TODO */ }, + bottomContent = { + BottomControls( + isFullScreen = isFullScreen, + onFullScreenToggle = onFullScreenToggle, + customization = customization + ) + } + ) { + MiddleControls( + modifier = Modifier.weight(1f), + isPlaying = isPlaying, + hasEnded = hasEnded, + customization = customization, + onPreviousClick = onPreviousClick, + onNextClick = onNextClick, + onPauseToggle = onPauseToggle + ) + } +} + +@Preview(showBackground = true) +@Composable +private fun PreviewPlayerControls() { + PlayerControls( + isVisible = true, + isPlaying = true, + hasEnded = false, + isFullScreen = false, + onPreviousClick = {}, + onPauseToggle = {}, + onNextClick = {}, + onFullScreenToggle = {}, + customization = ControlsCustomization() + ) +} diff --git a/androidenhancedvideoplayer/src/main/java/com/profusion/androidenhancedvideoplayer/components/playerOverlay/PlayerControlsScaffold.kt b/androidenhancedvideoplayer/src/main/java/com/profusion/androidenhancedvideoplayer/components/playerOverlay/PlayerControlsScaffold.kt new file mode 100644 index 00000000..7b56cb77 --- /dev/null +++ b/androidenhancedvideoplayer/src/main/java/com/profusion/androidenhancedvideoplayer/components/playerOverlay/PlayerControlsScaffold.kt @@ -0,0 +1,39 @@ +package com.profusion.androidenhancedvideoplayer.components.playerOverlay + +import androidx.compose.animation.AnimatedVisibility +import androidx.compose.animation.fadeIn +import androidx.compose.animation.fadeOut +import androidx.compose.foundation.background +import androidx.compose.foundation.layout.Arrangement +import androidx.compose.foundation.layout.Column +import androidx.compose.foundation.layout.ColumnScope +import androidx.compose.foundation.layout.fillMaxSize +import androidx.compose.runtime.Composable +import androidx.compose.ui.Modifier +import androidx.compose.ui.graphics.Color + +@Composable +fun PlayerControlsScaffold( + modifier: Modifier = Modifier, + isVisible: Boolean, + topContent: @Composable ColumnScope.() -> Unit, + bottomContent: @Composable ColumnScope.() -> Unit, + content: @Composable ColumnScope.() -> Unit +) { + AnimatedVisibility( + visible = isVisible, + enter = fadeIn(), + exit = fadeOut(), + modifier = modifier + .background(Color.Black.copy(alpha = 0.6f)) + ) { + Column( + modifier = Modifier.fillMaxSize(), + verticalArrangement = Arrangement.SpaceBetween + ) { + topContent() + content() + bottomContent() + } + } +} diff --git a/androidenhancedvideoplayer/src/main/java/com/profusion/androidenhancedvideoplayer/components/PlayerIcons.kt b/androidenhancedvideoplayer/src/main/java/com/profusion/androidenhancedvideoplayer/components/playerOverlay/PlayerIcons.kt similarity index 96% rename from androidenhancedvideoplayer/src/main/java/com/profusion/androidenhancedvideoplayer/components/PlayerIcons.kt rename to androidenhancedvideoplayer/src/main/java/com/profusion/androidenhancedvideoplayer/components/playerOverlay/PlayerIcons.kt index a209d4ab..2aaa1f8c 100644 --- a/androidenhancedvideoplayer/src/main/java/com/profusion/androidenhancedvideoplayer/components/PlayerIcons.kt +++ b/androidenhancedvideoplayer/src/main/java/com/profusion/androidenhancedvideoplayer/components/playerOverlay/PlayerIcons.kt @@ -1,4 +1,4 @@ -package com.profusion.androidenhancedvideoplayer.components +package com.profusion.androidenhancedvideoplayer.components.playerOverlay import androidx.compose.foundation.Image import androidx.compose.runtime.Composable