simplify ui change

This commit is contained in:
Christian Schabesberger 2024-09-21 15:37:12 +02:00
parent 25593c8ed6
commit 87cc8a2c77
15 changed files with 173 additions and 116 deletions

View File

@ -32,8 +32,10 @@ enum class PlayMode {
EMBEDDED_VIDEO,
FULLSCREEN_VIDEO,
PIP,
BACKGROUND,
AUDIO_FOREGROUND,
BACKGROUND_VIDEO,
BACKGROUND_AUDIO,
FULLSCREEN_AUDIO,
EMBEDDED_AUDIO
}
enum class RepeatMode {

View File

@ -44,8 +44,7 @@ interface NewPlayerViewModel {
fun pause()
fun prevStream()
fun nextStream()
fun switchToFullscreen(embeddedUiConfig: EmbeddedUiConfig)
fun switchToEmbeddedView()
fun changeUiMode(newUiModeState: UIModeState, embeddedUiConfig: EmbeddedUiConfig)
fun onBackPressed()
fun showUi()
fun hideUi()
@ -56,8 +55,6 @@ interface NewPlayerViewModel {
fun finishFastSeek()
fun brightnessChange(changeRate: Float, systemBrightness: Float)
fun volumeChange(changeRate: Float)
fun openStreamSelection(selectChapter: Boolean, embeddedUiConfig: EmbeddedUiConfig)
fun closeStreamSelection()
fun chapterSelected(chapterId: Int)
fun streamSelected(streamId: Int)
fun cycleRepeatMode()

View File

@ -1,6 +1,7 @@
package net.newpipe.newplayer.model
import android.os.Bundle
import androidx.media3.common.util.UnstableApi
import kotlinx.coroutines.flow.MutableSharedFlow
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.SharedFlow
@ -8,6 +9,7 @@ import kotlinx.coroutines.flow.asSharedFlow
import net.newpipe.newplayer.NewPlayer
import net.newpipe.newplayer.ui.ContentScale
@UnstableApi
open class NewPlayerViewModelDummy : NewPlayerViewModel {
override var newPlayer: NewPlayer? = null
override val uiState = MutableStateFlow(NewPlayerUIState.DEFAULT)
@ -25,18 +27,10 @@ open class NewPlayerViewModelDummy : NewPlayerViewModel {
println("dummy impl")
}
override fun switchToEmbeddedView() {
println("dummy impl")
}
override fun onBackPressed() {
println("dummy impl")
}
override fun switchToFullscreen(embeddedUiConfig: EmbeddedUiConfig) {
println("dummy impl")
}
override fun showUi() {
println("dummy impl")
}
@ -73,14 +67,6 @@ open class NewPlayerViewModelDummy : NewPlayerViewModel {
println("dummy impl")
}
override fun openStreamSelection(selectChapter: Boolean, embeddedUiConfig: EmbeddedUiConfig) {
println("dummy impl")
}
override fun closeStreamSelection() {
println("dummy impl")
}
override fun chapterSelected(chapterId: Int) {
println("dummp impl chapter selected: $chapterId")
}
@ -128,4 +114,8 @@ open class NewPlayerViewModelDummy : NewPlayerViewModel {
override fun nextStream() {
println("dummy impl")
}
override fun changeUiMode(newUiModeState: UIModeState, embeddedUiConfig: EmbeddedUiConfig) {
println("dummy uiMode change: New UI Mode State: $newUiModeState")
}
}

View File

@ -150,7 +150,7 @@ class NewPlayerViewModelImpl @Inject constructor(
mutableUiState.update {
it.copy(playing = isPlaying, isLoading = false)
}
if (isPlaying && uiState.value.uiMode.controllerUiVisible) {
if (isPlaying && uiState.value.uiMode.videoControllerUiVisible) {
resetHideUiDelayedJob()
} else {
uiVisibilityJob?.cancel()
@ -329,6 +329,41 @@ class NewPlayerViewModelImpl @Inject constructor(
}
}
override fun changeUiMode(newUiModeState: UIModeState, embeddedUiConfig: EmbeddedUiConfig) {
if (!uiState.value.uiMode.fullscreen) {
this.embeddedUiConfig = embeddedUiConfig
}
if (newUiModeState.isStreamSelect) {
resetPlaylistProgressUpdaterJob()
uiVisibilityJob?.cancel()
}
if (newUiModeState.isChapterSelect) {
resetPlaylistProgressUpdaterJob()
uiVisibilityJob?.cancel()
}
if ((uiState.value.uiMode.isStreamSelect || uiState.value.uiMode.isChapterSelect)
&& (!newUiModeState.isStreamSelect && !newUiModeState.isChapterSelect)
) {
playlistProgressUpdaterJob?.cancel()
progressUpdaterJob?.cancel()
}
if(uiState.value.uiMode.fullscreen && !newUiModeState.fullscreen) {
uiVisibilityJob?.cancel()
finishFastSeek()
}
if(!uiState.value.uiMode.fullscreen && newUiModeState.fullscreen) {
uiVisibilityJob?.cancel()
finishFastSeek()
}
updateUiMode(newUiModeState)
}
override fun showUi() {
mutableUiState.update {
it.copy(uiMode = it.uiMode.getControllerUiVisibleState())
@ -430,13 +465,13 @@ class NewPlayerViewModelImpl @Inject constructor(
)
}
if (mutableUiState.value.uiMode.controllerUiVisible) {
if (mutableUiState.value.uiMode.videoControllerUiVisible) {
resetHideUiDelayedJob()
}
}
override fun finishFastSeek() {
if (mutableUiState.value.uiMode.controllerUiVisible) {
if (mutableUiState.value.uiMode.videoControllerUiVisible) {
resetHideUiDelayedJob()
}
@ -482,34 +517,6 @@ class NewPlayerViewModelImpl @Inject constructor(
}
}
override fun openStreamSelection(selectChapter: Boolean, embeddedUiConfig: EmbeddedUiConfig) {
uiVisibilityJob?.cancel()
if (!uiState.value.uiMode.fullscreen) {
this.embeddedUiConfig = embeddedUiConfig
}
updateUiMode(
if (selectChapter) uiState.value.uiMode.getChapterSelectUiState()
else uiState.value.uiMode.getStreamSelectUiState()
)
if (selectChapter) {
resetProgressUpdatePeriodicallyJob()
} else {
resetPlaylistProgressUpdaterJob()
}
}
override fun closeStreamSelection() {
playlistProgressUpdaterJob?.cancel()
progressUpdaterJob?.cancel()
updateUiMode(uiState.value.uiMode.getUiHiddenState())
}
override fun switchToEmbeddedView() {
uiVisibilityJob?.cancel()
finishFastSeek()
updateUiMode(UIModeState.EMBEDDED_VIDEO)
}
override fun onBackPressed() {
val nextMode = uiState.value.uiMode.getNextModeWhenBackPressed()
if (nextMode != null) {
@ -519,14 +526,6 @@ class NewPlayerViewModelImpl @Inject constructor(
}
}
override fun switchToFullscreen(embeddedUiConfig: EmbeddedUiConfig) {
uiVisibilityJob?.cancel()
finishFastSeek()
this.embeddedUiConfig = embeddedUiConfig
updateUiMode(UIModeState.FULLSCREEN_VIDEO)
}
override fun chapterSelected(chapterId: Int) {
newPlayer?.selectChapter(chapterId)
}

View File

@ -33,7 +33,12 @@ enum class UIModeState {
FULLSCREEN_VIDEO,
FULLSCREEN_VIDEO_CONTROLLER_UI,
FULLSCREEN_VIDEO_CHAPTER_SELECT,
FULLSCREEN_VIDEO_STREAM_SELECT;
FULLSCREEN_VIDEO_STREAM_SELECT,
EMBEDDED_AUDIO,
FULLSCREEN_AUDIO,
AUDIO_CHAPTER_SELECT,
AUDIO_STREAM_SELECT;
val fullscreen: Boolean
get() =
@ -44,10 +49,13 @@ enum class UIModeState {
FULLSCREEN_VIDEO_CONTROLLER_UI -> true
FULLSCREEN_VIDEO_CHAPTER_SELECT -> true
FULLSCREEN_VIDEO_STREAM_SELECT -> true
FULLSCREEN_AUDIO -> true
AUDIO_CHAPTER_SELECT -> true
AUDIO_STREAM_SELECT -> true
else -> false
}
val controllerUiVisible: Boolean
val videoControllerUiVisible: Boolean
get() =
when (this) {
EMBEDDED_VIDEO_CONTROLLER_UI -> true
@ -60,6 +68,7 @@ enum class UIModeState {
when (this) {
EMBEDDED_VIDEO_STREAM_SELECT -> true
FULLSCREEN_VIDEO_STREAM_SELECT -> true
AUDIO_STREAM_SELECT -> true
else -> false
}
@ -68,6 +77,7 @@ enum class UIModeState {
when (this) {
EMBEDDED_VIDEO_CHAPTER_SELECT -> true
FULLSCREEN_VIDEO_CHAPTER_SELECT -> true
AUDIO_CHAPTER_SELECT -> true
else -> false
}
@ -125,6 +135,10 @@ enum class UIModeState {
EMBEDDED_VIDEO_CHAPTER_SELECT -> EMBEDDED_VIDEO_STREAM_SELECT
EMBEDDED_VIDEO_CONTROLLER_UI -> EMBEDDED_VIDEO_STREAM_SELECT
FULLSCREEN_AUDIO -> AUDIO_STREAM_SELECT
EMBEDDED_AUDIO -> AUDIO_STREAM_SELECT
AUDIO_CHAPTER_SELECT -> AUDIO_STREAM_SELECT
else -> this
}
@ -138,6 +152,10 @@ enum class UIModeState {
EMBEDDED_VIDEO_STREAM_SELECT -> EMBEDDED_VIDEO_CHAPTER_SELECT
EMBEDDED_VIDEO_CONTROLLER_UI -> EMBEDDED_VIDEO_CHAPTER_SELECT
FULLSCREEN_AUDIO -> AUDIO_CHAPTER_SELECT
EMBEDDED_AUDIO -> AUDIO_CHAPTER_SELECT
AUDIO_STREAM_SELECT -> AUDIO_CHAPTER_SELECT
else -> this
}
@ -151,6 +169,10 @@ enum class UIModeState {
FULLSCREEN_VIDEO_CONTROLLER_UI -> PlayMode.FULLSCREEN_VIDEO
FULLSCREEN_VIDEO_CHAPTER_SELECT -> PlayMode.FULLSCREEN_VIDEO
FULLSCREEN_VIDEO_STREAM_SELECT -> PlayMode.FULLSCREEN_VIDEO
EMBEDDED_AUDIO -> PlayMode.EMBEDDED_AUDIO
FULLSCREEN_AUDIO -> PlayMode.FULLSCREEN_AUDIO
AUDIO_CHAPTER_SELECT -> PlayMode.FULLSCREEN_AUDIO
AUDIO_STREAM_SELECT -> PlayMode.FULLSCREEN_AUDIO
}
fun getNextModeWhenBackPressed() = when (this) {
@ -160,7 +182,13 @@ enum class UIModeState {
FULLSCREEN_VIDEO_STREAM_SELECT -> FULLSCREEN_VIDEO
FULLSCREEN_VIDEO_CHAPTER_SELECT -> FULLSCREEN_VIDEO
FULLSCREEN_VIDEO_CONTROLLER_UI -> EMBEDDED_VIDEO
else -> null
PLACEHOLDER -> null
EMBEDDED_VIDEO -> null
EMBEDDED_VIDEO_CONTROLLER_UI -> null
EMBEDDED_AUDIO -> null
FULLSCREEN_AUDIO -> EMBEDDED_AUDIO
AUDIO_CHAPTER_SELECT -> FULLSCREEN_AUDIO
AUDIO_STREAM_SELECT -> FULLSCREEN_AUDIO
}
companion object {
@ -170,8 +198,10 @@ enum class UIModeState {
PlayMode.EMBEDDED_VIDEO -> EMBEDDED_VIDEO
PlayMode.FULLSCREEN_VIDEO -> FULLSCREEN_VIDEO
PlayMode.PIP -> TODO()
PlayMode.BACKGROUND -> TODO()
PlayMode.AUDIO_FOREGROUND -> TODO()
PlayMode.BACKGROUND_VIDEO -> TODO()
PlayMode.BACKGROUND_AUDIO -> TODO()
PlayMode.FULLSCREEN_AUDIO -> FULLSCREEN_AUDIO
PlayMode.EMBEDDED_AUDIO -> EMBEDDED_AUDIO
}
}
}

View File

@ -40,7 +40,7 @@ import androidx.compose.ui.unit.dp
import net.newpipe.newplayer.ui.theme.VideoPlayerTheme
@Composable
fun VideoPlayerLoadingPlaceholder(aspectRatio: Float = 3F / 1F) {
fun LoadingPlaceholder(aspectRatio: Float = 3F / 1F) {
Surface(
modifier = Modifier
.fillMaxWidth()
@ -63,6 +63,6 @@ fun VideoPlayerLoadingPlaceholder(aspectRatio: Float = 3F / 1F) {
@Composable
fun VideoPlayerLoaidingPlaceholderPreview() {
VideoPlayerTheme {
VideoPlayerLoadingPlaceholder()
LoadingPlaceholder()
}
}

View File

@ -26,26 +26,16 @@ import android.util.Log
import android.view.SurfaceView
import androidx.activity.compose.BackHandler
import androidx.annotation.OptIn
import androidx.compose.animation.AnimatedVisibility
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.aspectRatio
import androidx.compose.foundation.layout.fillMaxHeight
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.material3.Surface
import androidx.compose.runtime.Composable
import androidx.compose.runtime.DisposableEffect
import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.collectAsState
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.runtime.setValue
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.platform.LocalLifecycleOwner
import androidx.compose.ui.platform.LocalView
import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.viewinterop.AndroidView
@ -59,9 +49,8 @@ import androidx.media3.common.util.UnstableApi
import net.newpipe.newplayer.model.UIModeState
import net.newpipe.newplayer.model.NewPlayerViewModel
import net.newpipe.newplayer.model.NewPlayerViewModelDummy
import net.newpipe.newplayer.ui.streamselect.StreamSelectUI
import net.newpipe.newplayer.ui.audioplayer.AudioPlayerUI
import net.newpipe.newplayer.ui.theme.VideoPlayerTheme
import net.newpipe.newplayer.ui.videoplayer.VideoPlayerControllerUI
import net.newpipe.newplayer.ui.videoplayer.VideoPlayerUi
import net.newpipe.newplayer.utils.LockScreenOrientation
import net.newpipe.newplayer.utils.getDefaultBrightness
@ -75,9 +64,9 @@ fun NewPlayerUI(
viewModel: NewPlayerViewModel?,
) {
if (viewModel == null) {
VideoPlayerLoadingPlaceholder()
LoadingPlaceholder()
} else if (viewModel.newPlayer == null) {
VideoPlayerLoadingPlaceholder(viewModel.uiState.collectAsState().value.embeddedUiRatio)
LoadingPlaceholder(viewModel.uiState.collectAsState().value.embeddedUiRatio)
} else {
val uiState by viewModel.uiState.collectAsState()
@ -142,12 +131,22 @@ fun NewPlayerUI(
)
}
// Set UI
if (uiState.uiMode == UIModeState.PLACEHOLDER) {
VideoPlayerLoadingPlaceholder(uiState.embeddedUiRatio)
} else {
if (uiState.uiMode == UIModeState.FULLSCREEN_VIDEO ||
uiState.uiMode == UIModeState.FULLSCREEN_VIDEO_CONTROLLER_UI ||
uiState.uiMode == UIModeState.FULLSCREEN_VIDEO_CHAPTER_SELECT ||
uiState.uiMode == UIModeState.FULLSCREEN_VIDEO_STREAM_SELECT ||
uiState.uiMode == UIModeState.EMBEDDED_VIDEO ||
uiState.uiMode == UIModeState.EMBEDDED_VIDEO_CONTROLLER_UI ||
uiState.uiMode == UIModeState.EMBEDDED_VIDEO_STREAM_SELECT ||
uiState.uiMode == UIModeState.EMBEDDED_VIDEO_CHAPTER_SELECT
) {
VideoPlayerUi(viewModel = viewModel, uiState = uiState)
} else if (uiState.uiMode == UIModeState.FULLSCREEN_AUDIO) {
AudioPlayerUI(viewModel = viewModel, uiState = uiState)
} else {
LoadingPlaceholder(uiState.embeddedUiRatio)
}
}
}

View File

@ -40,6 +40,7 @@ import net.newpipe.newplayer.R
import net.newpipe.newplayer.model.EmbeddedUiConfig
import net.newpipe.newplayer.model.NewPlayerUIState
import net.newpipe.newplayer.model.NewPlayerViewModel
import net.newpipe.newplayer.model.UIModeState
import net.newpipe.newplayer.utils.getEmbeddedUiConfig
@androidx.annotation.OptIn(UnstableApi::class)
@ -59,10 +60,7 @@ fun AudioPlayerTopBar(
AnimatedVisibility(visible = uiState.chapters.isNotEmpty()) {
IconButton(
onClick = {
viewModel.openStreamSelection(
selectChapter = true,
embeddedUiConfig
)
viewModel.changeUiMode(UIModeState.AUDIO_CHAPTER_SELECT, embeddedUiConfig)
},
) {
Icon(
@ -74,10 +72,7 @@ fun AudioPlayerTopBar(
AnimatedVisibility(visible = 1 < uiState.playList.size) {
IconButton(
onClick = {
viewModel.openStreamSelection(
selectChapter = false,
embeddedUiConfig
)
viewModel.changeUiMode(UIModeState.AUDIO_STREAM_SELECT, embeddedUiConfig)
},
) {
Icon(

View File

@ -20,6 +20,7 @@
package net.newpipe.newplayer.ui.streamselect
import android.app.Activity
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.automirrored.filled.PlaylistAdd
@ -37,18 +38,21 @@ import androidx.compose.material3.TopAppBarDefaults.topAppBarColors
import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.text.style.TextOverflow
import androidx.compose.ui.tooling.preview.Preview
import androidx.media3.common.util.UnstableApi
import net.newpipe.newplayer.R
import net.newpipe.newplayer.RepeatMode
import net.newpipe.newplayer.model.EmbeddedUiConfig
import net.newpipe.newplayer.model.NewPlayerUIState
import net.newpipe.newplayer.model.NewPlayerViewModel
import net.newpipe.newplayer.model.NewPlayerViewModelDummy
import net.newpipe.newplayer.ui.common.RepeatModeButton
import net.newpipe.newplayer.ui.common.ShuffleModeButton
import net.newpipe.newplayer.ui.theme.VideoPlayerTheme
import net.newpipe.newplayer.utils.getEmbeddedUiConfig
import net.newpipe.newplayer.utils.getLocale
import net.newpipe.newplayer.utils.getPlaylistDurationInMS
import net.newpipe.newplayer.utils.getTimeStringFromMs
@ -62,6 +66,12 @@ fun StreamSelectTopBar(
uiState: NewPlayerUIState
) {
val embeddedUiConfig =
if (LocalContext.current is Activity)
getEmbeddedUiConfig(activity = LocalContext.current as Activity)
else
EmbeddedUiConfig.DUMMY
TopAppBar(modifier = modifier,
colors = topAppBarColors(containerColor = Color.Transparent),
title = {
@ -91,7 +101,9 @@ fun StreamSelectTopBar(
}
IconButton(
onClick = viewModel::closeStreamSelection
onClick = {
viewModel.changeUiMode(uiState.uiMode.getUiHiddenState(), embeddedUiConfig)
}
) {
Icon(
imageVector = Icons.Filled.Close,

View File

@ -20,6 +20,7 @@
package net.newpipe.newplayer.ui.streamselect
import android.app.Activity
import androidx.annotation.OptIn
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Box
@ -36,15 +37,18 @@ import androidx.compose.material3.Surface
import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier
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 androidx.media3.common.util.UnstableApi
import net.newpipe.newplayer.model.EmbeddedUiConfig
import net.newpipe.newplayer.model.NewPlayerUIState
import net.newpipe.newplayer.model.NewPlayerViewModel
import net.newpipe.newplayer.model.NewPlayerViewModelDummy
import net.newpipe.newplayer.ui.theme.VideoPlayerTheme
import net.newpipe.newplayer.ui.videoplayer.STREAMSELECT_UI_BACKGROUND_COLOR
import net.newpipe.newplayer.utils.ReorderHapticFeedbackType
import net.newpipe.newplayer.utils.getEmbeddedUiConfig
import net.newpipe.newplayer.utils.getInsets
import net.newpipe.newplayer.utils.rememberReorderHapticFeedback
import sh.calvin.reorderable.ReorderableItem
@ -60,6 +64,12 @@ fun StreamSelectUI(
uiState: NewPlayerUIState
) {
val insets = getInsets()
val embeddedUiConfig = if (LocalContext.current is Activity)
getEmbeddedUiConfig(activity = LocalContext.current as Activity)
else
EmbeddedUiConfig.DUMMY
Surface(
modifier = Modifier.fillMaxSize(),
color = STREAMSELECT_UI_BACKGROUND_COLOR
@ -72,8 +82,12 @@ fun StreamSelectUI(
topBar = {
if (isChapterSelect) {
ChapterSelectTopBar(
onClose =
viewModel::closeStreamSelection
onClose = {
viewModel.changeUiMode(
uiState.uiMode.getUiHiddenState(),
embeddedUiConfig
)
}
)
} else {
StreamSelectTopBar(viewModel = viewModel, uiState = uiState)
@ -142,7 +156,9 @@ fun ReorderableStreamItemsList(
verticalArrangement = Arrangement.spacedBy(5.dp),
state = lazyListState
) {
itemsIndexed(uiState.playList, key = { _, item -> item.mediaId.toLong() }) { index, playlistItem ->
itemsIndexed(
uiState.playList,
key = { _, item -> item.mediaId.toLong() }) { index, playlistItem ->
ReorderableItem(
state = reorderableLazyListState,
key = playlistItem.mediaId.toLong()

View File

@ -60,7 +60,7 @@ fun VideoPlayerControllerUI(
val insets = getInsets()
AnimatedVisibility(uiState.uiMode.controllerUiVisible) {
AnimatedVisibility(uiState.uiMode.videoControllerUiVisible) {
Surface(
modifier = Modifier.fillMaxSize(), color = CONTROLLER_UI_BACKGROUND_COLOR
) {}
@ -82,7 +82,7 @@ fun VideoPlayerControllerUI(
}
}
AnimatedVisibility(uiState.uiMode.controllerUiVisible) {
AnimatedVisibility(uiState.uiMode.videoControllerUiVisible) {
AnimatedVisibility(visible = !uiState.isLoading) {
Box(modifier = Modifier.fillMaxSize()) {

View File

@ -77,10 +77,13 @@ fun BottomUI(
}
IconButton(
onClick = if (uiState.uiMode.fullscreen) viewModel::switchToEmbeddedView
else {
{ // <- head of lambda ... yea kotlin is weird
viewModel.switchToFullscreen(embeddedUiConfig)
onClick = if (uiState.uiMode.fullscreen) {
{
viewModel.changeUiMode(UIModeState.EMBEDDED_VIDEO, embeddedUiConfig)
}
} else {
{
viewModel.changeUiMode(UIModeState.FULLSCREEN_VIDEO, embeddedUiConfig)
}
}
) {
@ -94,7 +97,6 @@ fun BottomUI(
}
///////////////////////////////////////////////////////////////////
// Preview
///////////////////////////////////////////////////////////////////

View File

@ -53,6 +53,7 @@ import net.newpipe.newplayer.R
import net.newpipe.newplayer.model.NewPlayerUIState
import net.newpipe.newplayer.model.NewPlayerViewModel
import net.newpipe.newplayer.model.NewPlayerViewModelDummy
import net.newpipe.newplayer.model.UIModeState
import net.newpipe.newplayer.ui.theme.VideoPlayerTheme
import net.newpipe.newplayer.ui.theme.video_player_onSurface
import net.newpipe.newplayer.utils.getEmbeddedUiConfig
@ -103,7 +104,12 @@ fun TopUI(
}
AnimatedVisibility(visible = uiState.chapters.isNotEmpty()) {
IconButton(
onClick = { viewModel.openStreamSelection(selectChapter = true, embeddedUiConfig) },
onClick = {
viewModel.changeUiMode(
uiState.uiMode.getChapterSelectUiState(),
embeddedUiConfig
)
}
) {
Icon(
imageVector = Icons.AutoMirrored.Filled.MenuBook,
@ -114,8 +120,8 @@ fun TopUI(
AnimatedVisibility(visible = 1 < uiState.playList.size) {
IconButton(
onClick = {
viewModel.openStreamSelection(
selectChapter = false,
viewModel.changeUiMode(
uiState.uiMode.getStreamSelectUiState(),
embeddedUiConfig
)
},

View File

@ -23,6 +23,7 @@ package net.newpipe.newplayer.ui.videoplayer.gesture_ui
import android.app.Activity
import android.util.Log
import androidx.annotation.OptIn
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.fillMaxSize
@ -38,14 +39,17 @@ import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.tooling.preview.Preview
import androidx.media3.common.util.UnstableApi
import net.newpipe.newplayer.model.NewPlayerUIState
import net.newpipe.newplayer.model.NewPlayerViewModel
import net.newpipe.newplayer.model.NewPlayerViewModelDummy
import net.newpipe.newplayer.model.UIModeState
import net.newpipe.newplayer.ui.theme.VideoPlayerTheme
import net.newpipe.newplayer.utils.getEmbeddedUiConfig
private const val TAG = "EmbeddedGestureUI"
@OptIn(UnstableApi::class)
@Composable
fun EmbeddedGestureUI(
modifier: Modifier = Modifier, viewModel: NewPlayerViewModel, uiState: NewPlayerUIState
@ -66,7 +70,7 @@ fun EmbeddedGestureUI(
// this check is there to allow a temporary move up in the downward gesture
if (downwardMovementMode == false) {
viewModel.switchToFullscreen(embeddedUiConfig)
viewModel.changeUiMode(UIModeState.FULLSCREEN_VIDEO, embeddedUiConfig)
} else {
viewModel.embeddedDraggedDown(movement.y)
}
@ -78,7 +82,7 @@ fun EmbeddedGestureUI(
}
val defaultOnRegularTap = {
if (uiState.uiMode.controllerUiVisible) {
if (uiState.uiMode.videoControllerUiVisible) {
viewModel.hideUi()
} else {
viewModel.showUi()
@ -151,6 +155,7 @@ fun EmbeddedGestureUI(
}
@OptIn(UnstableApi::class)
@Preview(device = "spec:width=600px,height=400px,dpi=440,orientation=landscape")
@Composable
fun EmbeddedGestureUIPreview() {
@ -159,10 +164,6 @@ fun EmbeddedGestureUIPreview() {
EmbeddedGestureUI(
modifier = Modifier,
viewModel = object : NewPlayerViewModelDummy() {
override fun switchToEmbeddedView() {
println("switch to fullscreen")
}
override fun embeddedDraggedDown(offset: Float) {
println("embedded view dragged down by $offset")
}

View File

@ -22,6 +22,7 @@
package net.newpipe.newplayer.ui.videoplayer.gesture_ui
import android.app.Activity
import androidx.annotation.OptIn
import androidx.compose.animation.AnimatedVisibility
import androidx.compose.animation.core.Spring
import androidx.compose.animation.core.spring
@ -44,17 +45,20 @@ 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 androidx.media3.common.util.UnstableApi
import net.newpipe.newplayer.model.UIModeState
import net.newpipe.newplayer.model.NewPlayerUIState
import net.newpipe.newplayer.model.NewPlayerViewModel
import net.newpipe.newplayer.model.NewPlayerViewModelDummy
import net.newpipe.newplayer.ui.theme.VideoPlayerTheme
import net.newpipe.newplayer.utils.getDefaultBrightness
import net.newpipe.newplayer.utils.getEmbeddedUiConfig
private enum class IndicatorMode {
NONE, VOLUME_INDICATOR_VISSIBLE, BRIGHTNESS_INDICATOR_VISSIBLE
}
@OptIn(UnstableApi::class)
@Composable
fun FullscreenGestureUI(
modifier: Modifier = Modifier, viewModel: NewPlayerViewModel, uiState: NewPlayerUIState
@ -69,7 +73,7 @@ fun FullscreenGestureUI(
}
val defaultOnRegularTap = {
if (uiState.uiMode.controllerUiVisible) {
if (uiState.uiMode.videoControllerUiVisible) {
viewModel.hideUi()
} else {
viewModel.showUi()
@ -79,6 +83,7 @@ fun FullscreenGestureUI(
val activity = LocalContext.current as Activity
val defaultBrightness = getDefaultBrightness(activity)
val embeddedUiConfig = getEmbeddedUiConfig(activity = activity)
Box(modifier = modifier.onGloballyPositioned { coordinates ->
heightPx = coordinates.size.height.toFloat()
@ -119,7 +124,7 @@ fun FullscreenGestureUI(
onRegularTap = defaultOnRegularTap,
onMovement = { movement ->
if (0 < movement.y) {
viewModel.switchToEmbeddedView()
viewModel.changeUiMode(UIModeState.EMBEDDED_VIDEO, embeddedUiConfig)
}
},
onMultiTap = { count ->
@ -215,6 +220,7 @@ fun IndicatorAnimation(modifier: Modifier, visible: Boolean, content: @Composabl
}
@OptIn(UnstableApi::class)
@Preview(device = "spec:width=1080px,height=600px,dpi=440,orientation=landscape")
@Composable
fun FullscreenGestureUIPreview() {
@ -231,6 +237,7 @@ fun FullscreenGestureUIPreview() {
}
}
@OptIn(UnstableApi::class)
@Preview(device = "spec:parent=pixel_8,orientation=landscape")
@Composable
fun FullscreenGestureUIPreviewInteractive() {
@ -255,6 +262,7 @@ fun FullscreenGestureUIPreviewInteractive() {
Surface(modifier = Modifier.wrapContentSize(), color = Color.Gray) {
FullscreenGestureUI(
modifier = Modifier,
@OptIn(UnstableApi::class)
object : NewPlayerViewModelDummy() {
override fun hideUi() {
uiVisible = false