From 819dc8038780eff71f100918857281ccaf4e8b67 Mon Sep 17 00:00:00 2001 From: Christian Schabesberger Date: Fri, 9 Aug 2024 14:34:18 +0200 Subject: [PATCH] restructure code and remove boilerplate by propagating viewmodel and uistate --- .../newplayer/model/VideoPlayerUIState.kt | 4 +- .../model/VideoPlayerViewModelImpl.kt | 86 +------- .../model/ViewoPlayerViewModelDummy.kt | 84 ++++++++ .../newplayer/ui/VideoPlayerControllerUI.kt | 185 +++--------------- .../net/newpipe/newplayer/ui/VideoPlayerUI.kt | 46 +---- .../net/newpipe/newplayer/ui/theme/Color.kt | 42 ++-- .../newplayer/ui/videoplayer/BottomUI.kt | 54 +++-- .../newplayer/ui/videoplayer/CenterUI.kt | 41 ++-- .../newplayer/ui/videoplayer/GestureUI.kt | 53 +---- .../gesture_ui/EmbeddedGestureUI.kt | 71 +++---- .../gesture_ui/FullscreenGestureUI.kt | 122 ++++++------ .../java/net/newpipe/newplayer/utils/utils.kt | 7 +- 12 files changed, 294 insertions(+), 501 deletions(-) create mode 100644 new-player/src/main/java/net/newpipe/newplayer/model/ViewoPlayerViewModelDummy.kt diff --git a/new-player/src/main/java/net/newpipe/newplayer/model/VideoPlayerUIState.kt b/new-player/src/main/java/net/newpipe/newplayer/model/VideoPlayerUIState.kt index f788d7b..29ccc3b 100644 --- a/new-player/src/main/java/net/newpipe/newplayer/model/VideoPlayerUIState.kt +++ b/new-player/src/main/java/net/newpipe/newplayer/model/VideoPlayerUIState.kt @@ -38,7 +38,7 @@ data class VideoPlayerUIState( val isLoading: Boolean, val durationInMs: Long, val playbackPositionInMs: Long, - val fastseekSeconds: Int, + val fastSeekSeconds: Int, val soundVolume: Float, // when null use system value @@ -58,7 +58,7 @@ data class VideoPlayerUIState( isLoading = true, durationInMs = 0, playbackPositionInMs = 0, - fastseekSeconds = 0, + fastSeekSeconds = 0, soundVolume = 0f, brightness = null ) diff --git a/new-player/src/main/java/net/newpipe/newplayer/model/VideoPlayerViewModelImpl.kt b/new-player/src/main/java/net/newpipe/newplayer/model/VideoPlayerViewModelImpl.kt index 53c3904..9afb258 100644 --- a/new-player/src/main/java/net/newpipe/newplayer/model/VideoPlayerViewModelImpl.kt +++ b/new-player/src/main/java/net/newpipe/newplayer/model/VideoPlayerViewModelImpl.kt @@ -279,7 +279,7 @@ class VideoPlayerViewModelImpl @Inject constructor( override fun fastSeek(count: Int) { mutableUiState.update { it.copy( - fastseekSeconds = count * (newPlayer?.fastSeekAmountSec ?: 10) + fastSeekSeconds = count * (newPlayer?.fastSeekAmountSec ?: 10) ) } @@ -289,13 +289,13 @@ class VideoPlayerViewModelImpl @Inject constructor( } override fun finishFastSeek() { - val fastSeekAmount = mutableUiState.value.fastseekSeconds + val fastSeekAmount = mutableUiState.value.fastSeekSeconds if (fastSeekAmount != 0) { Log.d(TAG, "$fastSeekAmount") newPlayer?.currentPosition = (newPlayer?.currentPosition ?: 0) + (fastSeekAmount * 1000) mutableUiState.update { - it.copy(fastseekSeconds = 0) + it.copy(fastSeekSeconds = 0) } } } @@ -356,84 +356,4 @@ class VideoPlayerViewModelImpl @Inject constructor( } ?: minContentRatio - - - companion object { - val dummy = object : VideoPlayerViewModel { - override var newPlayer: NewPlayer? = null - override val internalPlayer: Player? = null - override val uiState = MutableStateFlow(VideoPlayerUIState.DEFAULT) - override var minContentRatio = 4F / 3F - override var maxContentRatio = 16F / 9F - override var contentFitMode = ContentScale.FIT_INSIDE - - override fun initUIState(instanceState: Bundle) { - println("dummy impl") - } - - override fun addCallbackListener(listener: VideoPlayerViewModel.Listener) { - println("dummy impl") - } - - override fun play() { - println("dummy impl") - } - - override fun switchToEmbeddedView() { - println("dummy impl") - } - - override fun switchToFullscreen() { - println("dummy impl") - } - - override fun showUi() { - println("dummy impl") - } - - override fun hideUi() { - println("dummy impl") - } - - override fun seekPositionChanged(newValue: Float) { - println("dymmy seekPositionChanged: newValue: ${newValue}") - } - - override fun seekingFinished() { - println("dummy impl") - } - - override fun embeddedDraggedDown(offset: Float) { - println("dymmy embeddedDraggedDown: offset: ${offset}") - } - - override fun fastSeek(steps: Int) { - println("dummy impl") - } - - override fun finishFastSeek() { - println("dummy impl") - } - - override fun brightnessChange(changeRate: Float, currentValue: Float) { - println("dummy impl") - } - - override fun volumeChange(changeRate: Float) { - println("dummy impl") - } - - override fun pause() { - println("dummy pause") - } - - override fun prevStream() { - println("dummy impl") - } - - override fun nextStream() { - println("dummy impl") - } - } - } } \ No newline at end of file diff --git a/new-player/src/main/java/net/newpipe/newplayer/model/ViewoPlayerViewModelDummy.kt b/new-player/src/main/java/net/newpipe/newplayer/model/ViewoPlayerViewModelDummy.kt new file mode 100644 index 0000000..b4f99cc --- /dev/null +++ b/new-player/src/main/java/net/newpipe/newplayer/model/ViewoPlayerViewModelDummy.kt @@ -0,0 +1,84 @@ +package net.newpipe.newplayer.model + +import android.os.Bundle +import androidx.media3.common.Player +import kotlinx.coroutines.flow.MutableStateFlow +import net.newpipe.newplayer.NewPlayer +import net.newpipe.newplayer.ui.ContentScale + +open class VideoPlayerViewModelDummy : VideoPlayerViewModel { + override var newPlayer: NewPlayer? = null + override val internalPlayer: Player? = null + override val uiState = MutableStateFlow(VideoPlayerUIState.DEFAULT) + override var minContentRatio = 4F / 3F + override var maxContentRatio = 16F / 9F + override var contentFitMode = ContentScale.FIT_INSIDE + + override fun initUIState(instanceState: Bundle) { + println("dummy impl") + } + + override fun addCallbackListener(listener: VideoPlayerViewModel.Listener) { + println("dummy impl") + } + + override fun play() { + println("dummy impl") + } + + override fun switchToEmbeddedView() { + println("dummy impl") + } + + override fun switchToFullscreen() { + println("dummy impl") + } + + override fun showUi() { + println("dummy impl") + } + + override fun hideUi() { + println("dummy impl") + } + + override fun seekPositionChanged(newValue: Float) { + println("dymmy seekPositionChanged: newValue: ${newValue}") + } + + override fun seekingFinished() { + println("dummy impl") + } + + override fun embeddedDraggedDown(offset: Float) { + println("dymmy embeddedDraggedDown: offset: ${offset}") + } + + override fun fastSeek(steps: Int) { + println("dummy impl") + } + + override fun finishFastSeek() { + println("dummy impl") + } + + override fun brightnessChange(changeRate: Float, currentValue: Float) { + println("dummy impl") + } + + override fun volumeChange(changeRate: Float) { + println("dummy impl") + } + + override fun pause() { + println("dummy pause") + } + + override fun prevStream() { + println("dummy impl") + } + + override fun nextStream() { + println("dummy impl") + } +} diff --git a/new-player/src/main/java/net/newpipe/newplayer/ui/VideoPlayerControllerUI.kt b/new-player/src/main/java/net/newpipe/newplayer/ui/VideoPlayerControllerUI.kt index a3726df..78130d5 100644 --- a/new-player/src/main/java/net/newpipe/newplayer/ui/VideoPlayerControllerUI.kt +++ b/new-player/src/main/java/net/newpipe/newplayer/ui/VideoPlayerControllerUI.kt @@ -48,54 +48,34 @@ import androidx.compose.ui.graphics.Color import androidx.compose.ui.platform.LocalContext import androidx.compose.ui.tooling.preview.Preview import androidx.compose.ui.unit.dp +import net.newpipe.newplayer.model.VideoPlayerUIState +import net.newpipe.newplayer.model.VideoPlayerViewModel +import net.newpipe.newplayer.model.VideoPlayerViewModelDummy +import net.newpipe.newplayer.model.VideoPlayerViewModelImpl import net.newpipe.newplayer.ui.theme.VideoPlayerTheme import net.newpipe.newplayer.ui.videoplayer.BottomUI import net.newpipe.newplayer.ui.videoplayer.CenterUI import net.newpipe.newplayer.ui.videoplayer.TopUI import net.newpipe.newplayer.ui.videoplayer.GestureUI -import net.newpipe.newplayer.utils.getScreenBrightnes +import net.newpipe.newplayer.utils.getDefaultBrightness @Composable fun VideoPlayerControllerUI( - isPlaying: Boolean, - fullscreen: Boolean, - uiVissible: Boolean, - seekPosition: Float, - isLoading: Boolean, - durationInMs: Long, - playbackPositionInMs: Long, - bufferedPercentage: Float, - fastSeekSeconds: Int, - soundVolume: Float, - brightnes: Float, - play: () -> Unit, - pause: () -> Unit, - prevStream: () -> Unit, - nextStream: () -> Unit, - switchToFullscreen: () -> Unit, - switchToEmbeddedView: () -> Unit, - showUi: () -> Unit, - hideUi: () -> Unit, - seekPositionChanged: (Float) -> Unit, - seekingFinished: () -> Unit, - embeddedDraggedDownBy: (Float) -> Unit, - fastSeek: (Int) -> Unit, - finishFastSeek: () -> Unit, - brightnessChange: (Float, Float) -> Unit, - volumeChange: (Float) -> Unit + viewModel: VideoPlayerViewModel, + uiState: VideoPlayerUIState ) { val context = LocalContext.current - if (fullscreen) { + if (uiState.fullscreen) { BackHandler { - switchToEmbeddedView() + viewModel.switchToEmbeddedView() } } val internalBrightnessChange = { rateChange: Float -> - val systemBrightness = getScreenBrightnes(context as Activity) - brightnessChange(rateChange, systemBrightness) + val systemBrightness = getDefaultBrightness(context as Activity) + viewModel.brightnessChange(rateChange, systemBrightness) } val insets = @@ -103,35 +83,23 @@ fun VideoPlayerControllerUI( .union(WindowInsets.displayCutout) .union(WindowInsets.waterfall) - if (!uiVissible) { + if (!uiState.uiVissible) { GestureUI( modifier = Modifier .fillMaxSize(), // .windowInsetsPadding(WindowInsets.systemGestures), - hideUi = hideUi, - showUi = showUi, - uiVissible = uiVissible, - fullscreen = fullscreen, - fastSeekSeconds = fastSeekSeconds, - brightnes = brightnes, - soundVolume = soundVolume, - switchToFullscreen = switchToFullscreen, - switchToEmbeddedView = switchToEmbeddedView, - embeddedDraggedDownBy = embeddedDraggedDownBy, - fastSeek = fastSeek, - fastSeekFinished = finishFastSeek, - brightnessChange = internalBrightnessChange, - volumeChange = volumeChange + viewModel = viewModel, + uiState = uiState ) } - AnimatedVisibility(uiVissible) { + AnimatedVisibility(uiState.uiVissible) { Surface( modifier = Modifier.fillMaxSize(), color = Color(0x75000000) ) {} } - if (isLoading) { + if (uiState.isLoading) { Box(modifier = Modifier.fillMaxSize()) { CircularProgressIndicator( modifier = Modifier @@ -143,41 +111,25 @@ fun VideoPlayerControllerUI( } } - AnimatedVisibility(uiVissible) { + AnimatedVisibility(uiState.uiVissible) { GestureUI( modifier = Modifier .fillMaxSize() .windowInsetsPadding(WindowInsets.systemGestures), - hideUi = hideUi, - showUi = showUi, - uiVissible = uiVissible, - fullscreen = fullscreen, - fastSeekSeconds = fastSeekSeconds, - soundVolume = soundVolume, - brightnes = brightnes, - switchToFullscreen = switchToFullscreen, - switchToEmbeddedView = switchToEmbeddedView, - embeddedDraggedDownBy = embeddedDraggedDownBy, - fastSeek = fastSeek, - fastSeekFinished = finishFastSeek, - volumeChange = volumeChange, - brightnessChange = internalBrightnessChange + viewModel = viewModel, + uiState = uiState ) Box(modifier = Modifier.fillMaxSize()) { CenterUI( modifier = Modifier.align(Alignment.Center), - isPlaying = isPlaying, - isLoading = isLoading, - play = play, - pause = pause, - prevStream = prevStream, - nextStream = nextStream + viewModel = viewModel, + uiState = uiState ) } Box( - modifier = if (fullscreen) Modifier.windowInsetsPadding(insets) else Modifier + modifier = if (uiState.fullscreen) Modifier.windowInsetsPadding(insets) else Modifier ) { TopUI( modifier = Modifier @@ -193,15 +145,8 @@ fun VideoPlayerControllerUI( .padding(start = 16.dp, end = 16.dp) .defaultMinSize(minHeight = 40.dp) .fillMaxWidth(), - isFullscreen = fullscreen, - durationInMs = durationInMs, - playbackPositionInMs = playbackPositionInMs, - seekPosition = seekPosition, - bufferedPercentage = bufferedPercentage, - switchToFullscreen = switchToFullscreen, - switchToEmbeddedView = switchToEmbeddedView, - seekPositionChanged = seekPositionChanged, - seekingFinished = seekingFinished + viewModel = viewModel, + uiState = uiState ) } } @@ -236,32 +181,7 @@ fun PreviewBackgroundSurface( fun VideoPlayerControllerUIPreviewEmbedded() { VideoPlayerTheme { PreviewBackgroundSurface { - VideoPlayerControllerUI(isPlaying = false, - fullscreen = false, - uiVissible = true, - seekPosition = 0.3F, - isLoading = false, - durationInMs = 9 * 60 * 1000, - playbackPositionInMs = 6 * 60 * 1000, - bufferedPercentage = 0.4f, - fastSeekSeconds = 0, - soundVolume = 0f, - brightnes = 0f, - play = {}, - pause = {}, - prevStream = {}, - nextStream = {}, - switchToFullscreen = {}, - switchToEmbeddedView = {}, - showUi = {}, - hideUi = {}, - seekPositionChanged = {}, - seekingFinished = {}, - embeddedDraggedDownBy = {}, - fastSeek = {}, - finishFastSeek = {}, - brightnessChange = { a, b ->}, - volumeChange = {}) + VideoPlayerControllerUI(VideoPlayerViewModelDummy(), VideoPlayerUIState.DEFAULT) } } } @@ -271,32 +191,7 @@ fun VideoPlayerControllerUIPreviewEmbedded() { fun VideoPlayerControllerUIPreviewLandscape() { VideoPlayerTheme { PreviewBackgroundSurface { - VideoPlayerControllerUI(isPlaying = true, - fullscreen = true, - uiVissible = true, - seekPosition = 0.3F, - isLoading = true, - durationInMs = 9 * 60 * 1000, - playbackPositionInMs = 6 * 60 * 1000, - bufferedPercentage = 0.4f, - fastSeekSeconds = 0, - brightnes = 0f, - soundVolume = 0f, - play = {}, - pause = {}, - prevStream = {}, - nextStream = {}, - switchToEmbeddedView = {}, - switchToFullscreen = {}, - showUi = {}, - hideUi = {}, - seekPositionChanged = {}, - seekingFinished = {}, - embeddedDraggedDownBy = {}, - fastSeek = {}, - finishFastSeek = {}, - brightnessChange = { a, b -> }, - volumeChange = {}) + VideoPlayerControllerUI(VideoPlayerViewModelDummy(), VideoPlayerUIState.DEFAULT) } } } @@ -306,33 +201,7 @@ fun VideoPlayerControllerUIPreviewLandscape() { fun VideoPlayerControllerUIPreviewPortrait() { VideoPlayerTheme { PreviewBackgroundSurface { - VideoPlayerControllerUI( - isPlaying = false, - fullscreen = true, - uiVissible = true, - seekPosition = 0.3F, - isLoading = false, - durationInMs = 9 * 60 * 1000, - playbackPositionInMs = 6 * 60 * 1000, - bufferedPercentage = 0.4f, - fastSeekSeconds = 0, - brightnes = 0f, - soundVolume = 0f, - play = {}, - pause = {}, - prevStream = {}, - nextStream = {}, - switchToEmbeddedView = {}, - switchToFullscreen = {}, - showUi = {}, - hideUi = {}, - seekPositionChanged = {}, - seekingFinished = {}, - embeddedDraggedDownBy = {}, - fastSeek = {}, - finishFastSeek = {}, - brightnessChange = { a, b -> }, - volumeChange = {}) + VideoPlayerControllerUI(VideoPlayerViewModelDummy(), VideoPlayerUIState.DEFAULT) } } } \ No newline at end of file diff --git a/new-player/src/main/java/net/newpipe/newplayer/ui/VideoPlayerUI.kt b/new-player/src/main/java/net/newpipe/newplayer/ui/VideoPlayerUI.kt index 1564200..32242de 100644 --- a/new-player/src/main/java/net/newpipe/newplayer/ui/VideoPlayerUI.kt +++ b/new-player/src/main/java/net/newpipe/newplayer/ui/VideoPlayerUI.kt @@ -53,11 +53,11 @@ import androidx.lifecycle.Lifecycle import androidx.lifecycle.LifecycleEventObserver import androidx.media3.common.Player import net.newpipe.newplayer.model.VideoPlayerViewModel -import net.newpipe.newplayer.model.VideoPlayerViewModelImpl +import net.newpipe.newplayer.model.VideoPlayerViewModelDummy import net.newpipe.newplayer.ui.theme.VideoPlayerTheme import net.newpipe.newplayer.utils.LockScreenOrientation -import net.newpipe.newplayer.utils.getScreenBrightnes -import net.newpipe.newplayer.utils.setScreenBrightnes +import net.newpipe.newplayer.utils.getDefaultBrightness +import net.newpipe.newplayer.utils.setScreenBrightness private const val TAG = "VideoPlayerUI" @@ -89,8 +89,7 @@ fun VideoPlayerUI( // Setup fullscreen if (uiState.fullscreen) { LaunchedEffect(key1 = true) { - WindowCompat.getInsetsController(window, view) - .isAppearanceLightStatusBars = false + WindowCompat.getInsetsController(window, view).isAppearanceLightStatusBars = false } } @@ -130,13 +129,12 @@ fun VideoPlayerUI( val screenRatio = displayMetrics.widthPixels.toFloat() / displayMetrics.heightPixels.toFloat() - val systemScreenBrightnes = getScreenBrightnes(activity) + val defaultBrightness = getDefaultBrightness(activity) LaunchedEffect(key1 = uiState.brightness) { Log.d(TAG, "New Brightnes: ${uiState.brightness}") - setScreenBrightnes( - uiState.brightness - ?: if (systemScreenBrightnes < 0f) 0.5f else systemScreenBrightnes, activity + setScreenBrightness( + uiState.brightness ?: defaultBrightness, activity ) } @@ -160,33 +158,7 @@ fun VideoPlayerUI( } VideoPlayerControllerUI( - isPlaying = uiState.playing, - fullscreen = uiState.fullscreen, - uiVissible = uiState.uiVissible, - seekPosition = uiState.seekerPosition, - isLoading = uiState.isLoading, - durationInMs = uiState.durationInMs, - playbackPositionInMs = uiState.playbackPositionInMs, - bufferedPercentage = uiState.bufferedPercentage, - fastSeekSeconds = uiState.fastseekSeconds, - brightnes = uiState.brightness - ?: if (systemScreenBrightnes < 0f) 0.5f else systemScreenBrightnes, - soundVolume = uiState.soundVolume, - play = viewModel::play, - pause = viewModel::pause, - prevStream = viewModel::prevStream, - nextStream = viewModel::nextStream, - switchToFullscreen = viewModel::switchToFullscreen, - switchToEmbeddedView = viewModel::switchToEmbeddedView, - showUi = viewModel::showUi, - hideUi = viewModel::hideUi, - seekPositionChanged = viewModel::seekPositionChanged, - seekingFinished = viewModel::seekingFinished, - embeddedDraggedDownBy = viewModel::embeddedDraggedDown, - fastSeek = viewModel::fastSeek, - finishFastSeek = viewModel::finishFastSeek, - volumeChange = viewModel::volumeChange, - brightnessChange = viewModel::brightnessChange + viewModel, uiState = uiState ) } } @@ -253,6 +225,6 @@ fun PlaySurface( @Composable fun PlayerUIPreviewEmbeded() { VideoPlayerTheme { - VideoPlayerUI(viewModel = VideoPlayerViewModelImpl.dummy) + VideoPlayerUI(viewModel = VideoPlayerViewModelDummy()) } } \ No newline at end of file diff --git a/new-player/src/main/java/net/newpipe/newplayer/ui/theme/Color.kt b/new-player/src/main/java/net/newpipe/newplayer/ui/theme/Color.kt index 0f657aa..d08c288 100644 --- a/new-player/src/main/java/net/newpipe/newplayer/ui/theme/Color.kt +++ b/new-player/src/main/java/net/newpipe/newplayer/ui/theme/Color.kt @@ -23,6 +23,8 @@ package net.newpipe.newplayer.ui.theme import androidx.compose.runtime.Composable import androidx.compose.ui.graphics.Color import androidx.compose.ui.tooling.preview.Preview +import net.newpipe.newplayer.model.VideoPlayerUIState +import net.newpipe.newplayer.model.VideoPlayerViewModelDummy import net.newpipe.newplayer.ui.PreviewBackgroundSurface import net.newpipe.newplayer.ui.VideoPlayerControllerUI @@ -72,32 +74,20 @@ val video_player_scrim = Color(0xFF000000) fun VideoPlayerControllerUIPreviewEmbeddedColorPreview() { VideoPlayerTheme { PreviewBackgroundSurface { - VideoPlayerControllerUI(isPlaying = false, - fullscreen = false, - uiVissible = true, - seekPosition = 0.3F, - isLoading = false, - durationInMs = 9*60*1000, - playbackPositionInMs = 6*60*1000, - bufferedPercentage = 0.4f, - fastSeekSeconds = 10, - brightnes = 0f, - soundVolume = 0f, - play = {}, - pause = {}, - prevStream = {}, - nextStream = {}, - switchToFullscreen = {}, - switchToEmbeddedView = {}, - showUi = {}, - hideUi = {}, - seekPositionChanged = {}, - seekingFinished = {}, - embeddedDraggedDownBy = {}, - fastSeek = {}, - finishFastSeek = {}, - brightnessChange = {a, b ->}, - volumeChange = {}) + VideoPlayerControllerUI( + viewModel = VideoPlayerViewModelDummy(), + uiState = VideoPlayerUIState.DEFAULT.copy( + playing = true, + fullscreen = false, + uiVissible = true, + seekerPosition = 0.3f, + isLoading = false, + durationInMs = 9 * 60 * 1000, + playbackPositionInMs = 6 * 60 * 1000, + bufferedPercentage = 0.4f, + fastSeekSeconds = 10, + ), + ) } } } diff --git a/new-player/src/main/java/net/newpipe/newplayer/ui/videoplayer/BottomUI.kt b/new-player/src/main/java/net/newpipe/newplayer/ui/videoplayer/BottomUI.kt index 13c36a7..45fb5f5 100644 --- a/new-player/src/main/java/net/newpipe/newplayer/ui/videoplayer/BottomUI.kt +++ b/new-player/src/main/java/net/newpipe/newplayer/ui/videoplayer/BottomUI.kt @@ -42,6 +42,9 @@ import androidx.compose.ui.res.stringResource import androidx.compose.ui.tooling.preview.Preview import androidx.core.os.ConfigurationCompat import net.newpipe.newplayer.R +import net.newpipe.newplayer.model.VideoPlayerUIState +import net.newpipe.newplayer.model.VideoPlayerViewModel +import net.newpipe.newplayer.model.VideoPlayerViewModelDummy import net.newpipe.newplayer.ui.seeker.DefaultSeekerColor import net.newpipe.newplayer.ui.seeker.Seeker import net.newpipe.newplayer.ui.seeker.SeekerColors @@ -52,39 +55,33 @@ import kotlin.math.min @Composable fun BottomUI( - modifier: Modifier, - isFullscreen: Boolean, - seekPosition: Float, - durationInMs: Long, - playbackPositionInMs: Long, - bufferedPercentage: Float, - switchToFullscreen: () -> Unit, - switchToEmbeddedView: () -> Unit, - seekPositionChanged: (Float) -> Unit, - seekingFinished: () -> Unit + modifier: Modifier, viewModel: VideoPlayerViewModel, uiState: VideoPlayerUIState ) { Row( verticalAlignment = Alignment.CenterVertically, horizontalArrangement = Arrangement.SpaceBetween, modifier = modifier ) { - Text(getTimeStringFromMs(playbackPositionInMs, getLocale() ?: Locale.US)) + Text(getTimeStringFromMs(uiState.playbackPositionInMs, getLocale() ?: Locale.US)) Seeker( Modifier.weight(1F), - value = seekPosition, - onValueChange = seekPositionChanged, - onValueChangeFinished = seekingFinished, - readAheadValue = bufferedPercentage, + value = uiState.seekerPosition, + onValueChange = viewModel::seekPositionChanged, + onValueChangeFinished = viewModel::seekingFinished, + readAheadValue = uiState.bufferedPercentage, colors = customizedSeekerColors() ) //Slider(value = 0.4F, onValueChange = {}, modifier = Modifier.weight(1F)) - Text(getTimeStringFromMs(durationInMs, getLocale() ?: Locale.US)) + Text(getTimeStringFromMs(uiState.durationInMs, getLocale() ?: Locale.US)) - IconButton(onClick = if (isFullscreen) switchToEmbeddedView else switchToFullscreen) { + IconButton( + onClick = if (uiState.fullscreen) viewModel::switchToEmbeddedView + else viewModel::switchToFullscreen + ) { Icon( - imageVector = if (isFullscreen) Icons.Filled.FullscreenExit + imageVector = if (uiState.fullscreen) Icons.Filled.FullscreenExit else Icons.Filled.Fullscreen, contentDescription = stringResource(R.string.widget_description_toggle_fullscreen) ) @@ -93,7 +90,7 @@ fun BottomUI( } @Composable -private fun customizedSeekerColors() : SeekerColors { +private fun customizedSeekerColors(): SeekerColors { val colors = DefaultSeekerColor( progressColor = MaterialTheme.colorScheme.primary, thumbColor = MaterialTheme.colorScheme.primary, @@ -124,7 +121,7 @@ private const val MILLIS_PER_DAY = private const val MILLIS_PER_HOUR = MINUTES_PER_HOUR * SECONDS_PER_MINUTE * MILLIS_PER_SECOND private const val MILLIS_PER_MINUTE = SECONDS_PER_MINUTE * MILLIS_PER_SECOND -private fun getTimeStringFromMs(timeSpanInMs: Long, locale: Locale) : String { +private fun getTimeStringFromMs(timeSpanInMs: Long, locale: Locale): String { val days = timeSpanInMs / MILLIS_PER_DAY val millisThisDay = timeSpanInMs - days * MILLIS_PER_DAY val hours = millisThisDay / MILLIS_PER_HOUR @@ -153,15 +150,14 @@ fun VideoPlayerControllerBottomUIPreview() { Surface(color = Color.Black) { BottomUI( modifier = Modifier, - isFullscreen = true, - seekPosition = 0.4F, - durationInMs = 90 * 60 * 1000, - playbackPositionInMs = 3 * 60 * 1000, - bufferedPercentage = 0.4f, - switchToFullscreen = { }, - switchToEmbeddedView = { }, - seekPositionChanged = {}, - seekingFinished = {} + viewModel = VideoPlayerViewModelDummy(), + uiState = VideoPlayerUIState.DEFAULT.copy( + fullscreen = true, + seekerPosition = 0.4f, + durationInMs = 90 * 60 * 1000, + playbackPositionInMs = 3 * 60 * 1000, + bufferedPercentage = 0.4f + ), ) } } diff --git a/new-player/src/main/java/net/newpipe/newplayer/ui/videoplayer/CenterUI.kt b/new-player/src/main/java/net/newpipe/newplayer/ui/videoplayer/CenterUI.kt index 465035a..c626c9d 100644 --- a/new-player/src/main/java/net/newpipe/newplayer/ui/videoplayer/CenterUI.kt +++ b/new-player/src/main/java/net/newpipe/newplayer/ui/videoplayer/CenterUI.kt @@ -42,41 +42,41 @@ import androidx.compose.ui.res.stringResource import androidx.compose.ui.tooling.preview.Preview import androidx.compose.ui.unit.dp import net.newpipe.newplayer.R +import net.newpipe.newplayer.model.VideoPlayerUIState +import net.newpipe.newplayer.model.VideoPlayerViewModel +import net.newpipe.newplayer.model.VideoPlayerViewModelDummy +import net.newpipe.newplayer.model.VideoPlayerViewModelImpl import net.newpipe.newplayer.ui.theme.VideoPlayerTheme @Composable fun CenterUI( - modifier: Modifier, - isPlaying: Boolean, - isLoading: Boolean, - play: () -> Unit, - pause: () -> Unit, - nextStream: () -> Unit, - prevStream: () -> Unit + modifier: Modifier = Modifier, + viewModel: VideoPlayerViewModel, + uiState: VideoPlayerUIState ) { Row( verticalAlignment = Alignment.CenterVertically, horizontalArrangement = Arrangement.SpaceBetween, modifier = modifier, ) { - if (!isLoading) { + if (!uiState.isLoading) { CenterControllButton( buttonModifier = Modifier.size(80.dp), iconModifier = Modifier.size(40.dp), icon = Icons.Filled.SkipPrevious, contentDescriptoion = stringResource(R.string.widget_description_previous_stream), - onClick = prevStream + onClick = viewModel::prevStream ) CenterControllButton( buttonModifier = Modifier.size(80.dp), iconModifier = Modifier.size(60.dp), - icon = if (isPlaying) Icons.Filled.Pause else Icons.Filled.PlayArrow, + icon = if (uiState.playing) Icons.Filled.Pause else Icons.Filled.PlayArrow, contentDescriptoion = stringResource( - if (isPlaying) R.string.widget_description_pause + if (uiState.playing) R.string.widget_description_pause else R.string.widget_description_play ), - onClick = if (isPlaying) pause else play + onClick = if (uiState.playing) viewModel::pause else viewModel::play ) CenterControllButton( @@ -84,7 +84,7 @@ fun CenterUI( iconModifier = Modifier.size(40.dp), icon = Icons.Filled.SkipNext, contentDescriptoion = stringResource(R.string.widget_description_next_stream), - onClick = nextStream + onClick = viewModel::nextStream ) } } @@ -122,13 +122,12 @@ fun VideoPlayerControllerUICenterUIPreview() { VideoPlayerTheme { Surface(color = Color.Black) { CenterUI( - modifier = Modifier, - isPlaying = true, - isLoading = false, - play = { }, - pause = { }, - nextStream = { }) { - } + viewModel = VideoPlayerViewModelDummy(), + uiState = VideoPlayerUIState.DEFAULT.copy( + isLoading = false, + playing = true + ) + ) } } -} \ No newline at end of file +} diff --git a/new-player/src/main/java/net/newpipe/newplayer/ui/videoplayer/GestureUI.kt b/new-player/src/main/java/net/newpipe/newplayer/ui/videoplayer/GestureUI.kt index f6e3916..0584f3c 100644 --- a/new-player/src/main/java/net/newpipe/newplayer/ui/videoplayer/GestureUI.kt +++ b/new-player/src/main/java/net/newpipe/newplayer/ui/videoplayer/GestureUI.kt @@ -23,6 +23,8 @@ package net.newpipe.newplayer.ui.videoplayer import androidx.compose.runtime.Composable import androidx.compose.ui.Modifier import androidx.compose.ui.graphics.Color +import net.newpipe.newplayer.model.VideoPlayerUIState +import net.newpipe.newplayer.model.VideoPlayerViewModel import net.newpipe.newplayer.ui.videoplayer.gesture_ui.EmbeddedGestureUI import net.newpipe.newplayer.ui.videoplayer.gesture_ui.FullscreenGestureUI @@ -39,54 +41,15 @@ val INDICATOR_BACKGROUND_COLOR = Color.Black.copy(alpha = 0.3f) @Composable fun GestureUI( - modifier: Modifier, - hideUi: () -> Unit, - showUi: () -> Unit, - uiVissible: Boolean, - fullscreen: Boolean, - fastSeekSeconds: Int, - brightnes: Float, - soundVolume: Float, - switchToFullscreen: () -> Unit, - switchToEmbeddedView: () -> Unit, - embeddedDraggedDownBy: (Float) -> Unit, - fastSeek: (Int) -> Unit, - fastSeekFinished: () -> Unit, - volumeChange: (Float) -> Unit, - brightnessChange: (Float) -> Unit, + modifier: Modifier, viewModel: VideoPlayerViewModel, uiState: VideoPlayerUIState ) { - val defaultOnRegularTap = { - if (uiVissible) { - hideUi() - } else { - showUi() - } - } - - if (fullscreen) { + if (uiState.fullscreen) { FullscreenGestureUI( - modifier = modifier, - uiVisible = uiVissible, - fastSeekSeconds = fastSeekSeconds, - hideUi = hideUi, - showUi = showUi, - fastSeek = fastSeek, - brightnes = brightnes, - volume = soundVolume, - switchToEmbeddedView = switchToEmbeddedView, - fastSeekFinished = fastSeekFinished, - volumeChange = volumeChange, - brightnesChange = brightnessChange) + modifier = modifier, viewModel = viewModel, uiState = uiState + ) } else { EmbeddedGestureUI( - modifier = modifier, - fastSeekSeconds = fastSeekSeconds, - uiVissible = uiVissible, - switchToFullscreen = switchToFullscreen, - embeddedDraggedDownBy = embeddedDraggedDownBy, - fastSeek = fastSeek, - fastSeekFinished = fastSeekFinished, - hideUi = hideUi, - showUi = showUi) + modifier = modifier, viewModel = viewModel, uiState = uiState + ) } } \ No newline at end of file diff --git a/new-player/src/main/java/net/newpipe/newplayer/ui/videoplayer/gesture_ui/EmbeddedGestureUI.kt b/new-player/src/main/java/net/newpipe/newplayer/ui/videoplayer/gesture_ui/EmbeddedGestureUI.kt index fb8c286..d2c57f5 100644 --- a/new-player/src/main/java/net/newpipe/newplayer/ui/videoplayer/gesture_ui/EmbeddedGestureUI.kt +++ b/new-player/src/main/java/net/newpipe/newplayer/ui/videoplayer/gesture_ui/EmbeddedGestureUI.kt @@ -32,6 +32,9 @@ import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.graphics.Color import androidx.compose.ui.tooling.preview.Preview +import net.newpipe.newplayer.model.VideoPlayerUIState +import net.newpipe.newplayer.model.VideoPlayerViewModel +import net.newpipe.newplayer.model.VideoPlayerViewModelDummy import net.newpipe.newplayer.ui.theme.VideoPlayerTheme import net.newpipe.newplayer.ui.videoplayer.FAST_SEEK_MODE_DURATION @@ -39,49 +42,34 @@ private const val TAG = "EmbeddedGestureUI" @Composable fun EmbeddedGestureUI( - modifier: Modifier = Modifier, - fastSeekSeconds: Int, - uiVissible: Boolean, - switchToFullscreen: () -> Unit, - embeddedDraggedDownBy: (Float) -> Unit, - fastSeek: (Int) -> Unit, - fastSeekFinished: () -> Unit, - hideUi: () -> Unit, - showUi: () -> Unit + modifier: Modifier = Modifier, viewModel: VideoPlayerViewModel, uiState: VideoPlayerUIState ) { val handleDownwardMovement = { movement: TouchedPosition -> Log.d(TAG, "${movement.x}:${movement.y}") if (0 < movement.y) { - embeddedDraggedDownBy(movement.y) + viewModel.embeddedDraggedDown(movement.y) } else { - switchToFullscreen() + viewModel.switchToFullscreen() } } val defaultOnRegularTap = { - if (uiVissible) { - hideUi() + if (uiState.uiVissible) { + viewModel.hideUi() } else { - showUi() + viewModel.showUi() } } Row(modifier = modifier) { GestureSurface( - modifier = Modifier - .weight(1f), - multiTapTimeoutInMs = FAST_SEEK_MODE_DURATION, - onRegularTap = defaultOnRegularTap, - onMultiTap = { - fastSeek(-it) - }, - onMultiTapFinished = fastSeekFinished, - onMovement = handleDownwardMovement + modifier = Modifier.weight(1f), onRegularTap = defaultOnRegularTap, onMultiTap = { + viewModel.fastSeek(-it) + }, onMultiTapFinished = viewModel::finishFastSeek, onMovement = handleDownwardMovement ) { FadedAnimationForSeekFeedback( - fastSeekSeconds, - backwards = true + uiState.fastSeekSeconds, backwards = true ) { fastSeekSecondsToDisplay -> Box(modifier = Modifier.fillMaxSize()) { FastSeekVisualFeedback( @@ -93,15 +81,13 @@ fun EmbeddedGestureUI( } } GestureSurface( - modifier = Modifier - .weight(1f), - multiTapTimeoutInMs = FAST_SEEK_MODE_DURATION, + modifier = Modifier.weight(1f), onRegularTap = defaultOnRegularTap, onMovement = handleDownwardMovement, - onMultiTap = fastSeek, - onMultiTapFinished = fastSeekFinished + onMultiTap = viewModel::fastSeek, + onMultiTapFinished = viewModel::finishFastSeek ) { - FadedAnimationForSeekFeedback(fastSeekSeconds) { fastSeekSecondsToDisplay -> + FadedAnimationForSeekFeedback(uiState.fastSeekSeconds) { fastSeekSecondsToDisplay -> Box(modifier = Modifier.fillMaxSize()) { FastSeekVisualFeedback( modifier = Modifier.align(Alignment.Center), @@ -122,14 +108,21 @@ fun EmbeddedGestureUIPreview() { Surface(modifier = Modifier.wrapContentSize(), color = Color.DarkGray) { EmbeddedGestureUI( modifier = Modifier, - hideUi = { }, - showUi = { }, - uiVissible = false, - fastSeekSeconds = 0, - switchToFullscreen = { println("switch to fullscreen") }, - embeddedDraggedDownBy = { println("embedded dragged down") }, - fastSeek = { println("Fast seek by $it steps") }, - fastSeekFinished = {}) + viewModel = object : VideoPlayerViewModelDummy() { + override fun switchToEmbeddedView() { + println("switch to fullscreen") + } + + override fun embeddedDraggedDown(offset: Float) { + println("embedded view dragged down by $offset") + } + + override fun fastSeek(steps: Int) { + println("fast seek by $steps steps") + } + }, + uiState = VideoPlayerUIState.DEFAULT, + ) } } } diff --git a/new-player/src/main/java/net/newpipe/newplayer/ui/videoplayer/gesture_ui/FullscreenGestureUI.kt b/new-player/src/main/java/net/newpipe/newplayer/ui/videoplayer/gesture_ui/FullscreenGestureUI.kt index 440211a..cb0c811 100644 --- a/new-player/src/main/java/net/newpipe/newplayer/ui/videoplayer/gesture_ui/FullscreenGestureUI.kt +++ b/new-player/src/main/java/net/newpipe/newplayer/ui/videoplayer/gesture_ui/FullscreenGestureUI.kt @@ -21,14 +21,12 @@ package net.newpipe.newplayer.ui.videoplayer.gesture_ui +import android.app.Activity import androidx.compose.animation.AnimatedVisibility import androidx.compose.animation.core.Spring import androidx.compose.animation.core.spring -import androidx.compose.animation.core.tween import androidx.compose.animation.fadeIn import androidx.compose.animation.fadeOut -import androidx.compose.animation.scaleIn -import androidx.compose.animation.scaleOut import androidx.compose.foundation.layout.Box import androidx.compose.foundation.layout.Row import androidx.compose.foundation.layout.fillMaxSize @@ -44,9 +42,13 @@ import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.graphics.Color import androidx.compose.ui.layout.onGloballyPositioned +import androidx.compose.ui.platform.LocalContext import androidx.compose.ui.tooling.preview.Preview +import net.newpipe.newplayer.model.VideoPlayerUIState +import net.newpipe.newplayer.model.VideoPlayerViewModel +import net.newpipe.newplayer.model.VideoPlayerViewModelDummy import net.newpipe.newplayer.ui.theme.VideoPlayerTheme -import net.newpipe.newplayer.ui.videoplayer.FAST_SEEK_MODE_DURATION +import net.newpipe.newplayer.utils.getDefaultBrightness private enum class IndicatorMode { NONE, @@ -57,17 +59,8 @@ private enum class IndicatorMode { @Composable fun FullscreenGestureUI( modifier: Modifier = Modifier, - uiVisible: Boolean, - fastSeekSeconds: Int, - volume: Float, - brightnes: Float, - hideUi: () -> Unit, - showUi: () -> Unit, - fastSeek: (Int) -> Unit, - fastSeekFinished: () -> Unit, - switchToEmbeddedView: () -> Unit, - volumeChange: (Float) -> Unit, - brightnesChange: (Float) -> Unit + viewModel: VideoPlayerViewModel, + uiState: VideoPlayerUIState ) { var heightPx by remember { @@ -80,13 +73,17 @@ fun FullscreenGestureUI( val defaultOnRegularTap = { - if (uiVisible) { - hideUi() + if (uiState.uiVisible) { + viewModel.hideUi() } else { - showUi() + viewModel.showUi() } } + val activity = LocalContext.current as Activity + + val defaultBrightness = getDefaultBrightness(activity) + Box(modifier = modifier.onGloballyPositioned { coordinates -> heightPx = coordinates.size.height.toFloat() }) { @@ -97,9 +94,9 @@ fun FullscreenGestureUI( onRegularTap = defaultOnRegularTap, onMultiTap = { println("multitap ${-it}") - fastSeek(-it) + viewModel.fastSeek(-it) }, - onMultiTapFinished = fastSeekFinished, + onMultiTapFinished = viewModel::finishFastSeek, onUp = { indicatorMode = IndicatorMode.NONE }, @@ -110,13 +107,13 @@ fun FullscreenGestureUI( indicatorMode = IndicatorMode.BRIGHTNESS_INDICATOR_VISSIBLE if (heightPx != 0f) { - brightnesChange(-change.y / heightPx) + viewModel.brightnessChange(-change.y / heightPx, defaultBrightness) } } } ) { FadedAnimationForSeekFeedback( - fastSeekSeconds, + uiState.fastSeekSeconds, backwards = true ) { fastSeekSecondsToDisplay -> Box(modifier = Modifier.fillMaxSize()) { @@ -134,7 +131,7 @@ fun FullscreenGestureUI( onRegularTap = defaultOnRegularTap, onMovement = { movement -> if (0 < movement.y) { - switchToEmbeddedView() + viewModel.switchToEmbeddedView() } } ) @@ -142,8 +139,8 @@ fun FullscreenGestureUI( modifier = Modifier .weight(1f), onRegularTap = defaultOnRegularTap, - onMultiTap = fastSeek, - onMultiTapFinished = fastSeekFinished, + onMultiTap = viewModel::fastSeek, + onMultiTapFinished = viewModel::finishFastSeek, onUp = { indicatorMode = IndicatorMode.NONE }, @@ -153,12 +150,12 @@ fun FullscreenGestureUI( ) { indicatorMode = IndicatorMode.VOLUME_INDICATOR_VISSIBLE if (heightPx != 0f) { - volumeChange(-change.y / heightPx) + viewModel.volumeChange(-change.y / heightPx) } } } ) { - FadedAnimationForSeekFeedback(fastSeekSeconds) { fastSeekSecondsToDisplay -> + FadedAnimationForSeekFeedback(uiState.fastSeekSeconds) { fastSeekSecondsToDisplay -> Box(modifier = Modifier.fillMaxSize()) { FastSeekVisualFeedback( modifier = Modifier.align(Alignment.CenterStart), @@ -174,7 +171,7 @@ fun FullscreenGestureUI( modifier = Modifier.align(Alignment.Center), visible = indicatorMode == IndicatorMode.VOLUME_INDICATOR_VISSIBLE, ) { - VolumeCircle(volumeFraction = volume) + VolumeCircle(volumeFraction = uiState.soundVolume) } IndicatorAnimation( @@ -182,7 +179,7 @@ fun FullscreenGestureUI( visible = indicatorMode == IndicatorMode.BRIGHTNESS_INDICATOR_VISSIBLE, ) { VolumeCircle( - volumeFraction = brightnes, + volumeFraction = uiState.brightness ?: defaultBrightness, modifier = Modifier.align(Alignment.Center), isBrightness = true ) @@ -233,17 +230,13 @@ fun FullscreenGestureUIPreview() { Surface(modifier = Modifier.wrapContentSize(), color = Color.DarkGray) { FullscreenGestureUI( modifier = Modifier, - hideUi = { }, - showUi = { }, - uiVisible = false, - fastSeekSeconds = 0, - volume = 0f, - brightnes = 0f, - fastSeek = { println("fast seek by $it steps") }, - fastSeekFinished = {}, - switchToEmbeddedView = {}, - brightnesChange = {}, - volumeChange = {}) + object : VideoPlayerViewModelDummy() { + override fun fastSeek(steps: Int) { + println("fast seek by $steps steps") + } + }, + VideoPlayerUIState.DEFAULT + ) } } } @@ -256,7 +249,7 @@ fun FullscreenGestureUIPreviewInteractive() { mutableStateOf(0) } - var brightnesValue by remember { + var brightnessValue by remember { mutableStateOf(0f) } @@ -272,23 +265,38 @@ fun FullscreenGestureUIPreviewInteractive() { Surface(modifier = Modifier.wrapContentSize(), color = Color.Gray) { FullscreenGestureUI( modifier = Modifier, - hideUi = { uiVisible = false }, - showUi = { uiVisible = true }, - uiVisible = uiVisible, - fastSeekSeconds = seekSeconds, - volume = soundVolume, - brightnes = brightnesValue, - fastSeek = { seekSeconds = it * 10 }, - fastSeekFinished = { - seekSeconds = 0 + object : VideoPlayerViewModelDummy() { + override fun hideUi() { + uiVisible = false + } + + override fun showUi() { + uiVisible = true + } + + override fun fastSeek(steps: Int) { + seekSeconds = steps * 10 + } + + override fun finishFastSeek() { + seekSeconds = 0 + } + + override fun brightnessChange(changeRate: Float, currentValue: Float) { + brightnessValue = (brightnessValue + changeRate).coerceIn(0f, 1f) + } + + override fun volumeChange(changeRate: Float) { + soundVolume = (soundVolume + changeRate).coerceIn(0f, 1f) + } }, - switchToEmbeddedView = {}, - brightnesChange = { - brightnesValue = (brightnesValue + it).coerceIn(0f, 1f) - }, - volumeChange = { - soundVolume = (soundVolume + it).coerceIn(0f, 1f) - }) + uiState = VideoPlayerUIState.DEFAULT.copy( + uiVissible = uiVisible, + fastSeekSeconds = seekSeconds, + soundVolume = soundVolume, + brightness = brightnessValue + ), + ) } AnimatedVisibility(uiVisible) { diff --git a/new-player/src/main/java/net/newpipe/newplayer/utils/utils.kt b/new-player/src/main/java/net/newpipe/newplayer/utils/utils.kt index e6204b4..706c456 100644 --- a/new-player/src/main/java/net/newpipe/newplayer/utils/utils.kt +++ b/new-player/src/main/java/net/newpipe/newplayer/utils/utils.kt @@ -24,7 +24,6 @@ import android.annotation.SuppressLint import android.app.Activity import android.content.Context import android.content.ContextWrapper -import android.view.Window import android.view.WindowManager import androidx.compose.runtime.Composable import androidx.compose.runtime.DisposableEffect @@ -45,14 +44,14 @@ fun LockScreenOrientation(orientation: Int) { } @SuppressLint("NewApi") -fun getScreenBrightnes(activity: Activity) : Float { +fun getDefaultBrightness(activity: Activity) : Float { val window = activity.window val layout = window.attributes as WindowManager.LayoutParams - return layout.screenBrightness + return if(layout.screenBrightness < 0) 0.5f else layout.screenBrightness } @SuppressLint("NewApi") -fun setScreenBrightnes(value:Float, activity: Activity) { +fun setScreenBrightness(value:Float, activity: Activity) { val window = activity.window val layout = window.attributes as WindowManager.LayoutParams layout.screenBrightness = value