restructure code and remove boilerplate by propagating viewmodel and uistate

This commit is contained in:
Christian Schabesberger 2024-08-09 14:34:18 +02:00
parent 0d6227071e
commit 819dc80387
12 changed files with 294 additions and 501 deletions

View File

@ -38,7 +38,7 @@ data class VideoPlayerUIState(
val isLoading: Boolean, val isLoading: Boolean,
val durationInMs: Long, val durationInMs: Long,
val playbackPositionInMs: Long, val playbackPositionInMs: Long,
val fastseekSeconds: Int, val fastSeekSeconds: Int,
val soundVolume: Float, val soundVolume: Float,
// when null use system value // when null use system value
@ -58,7 +58,7 @@ data class VideoPlayerUIState(
isLoading = true, isLoading = true,
durationInMs = 0, durationInMs = 0,
playbackPositionInMs = 0, playbackPositionInMs = 0,
fastseekSeconds = 0, fastSeekSeconds = 0,
soundVolume = 0f, soundVolume = 0f,
brightness = null brightness = null
) )

View File

@ -279,7 +279,7 @@ class VideoPlayerViewModelImpl @Inject constructor(
override fun fastSeek(count: Int) { override fun fastSeek(count: Int) {
mutableUiState.update { mutableUiState.update {
it.copy( it.copy(
fastseekSeconds = count * (newPlayer?.fastSeekAmountSec ?: 10) fastSeekSeconds = count * (newPlayer?.fastSeekAmountSec ?: 10)
) )
} }
@ -289,13 +289,13 @@ class VideoPlayerViewModelImpl @Inject constructor(
} }
override fun finishFastSeek() { override fun finishFastSeek() {
val fastSeekAmount = mutableUiState.value.fastseekSeconds val fastSeekAmount = mutableUiState.value.fastSeekSeconds
if (fastSeekAmount != 0) { if (fastSeekAmount != 0) {
Log.d(TAG, "$fastSeekAmount") Log.d(TAG, "$fastSeekAmount")
newPlayer?.currentPosition = (newPlayer?.currentPosition ?: 0) + (fastSeekAmount * 1000) newPlayer?.currentPosition = (newPlayer?.currentPosition ?: 0) + (fastSeekAmount * 1000)
mutableUiState.update { mutableUiState.update {
it.copy(fastseekSeconds = 0) it.copy(fastSeekSeconds = 0)
} }
} }
} }
@ -356,84 +356,4 @@ class VideoPlayerViewModelImpl @Inject constructor(
} ?: minContentRatio } ?: 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")
}
}
}
} }

View File

@ -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")
}
}

View File

@ -48,54 +48,34 @@ import androidx.compose.ui.graphics.Color
import androidx.compose.ui.platform.LocalContext import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.tooling.preview.Preview import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.unit.dp 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.theme.VideoPlayerTheme
import net.newpipe.newplayer.ui.videoplayer.BottomUI import net.newpipe.newplayer.ui.videoplayer.BottomUI
import net.newpipe.newplayer.ui.videoplayer.CenterUI import net.newpipe.newplayer.ui.videoplayer.CenterUI
import net.newpipe.newplayer.ui.videoplayer.TopUI import net.newpipe.newplayer.ui.videoplayer.TopUI
import net.newpipe.newplayer.ui.videoplayer.GestureUI import net.newpipe.newplayer.ui.videoplayer.GestureUI
import net.newpipe.newplayer.utils.getScreenBrightnes import net.newpipe.newplayer.utils.getDefaultBrightness
@Composable @Composable
fun VideoPlayerControllerUI( fun VideoPlayerControllerUI(
isPlaying: Boolean, viewModel: VideoPlayerViewModel,
fullscreen: Boolean, uiState: VideoPlayerUIState
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
) { ) {
val context = LocalContext.current val context = LocalContext.current
if (fullscreen) { if (uiState.fullscreen) {
BackHandler { BackHandler {
switchToEmbeddedView() viewModel.switchToEmbeddedView()
} }
} }
val internalBrightnessChange = { rateChange: Float -> val internalBrightnessChange = { rateChange: Float ->
val systemBrightness = getScreenBrightnes(context as Activity) val systemBrightness = getDefaultBrightness(context as Activity)
brightnessChange(rateChange, systemBrightness) viewModel.brightnessChange(rateChange, systemBrightness)
} }
val insets = val insets =
@ -103,35 +83,23 @@ fun VideoPlayerControllerUI(
.union(WindowInsets.displayCutout) .union(WindowInsets.displayCutout)
.union(WindowInsets.waterfall) .union(WindowInsets.waterfall)
if (!uiVissible) { if (!uiState.uiVissible) {
GestureUI( GestureUI(
modifier = Modifier modifier = Modifier
.fillMaxSize(), .fillMaxSize(),
// .windowInsetsPadding(WindowInsets.systemGestures), // .windowInsetsPadding(WindowInsets.systemGestures),
hideUi = hideUi, viewModel = viewModel,
showUi = showUi, uiState = uiState
uiVissible = uiVissible,
fullscreen = fullscreen,
fastSeekSeconds = fastSeekSeconds,
brightnes = brightnes,
soundVolume = soundVolume,
switchToFullscreen = switchToFullscreen,
switchToEmbeddedView = switchToEmbeddedView,
embeddedDraggedDownBy = embeddedDraggedDownBy,
fastSeek = fastSeek,
fastSeekFinished = finishFastSeek,
brightnessChange = internalBrightnessChange,
volumeChange = volumeChange
) )
} }
AnimatedVisibility(uiVissible) { AnimatedVisibility(uiState.uiVissible) {
Surface( Surface(
modifier = Modifier.fillMaxSize(), color = Color(0x75000000) modifier = Modifier.fillMaxSize(), color = Color(0x75000000)
) {} ) {}
} }
if (isLoading) { if (uiState.isLoading) {
Box(modifier = Modifier.fillMaxSize()) { Box(modifier = Modifier.fillMaxSize()) {
CircularProgressIndicator( CircularProgressIndicator(
modifier = Modifier modifier = Modifier
@ -143,41 +111,25 @@ fun VideoPlayerControllerUI(
} }
} }
AnimatedVisibility(uiVissible) { AnimatedVisibility(uiState.uiVissible) {
GestureUI( GestureUI(
modifier = Modifier modifier = Modifier
.fillMaxSize() .fillMaxSize()
.windowInsetsPadding(WindowInsets.systemGestures), .windowInsetsPadding(WindowInsets.systemGestures),
hideUi = hideUi, viewModel = viewModel,
showUi = showUi, uiState = uiState
uiVissible = uiVissible,
fullscreen = fullscreen,
fastSeekSeconds = fastSeekSeconds,
soundVolume = soundVolume,
brightnes = brightnes,
switchToFullscreen = switchToFullscreen,
switchToEmbeddedView = switchToEmbeddedView,
embeddedDraggedDownBy = embeddedDraggedDownBy,
fastSeek = fastSeek,
fastSeekFinished = finishFastSeek,
volumeChange = volumeChange,
brightnessChange = internalBrightnessChange
) )
Box(modifier = Modifier.fillMaxSize()) { Box(modifier = Modifier.fillMaxSize()) {
CenterUI( CenterUI(
modifier = Modifier.align(Alignment.Center), modifier = Modifier.align(Alignment.Center),
isPlaying = isPlaying, viewModel = viewModel,
isLoading = isLoading, uiState = uiState
play = play,
pause = pause,
prevStream = prevStream,
nextStream = nextStream
) )
} }
Box( Box(
modifier = if (fullscreen) Modifier.windowInsetsPadding(insets) else Modifier modifier = if (uiState.fullscreen) Modifier.windowInsetsPadding(insets) else Modifier
) { ) {
TopUI( TopUI(
modifier = Modifier modifier = Modifier
@ -193,15 +145,8 @@ fun VideoPlayerControllerUI(
.padding(start = 16.dp, end = 16.dp) .padding(start = 16.dp, end = 16.dp)
.defaultMinSize(minHeight = 40.dp) .defaultMinSize(minHeight = 40.dp)
.fillMaxWidth(), .fillMaxWidth(),
isFullscreen = fullscreen, viewModel = viewModel,
durationInMs = durationInMs, uiState = uiState
playbackPositionInMs = playbackPositionInMs,
seekPosition = seekPosition,
bufferedPercentage = bufferedPercentage,
switchToFullscreen = switchToFullscreen,
switchToEmbeddedView = switchToEmbeddedView,
seekPositionChanged = seekPositionChanged,
seekingFinished = seekingFinished
) )
} }
} }
@ -236,32 +181,7 @@ fun PreviewBackgroundSurface(
fun VideoPlayerControllerUIPreviewEmbedded() { fun VideoPlayerControllerUIPreviewEmbedded() {
VideoPlayerTheme { VideoPlayerTheme {
PreviewBackgroundSurface { PreviewBackgroundSurface {
VideoPlayerControllerUI(isPlaying = false, VideoPlayerControllerUI(VideoPlayerViewModelDummy(), VideoPlayerUIState.DEFAULT)
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 = {})
} }
} }
} }
@ -271,32 +191,7 @@ fun VideoPlayerControllerUIPreviewEmbedded() {
fun VideoPlayerControllerUIPreviewLandscape() { fun VideoPlayerControllerUIPreviewLandscape() {
VideoPlayerTheme { VideoPlayerTheme {
PreviewBackgroundSurface { PreviewBackgroundSurface {
VideoPlayerControllerUI(isPlaying = true, VideoPlayerControllerUI(VideoPlayerViewModelDummy(), VideoPlayerUIState.DEFAULT)
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 = {})
} }
} }
} }
@ -306,33 +201,7 @@ fun VideoPlayerControllerUIPreviewLandscape() {
fun VideoPlayerControllerUIPreviewPortrait() { fun VideoPlayerControllerUIPreviewPortrait() {
VideoPlayerTheme { VideoPlayerTheme {
PreviewBackgroundSurface { PreviewBackgroundSurface {
VideoPlayerControllerUI( VideoPlayerControllerUI(VideoPlayerViewModelDummy(), VideoPlayerUIState.DEFAULT)
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 = {})
} }
} }
} }

View File

@ -53,11 +53,11 @@ import androidx.lifecycle.Lifecycle
import androidx.lifecycle.LifecycleEventObserver import androidx.lifecycle.LifecycleEventObserver
import androidx.media3.common.Player import androidx.media3.common.Player
import net.newpipe.newplayer.model.VideoPlayerViewModel 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.ui.theme.VideoPlayerTheme
import net.newpipe.newplayer.utils.LockScreenOrientation import net.newpipe.newplayer.utils.LockScreenOrientation
import net.newpipe.newplayer.utils.getScreenBrightnes import net.newpipe.newplayer.utils.getDefaultBrightness
import net.newpipe.newplayer.utils.setScreenBrightnes import net.newpipe.newplayer.utils.setScreenBrightness
private const val TAG = "VideoPlayerUI" private const val TAG = "VideoPlayerUI"
@ -89,8 +89,7 @@ fun VideoPlayerUI(
// Setup fullscreen // Setup fullscreen
if (uiState.fullscreen) { if (uiState.fullscreen) {
LaunchedEffect(key1 = true) { LaunchedEffect(key1 = true) {
WindowCompat.getInsetsController(window, view) WindowCompat.getInsetsController(window, view).isAppearanceLightStatusBars = false
.isAppearanceLightStatusBars = false
} }
} }
@ -130,13 +129,12 @@ fun VideoPlayerUI(
val screenRatio = val screenRatio =
displayMetrics.widthPixels.toFloat() / displayMetrics.heightPixels.toFloat() displayMetrics.widthPixels.toFloat() / displayMetrics.heightPixels.toFloat()
val systemScreenBrightnes = getScreenBrightnes(activity) val defaultBrightness = getDefaultBrightness(activity)
LaunchedEffect(key1 = uiState.brightness) { LaunchedEffect(key1 = uiState.brightness) {
Log.d(TAG, "New Brightnes: ${uiState.brightness}") Log.d(TAG, "New Brightnes: ${uiState.brightness}")
setScreenBrightnes( setScreenBrightness(
uiState.brightness uiState.brightness ?: defaultBrightness, activity
?: if (systemScreenBrightnes < 0f) 0.5f else systemScreenBrightnes, activity
) )
} }
@ -160,33 +158,7 @@ fun VideoPlayerUI(
} }
VideoPlayerControllerUI( VideoPlayerControllerUI(
isPlaying = uiState.playing, viewModel, uiState = uiState
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
) )
} }
} }
@ -253,6 +225,6 @@ fun PlaySurface(
@Composable @Composable
fun PlayerUIPreviewEmbeded() { fun PlayerUIPreviewEmbeded() {
VideoPlayerTheme { VideoPlayerTheme {
VideoPlayerUI(viewModel = VideoPlayerViewModelImpl.dummy) VideoPlayerUI(viewModel = VideoPlayerViewModelDummy())
} }
} }

View File

@ -23,6 +23,8 @@ package net.newpipe.newplayer.ui.theme
import androidx.compose.runtime.Composable import androidx.compose.runtime.Composable
import androidx.compose.ui.graphics.Color import androidx.compose.ui.graphics.Color
import androidx.compose.ui.tooling.preview.Preview 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.PreviewBackgroundSurface
import net.newpipe.newplayer.ui.VideoPlayerControllerUI import net.newpipe.newplayer.ui.VideoPlayerControllerUI
@ -72,32 +74,20 @@ val video_player_scrim = Color(0xFF000000)
fun VideoPlayerControllerUIPreviewEmbeddedColorPreview() { fun VideoPlayerControllerUIPreviewEmbeddedColorPreview() {
VideoPlayerTheme { VideoPlayerTheme {
PreviewBackgroundSurface { PreviewBackgroundSurface {
VideoPlayerControllerUI(isPlaying = false, VideoPlayerControllerUI(
fullscreen = false, viewModel = VideoPlayerViewModelDummy(),
uiVissible = true, uiState = VideoPlayerUIState.DEFAULT.copy(
seekPosition = 0.3F, playing = true,
isLoading = false, fullscreen = false,
durationInMs = 9*60*1000, uiVissible = true,
playbackPositionInMs = 6*60*1000, seekerPosition = 0.3f,
bufferedPercentage = 0.4f, isLoading = false,
fastSeekSeconds = 10, durationInMs = 9 * 60 * 1000,
brightnes = 0f, playbackPositionInMs = 6 * 60 * 1000,
soundVolume = 0f, bufferedPercentage = 0.4f,
play = {}, fastSeekSeconds = 10,
pause = {}, ),
prevStream = {}, )
nextStream = {},
switchToFullscreen = {},
switchToEmbeddedView = {},
showUi = {},
hideUi = {},
seekPositionChanged = {},
seekingFinished = {},
embeddedDraggedDownBy = {},
fastSeek = {},
finishFastSeek = {},
brightnessChange = {a, b ->},
volumeChange = {})
} }
} }
} }

View File

@ -42,6 +42,9 @@ import androidx.compose.ui.res.stringResource
import androidx.compose.ui.tooling.preview.Preview import androidx.compose.ui.tooling.preview.Preview
import androidx.core.os.ConfigurationCompat import androidx.core.os.ConfigurationCompat
import net.newpipe.newplayer.R 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.DefaultSeekerColor
import net.newpipe.newplayer.ui.seeker.Seeker import net.newpipe.newplayer.ui.seeker.Seeker
import net.newpipe.newplayer.ui.seeker.SeekerColors import net.newpipe.newplayer.ui.seeker.SeekerColors
@ -52,39 +55,33 @@ import kotlin.math.min
@Composable @Composable
fun BottomUI( fun BottomUI(
modifier: Modifier, modifier: Modifier, viewModel: VideoPlayerViewModel, uiState: VideoPlayerUIState
isFullscreen: Boolean,
seekPosition: Float,
durationInMs: Long,
playbackPositionInMs: Long,
bufferedPercentage: Float,
switchToFullscreen: () -> Unit,
switchToEmbeddedView: () -> Unit,
seekPositionChanged: (Float) -> Unit,
seekingFinished: () -> Unit
) { ) {
Row( Row(
verticalAlignment = Alignment.CenterVertically, verticalAlignment = Alignment.CenterVertically,
horizontalArrangement = Arrangement.SpaceBetween, horizontalArrangement = Arrangement.SpaceBetween,
modifier = modifier modifier = modifier
) { ) {
Text(getTimeStringFromMs(playbackPositionInMs, getLocale() ?: Locale.US)) Text(getTimeStringFromMs(uiState.playbackPositionInMs, getLocale() ?: Locale.US))
Seeker( Seeker(
Modifier.weight(1F), Modifier.weight(1F),
value = seekPosition, value = uiState.seekerPosition,
onValueChange = seekPositionChanged, onValueChange = viewModel::seekPositionChanged,
onValueChangeFinished = seekingFinished, onValueChangeFinished = viewModel::seekingFinished,
readAheadValue = bufferedPercentage, readAheadValue = uiState.bufferedPercentage,
colors = customizedSeekerColors() colors = customizedSeekerColors()
) )
//Slider(value = 0.4F, onValueChange = {}, modifier = Modifier.weight(1F)) //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( Icon(
imageVector = if (isFullscreen) Icons.Filled.FullscreenExit imageVector = if (uiState.fullscreen) Icons.Filled.FullscreenExit
else Icons.Filled.Fullscreen, else Icons.Filled.Fullscreen,
contentDescription = stringResource(R.string.widget_description_toggle_fullscreen) contentDescription = stringResource(R.string.widget_description_toggle_fullscreen)
) )
@ -93,7 +90,7 @@ fun BottomUI(
} }
@Composable @Composable
private fun customizedSeekerColors() : SeekerColors { private fun customizedSeekerColors(): SeekerColors {
val colors = DefaultSeekerColor( val colors = DefaultSeekerColor(
progressColor = MaterialTheme.colorScheme.primary, progressColor = MaterialTheme.colorScheme.primary,
thumbColor = 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_HOUR = MINUTES_PER_HOUR * SECONDS_PER_MINUTE * MILLIS_PER_SECOND
private const val MILLIS_PER_MINUTE = 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 days = timeSpanInMs / MILLIS_PER_DAY
val millisThisDay = timeSpanInMs - days * MILLIS_PER_DAY val millisThisDay = timeSpanInMs - days * MILLIS_PER_DAY
val hours = millisThisDay / MILLIS_PER_HOUR val hours = millisThisDay / MILLIS_PER_HOUR
@ -153,15 +150,14 @@ fun VideoPlayerControllerBottomUIPreview() {
Surface(color = Color.Black) { Surface(color = Color.Black) {
BottomUI( BottomUI(
modifier = Modifier, modifier = Modifier,
isFullscreen = true, viewModel = VideoPlayerViewModelDummy(),
seekPosition = 0.4F, uiState = VideoPlayerUIState.DEFAULT.copy(
durationInMs = 90 * 60 * 1000, fullscreen = true,
playbackPositionInMs = 3 * 60 * 1000, seekerPosition = 0.4f,
bufferedPercentage = 0.4f, durationInMs = 90 * 60 * 1000,
switchToFullscreen = { }, playbackPositionInMs = 3 * 60 * 1000,
switchToEmbeddedView = { }, bufferedPercentage = 0.4f
seekPositionChanged = {}, ),
seekingFinished = {}
) )
} }
} }

View File

@ -42,41 +42,41 @@ import androidx.compose.ui.res.stringResource
import androidx.compose.ui.tooling.preview.Preview import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.dp
import net.newpipe.newplayer.R 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 import net.newpipe.newplayer.ui.theme.VideoPlayerTheme
@Composable @Composable
fun CenterUI( fun CenterUI(
modifier: Modifier, modifier: Modifier = Modifier,
isPlaying: Boolean, viewModel: VideoPlayerViewModel,
isLoading: Boolean, uiState: VideoPlayerUIState
play: () -> Unit,
pause: () -> Unit,
nextStream: () -> Unit,
prevStream: () -> Unit
) { ) {
Row( Row(
verticalAlignment = Alignment.CenterVertically, verticalAlignment = Alignment.CenterVertically,
horizontalArrangement = Arrangement.SpaceBetween, horizontalArrangement = Arrangement.SpaceBetween,
modifier = modifier, modifier = modifier,
) { ) {
if (!isLoading) { if (!uiState.isLoading) {
CenterControllButton( CenterControllButton(
buttonModifier = Modifier.size(80.dp), buttonModifier = Modifier.size(80.dp),
iconModifier = Modifier.size(40.dp), iconModifier = Modifier.size(40.dp),
icon = Icons.Filled.SkipPrevious, icon = Icons.Filled.SkipPrevious,
contentDescriptoion = stringResource(R.string.widget_description_previous_stream), contentDescriptoion = stringResource(R.string.widget_description_previous_stream),
onClick = prevStream onClick = viewModel::prevStream
) )
CenterControllButton( CenterControllButton(
buttonModifier = Modifier.size(80.dp), buttonModifier = Modifier.size(80.dp),
iconModifier = Modifier.size(60.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( contentDescriptoion = stringResource(
if (isPlaying) R.string.widget_description_pause if (uiState.playing) R.string.widget_description_pause
else R.string.widget_description_play else R.string.widget_description_play
), ),
onClick = if (isPlaying) pause else play onClick = if (uiState.playing) viewModel::pause else viewModel::play
) )
CenterControllButton( CenterControllButton(
@ -84,7 +84,7 @@ fun CenterUI(
iconModifier = Modifier.size(40.dp), iconModifier = Modifier.size(40.dp),
icon = Icons.Filled.SkipNext, icon = Icons.Filled.SkipNext,
contentDescriptoion = stringResource(R.string.widget_description_next_stream), contentDescriptoion = stringResource(R.string.widget_description_next_stream),
onClick = nextStream onClick = viewModel::nextStream
) )
} }
} }
@ -122,13 +122,12 @@ fun VideoPlayerControllerUICenterUIPreview() {
VideoPlayerTheme { VideoPlayerTheme {
Surface(color = Color.Black) { Surface(color = Color.Black) {
CenterUI( CenterUI(
modifier = Modifier, viewModel = VideoPlayerViewModelDummy(),
isPlaying = true, uiState = VideoPlayerUIState.DEFAULT.copy(
isLoading = false, isLoading = false,
play = { }, playing = true
pause = { }, )
nextStream = { }) { )
}
} }
} }
} }

View File

@ -23,6 +23,8 @@ package net.newpipe.newplayer.ui.videoplayer
import androidx.compose.runtime.Composable import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Color 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.EmbeddedGestureUI
import net.newpipe.newplayer.ui.videoplayer.gesture_ui.FullscreenGestureUI import net.newpipe.newplayer.ui.videoplayer.gesture_ui.FullscreenGestureUI
@ -39,54 +41,15 @@ val INDICATOR_BACKGROUND_COLOR = Color.Black.copy(alpha = 0.3f)
@Composable @Composable
fun GestureUI( fun GestureUI(
modifier: Modifier, modifier: Modifier, viewModel: VideoPlayerViewModel, uiState: VideoPlayerUIState
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,
) { ) {
val defaultOnRegularTap = { if (uiState.fullscreen) {
if (uiVissible) {
hideUi()
} else {
showUi()
}
}
if (fullscreen) {
FullscreenGestureUI( FullscreenGestureUI(
modifier = modifier, modifier = modifier, viewModel = viewModel, uiState = uiState
uiVisible = uiVissible, )
fastSeekSeconds = fastSeekSeconds,
hideUi = hideUi,
showUi = showUi,
fastSeek = fastSeek,
brightnes = brightnes,
volume = soundVolume,
switchToEmbeddedView = switchToEmbeddedView,
fastSeekFinished = fastSeekFinished,
volumeChange = volumeChange,
brightnesChange = brightnessChange)
} else { } else {
EmbeddedGestureUI( EmbeddedGestureUI(
modifier = modifier, modifier = modifier, viewModel = viewModel, uiState = uiState
fastSeekSeconds = fastSeekSeconds, )
uiVissible = uiVissible,
switchToFullscreen = switchToFullscreen,
embeddedDraggedDownBy = embeddedDraggedDownBy,
fastSeek = fastSeek,
fastSeekFinished = fastSeekFinished,
hideUi = hideUi,
showUi = showUi)
} }
} }

View File

@ -32,6 +32,9 @@ import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Color import androidx.compose.ui.graphics.Color
import androidx.compose.ui.tooling.preview.Preview 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.theme.VideoPlayerTheme
import net.newpipe.newplayer.ui.videoplayer.FAST_SEEK_MODE_DURATION import net.newpipe.newplayer.ui.videoplayer.FAST_SEEK_MODE_DURATION
@ -39,49 +42,34 @@ private const val TAG = "EmbeddedGestureUI"
@Composable @Composable
fun EmbeddedGestureUI( fun EmbeddedGestureUI(
modifier: Modifier = Modifier, modifier: Modifier = Modifier, viewModel: VideoPlayerViewModel, uiState: VideoPlayerUIState
fastSeekSeconds: Int,
uiVissible: Boolean,
switchToFullscreen: () -> Unit,
embeddedDraggedDownBy: (Float) -> Unit,
fastSeek: (Int) -> Unit,
fastSeekFinished: () -> Unit,
hideUi: () -> Unit,
showUi: () -> Unit
) { ) {
val handleDownwardMovement = { movement: TouchedPosition -> val handleDownwardMovement = { movement: TouchedPosition ->
Log.d(TAG, "${movement.x}:${movement.y}") Log.d(TAG, "${movement.x}:${movement.y}")
if (0 < movement.y) { if (0 < movement.y) {
embeddedDraggedDownBy(movement.y) viewModel.embeddedDraggedDown(movement.y)
} else { } else {
switchToFullscreen() viewModel.switchToFullscreen()
} }
} }
val defaultOnRegularTap = { val defaultOnRegularTap = {
if (uiVissible) { if (uiState.uiVissible) {
hideUi() viewModel.hideUi()
} else { } else {
showUi() viewModel.showUi()
} }
} }
Row(modifier = modifier) { Row(modifier = modifier) {
GestureSurface( GestureSurface(
modifier = Modifier modifier = Modifier.weight(1f), onRegularTap = defaultOnRegularTap, onMultiTap = {
.weight(1f), viewModel.fastSeek(-it)
multiTapTimeoutInMs = FAST_SEEK_MODE_DURATION, }, onMultiTapFinished = viewModel::finishFastSeek, onMovement = handleDownwardMovement
onRegularTap = defaultOnRegularTap,
onMultiTap = {
fastSeek(-it)
},
onMultiTapFinished = fastSeekFinished,
onMovement = handleDownwardMovement
) { ) {
FadedAnimationForSeekFeedback( FadedAnimationForSeekFeedback(
fastSeekSeconds, uiState.fastSeekSeconds, backwards = true
backwards = true
) { fastSeekSecondsToDisplay -> ) { fastSeekSecondsToDisplay ->
Box(modifier = Modifier.fillMaxSize()) { Box(modifier = Modifier.fillMaxSize()) {
FastSeekVisualFeedback( FastSeekVisualFeedback(
@ -93,15 +81,13 @@ fun EmbeddedGestureUI(
} }
} }
GestureSurface( GestureSurface(
modifier = Modifier modifier = Modifier.weight(1f),
.weight(1f),
multiTapTimeoutInMs = FAST_SEEK_MODE_DURATION,
onRegularTap = defaultOnRegularTap, onRegularTap = defaultOnRegularTap,
onMovement = handleDownwardMovement, onMovement = handleDownwardMovement,
onMultiTap = fastSeek, onMultiTap = viewModel::fastSeek,
onMultiTapFinished = fastSeekFinished onMultiTapFinished = viewModel::finishFastSeek
) { ) {
FadedAnimationForSeekFeedback(fastSeekSeconds) { fastSeekSecondsToDisplay -> FadedAnimationForSeekFeedback(uiState.fastSeekSeconds) { fastSeekSecondsToDisplay ->
Box(modifier = Modifier.fillMaxSize()) { Box(modifier = Modifier.fillMaxSize()) {
FastSeekVisualFeedback( FastSeekVisualFeedback(
modifier = Modifier.align(Alignment.Center), modifier = Modifier.align(Alignment.Center),
@ -122,14 +108,21 @@ fun EmbeddedGestureUIPreview() {
Surface(modifier = Modifier.wrapContentSize(), color = Color.DarkGray) { Surface(modifier = Modifier.wrapContentSize(), color = Color.DarkGray) {
EmbeddedGestureUI( EmbeddedGestureUI(
modifier = Modifier, modifier = Modifier,
hideUi = { }, viewModel = object : VideoPlayerViewModelDummy() {
showUi = { }, override fun switchToEmbeddedView() {
uiVissible = false, println("switch to fullscreen")
fastSeekSeconds = 0, }
switchToFullscreen = { println("switch to fullscreen") },
embeddedDraggedDownBy = { println("embedded dragged down") }, override fun embeddedDraggedDown(offset: Float) {
fastSeek = { println("Fast seek by $it steps") }, println("embedded view dragged down by $offset")
fastSeekFinished = {}) }
override fun fastSeek(steps: Int) {
println("fast seek by $steps steps")
}
},
uiState = VideoPlayerUIState.DEFAULT,
)
} }
} }
} }

View File

@ -21,14 +21,12 @@
package net.newpipe.newplayer.ui.videoplayer.gesture_ui package net.newpipe.newplayer.ui.videoplayer.gesture_ui
import android.app.Activity
import androidx.compose.animation.AnimatedVisibility import androidx.compose.animation.AnimatedVisibility
import androidx.compose.animation.core.Spring import androidx.compose.animation.core.Spring
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.fadeIn
import androidx.compose.animation.fadeOut 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.Box
import androidx.compose.foundation.layout.Row import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.fillMaxSize import androidx.compose.foundation.layout.fillMaxSize
@ -44,9 +42,13 @@ import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Color import androidx.compose.ui.graphics.Color
import androidx.compose.ui.layout.onGloballyPositioned import androidx.compose.ui.layout.onGloballyPositioned
import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.tooling.preview.Preview 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.theme.VideoPlayerTheme
import net.newpipe.newplayer.ui.videoplayer.FAST_SEEK_MODE_DURATION import net.newpipe.newplayer.utils.getDefaultBrightness
private enum class IndicatorMode { private enum class IndicatorMode {
NONE, NONE,
@ -57,17 +59,8 @@ private enum class IndicatorMode {
@Composable @Composable
fun FullscreenGestureUI( fun FullscreenGestureUI(
modifier: Modifier = Modifier, modifier: Modifier = Modifier,
uiVisible: Boolean, viewModel: VideoPlayerViewModel,
fastSeekSeconds: Int, uiState: VideoPlayerUIState
volume: Float,
brightnes: Float,
hideUi: () -> Unit,
showUi: () -> Unit,
fastSeek: (Int) -> Unit,
fastSeekFinished: () -> Unit,
switchToEmbeddedView: () -> Unit,
volumeChange: (Float) -> Unit,
brightnesChange: (Float) -> Unit
) { ) {
var heightPx by remember { var heightPx by remember {
@ -80,13 +73,17 @@ fun FullscreenGestureUI(
val defaultOnRegularTap = { val defaultOnRegularTap = {
if (uiVisible) { if (uiState.uiVisible) {
hideUi() viewModel.hideUi()
} else { } else {
showUi() viewModel.showUi()
} }
} }
val activity = LocalContext.current as Activity
val defaultBrightness = getDefaultBrightness(activity)
Box(modifier = modifier.onGloballyPositioned { coordinates -> Box(modifier = modifier.onGloballyPositioned { coordinates ->
heightPx = coordinates.size.height.toFloat() heightPx = coordinates.size.height.toFloat()
}) { }) {
@ -97,9 +94,9 @@ fun FullscreenGestureUI(
onRegularTap = defaultOnRegularTap, onRegularTap = defaultOnRegularTap,
onMultiTap = { onMultiTap = {
println("multitap ${-it}") println("multitap ${-it}")
fastSeek(-it) viewModel.fastSeek(-it)
}, },
onMultiTapFinished = fastSeekFinished, onMultiTapFinished = viewModel::finishFastSeek,
onUp = { onUp = {
indicatorMode = IndicatorMode.NONE indicatorMode = IndicatorMode.NONE
}, },
@ -110,13 +107,13 @@ fun FullscreenGestureUI(
indicatorMode = IndicatorMode.BRIGHTNESS_INDICATOR_VISSIBLE indicatorMode = IndicatorMode.BRIGHTNESS_INDICATOR_VISSIBLE
if (heightPx != 0f) { if (heightPx != 0f) {
brightnesChange(-change.y / heightPx) viewModel.brightnessChange(-change.y / heightPx, defaultBrightness)
} }
} }
} }
) { ) {
FadedAnimationForSeekFeedback( FadedAnimationForSeekFeedback(
fastSeekSeconds, uiState.fastSeekSeconds,
backwards = true backwards = true
) { fastSeekSecondsToDisplay -> ) { fastSeekSecondsToDisplay ->
Box(modifier = Modifier.fillMaxSize()) { Box(modifier = Modifier.fillMaxSize()) {
@ -134,7 +131,7 @@ fun FullscreenGestureUI(
onRegularTap = defaultOnRegularTap, onRegularTap = defaultOnRegularTap,
onMovement = { movement -> onMovement = { movement ->
if (0 < movement.y) { if (0 < movement.y) {
switchToEmbeddedView() viewModel.switchToEmbeddedView()
} }
} }
) )
@ -142,8 +139,8 @@ fun FullscreenGestureUI(
modifier = Modifier modifier = Modifier
.weight(1f), .weight(1f),
onRegularTap = defaultOnRegularTap, onRegularTap = defaultOnRegularTap,
onMultiTap = fastSeek, onMultiTap = viewModel::fastSeek,
onMultiTapFinished = fastSeekFinished, onMultiTapFinished = viewModel::finishFastSeek,
onUp = { onUp = {
indicatorMode = IndicatorMode.NONE indicatorMode = IndicatorMode.NONE
}, },
@ -153,12 +150,12 @@ fun FullscreenGestureUI(
) { ) {
indicatorMode = IndicatorMode.VOLUME_INDICATOR_VISSIBLE indicatorMode = IndicatorMode.VOLUME_INDICATOR_VISSIBLE
if (heightPx != 0f) { if (heightPx != 0f) {
volumeChange(-change.y / heightPx) viewModel.volumeChange(-change.y / heightPx)
} }
} }
} }
) { ) {
FadedAnimationForSeekFeedback(fastSeekSeconds) { fastSeekSecondsToDisplay -> FadedAnimationForSeekFeedback(uiState.fastSeekSeconds) { fastSeekSecondsToDisplay ->
Box(modifier = Modifier.fillMaxSize()) { Box(modifier = Modifier.fillMaxSize()) {
FastSeekVisualFeedback( FastSeekVisualFeedback(
modifier = Modifier.align(Alignment.CenterStart), modifier = Modifier.align(Alignment.CenterStart),
@ -174,7 +171,7 @@ fun FullscreenGestureUI(
modifier = Modifier.align(Alignment.Center), modifier = Modifier.align(Alignment.Center),
visible = indicatorMode == IndicatorMode.VOLUME_INDICATOR_VISSIBLE, visible = indicatorMode == IndicatorMode.VOLUME_INDICATOR_VISSIBLE,
) { ) {
VolumeCircle(volumeFraction = volume) VolumeCircle(volumeFraction = uiState.soundVolume)
} }
IndicatorAnimation( IndicatorAnimation(
@ -182,7 +179,7 @@ fun FullscreenGestureUI(
visible = indicatorMode == IndicatorMode.BRIGHTNESS_INDICATOR_VISSIBLE, visible = indicatorMode == IndicatorMode.BRIGHTNESS_INDICATOR_VISSIBLE,
) { ) {
VolumeCircle( VolumeCircle(
volumeFraction = brightnes, volumeFraction = uiState.brightness ?: defaultBrightness,
modifier = Modifier.align(Alignment.Center), modifier = Modifier.align(Alignment.Center),
isBrightness = true isBrightness = true
) )
@ -233,17 +230,13 @@ fun FullscreenGestureUIPreview() {
Surface(modifier = Modifier.wrapContentSize(), color = Color.DarkGray) { Surface(modifier = Modifier.wrapContentSize(), color = Color.DarkGray) {
FullscreenGestureUI( FullscreenGestureUI(
modifier = Modifier, modifier = Modifier,
hideUi = { }, object : VideoPlayerViewModelDummy() {
showUi = { }, override fun fastSeek(steps: Int) {
uiVisible = false, println("fast seek by $steps steps")
fastSeekSeconds = 0, }
volume = 0f, },
brightnes = 0f, VideoPlayerUIState.DEFAULT
fastSeek = { println("fast seek by $it steps") }, )
fastSeekFinished = {},
switchToEmbeddedView = {},
brightnesChange = {},
volumeChange = {})
} }
} }
} }
@ -256,7 +249,7 @@ fun FullscreenGestureUIPreviewInteractive() {
mutableStateOf(0) mutableStateOf(0)
} }
var brightnesValue by remember { var brightnessValue by remember {
mutableStateOf(0f) mutableStateOf(0f)
} }
@ -272,23 +265,38 @@ fun FullscreenGestureUIPreviewInteractive() {
Surface(modifier = Modifier.wrapContentSize(), color = Color.Gray) { Surface(modifier = Modifier.wrapContentSize(), color = Color.Gray) {
FullscreenGestureUI( FullscreenGestureUI(
modifier = Modifier, modifier = Modifier,
hideUi = { uiVisible = false }, object : VideoPlayerViewModelDummy() {
showUi = { uiVisible = true }, override fun hideUi() {
uiVisible = uiVisible, uiVisible = false
fastSeekSeconds = seekSeconds, }
volume = soundVolume,
brightnes = brightnesValue, override fun showUi() {
fastSeek = { seekSeconds = it * 10 }, uiVisible = true
fastSeekFinished = { }
seekSeconds = 0
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 = {}, uiState = VideoPlayerUIState.DEFAULT.copy(
brightnesChange = { uiVissible = uiVisible,
brightnesValue = (brightnesValue + it).coerceIn(0f, 1f) fastSeekSeconds = seekSeconds,
}, soundVolume = soundVolume,
volumeChange = { brightness = brightnessValue
soundVolume = (soundVolume + it).coerceIn(0f, 1f) ),
}) )
} }
AnimatedVisibility(uiVisible) { AnimatedVisibility(uiVisible) {

View File

@ -24,7 +24,6 @@ import android.annotation.SuppressLint
import android.app.Activity import android.app.Activity
import android.content.Context import android.content.Context
import android.content.ContextWrapper import android.content.ContextWrapper
import android.view.Window
import android.view.WindowManager import android.view.WindowManager
import androidx.compose.runtime.Composable import androidx.compose.runtime.Composable
import androidx.compose.runtime.DisposableEffect import androidx.compose.runtime.DisposableEffect
@ -45,14 +44,14 @@ fun LockScreenOrientation(orientation: Int) {
} }
@SuppressLint("NewApi") @SuppressLint("NewApi")
fun getScreenBrightnes(activity: Activity) : Float { fun getDefaultBrightness(activity: Activity) : Float {
val window = activity.window val window = activity.window
val layout = window.attributes as WindowManager.LayoutParams val layout = window.attributes as WindowManager.LayoutParams
return layout.screenBrightness return if(layout.screenBrightness < 0) 0.5f else layout.screenBrightness
} }
@SuppressLint("NewApi") @SuppressLint("NewApi")
fun setScreenBrightnes(value:Float, activity: Activity) { fun setScreenBrightness(value:Float, activity: Activity) {
val window = activity.window val window = activity.window
val layout = window.attributes as WindowManager.LayoutParams val layout = window.attributes as WindowManager.LayoutParams
layout.screenBrightness = value layout.screenBrightness = value