restructure NewPlayerUI and VideoPlayerUI

This commit is contained in:
Christian Schabesberger 2024-09-21 13:21:35 +02:00
parent a222a7c9a0
commit 25593c8ed6
3 changed files with 132 additions and 68 deletions

View File

@ -53,7 +53,7 @@ import androidx.core.view.WindowCompat
import androidx.core.view.WindowInsetsCompat import androidx.core.view.WindowInsetsCompat
import androidx.core.view.WindowInsetsControllerCompat import androidx.core.view.WindowInsetsControllerCompat
import androidx.lifecycle.Lifecycle import androidx.lifecycle.Lifecycle
import androidx.lifecycle.LifecycleEventObserver
import androidx.media3.common.Player import androidx.media3.common.Player
import androidx.media3.common.util.UnstableApi import androidx.media3.common.util.UnstableApi
import net.newpipe.newplayer.model.UIModeState import net.newpipe.newplayer.model.UIModeState
@ -62,6 +62,7 @@ import net.newpipe.newplayer.model.NewPlayerViewModelDummy
import net.newpipe.newplayer.ui.streamselect.StreamSelectUI import net.newpipe.newplayer.ui.streamselect.StreamSelectUI
import net.newpipe.newplayer.ui.theme.VideoPlayerTheme import net.newpipe.newplayer.ui.theme.VideoPlayerTheme
import net.newpipe.newplayer.ui.videoplayer.VideoPlayerControllerUI 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.LockScreenOrientation
import net.newpipe.newplayer.utils.getDefaultBrightness import net.newpipe.newplayer.utils.getDefaultBrightness
import net.newpipe.newplayer.utils.setScreenBrightness import net.newpipe.newplayer.utils.setScreenBrightness
@ -79,11 +80,6 @@ fun NewPlayerUI(
VideoPlayerLoadingPlaceholder(viewModel.uiState.collectAsState().value.embeddedUiRatio) VideoPlayerLoadingPlaceholder(viewModel.uiState.collectAsState().value.embeddedUiRatio)
} else { } else {
val uiState by viewModel.uiState.collectAsState() val uiState by viewModel.uiState.collectAsState()
val exoPlayer by viewModel.newPlayer?.exoPlayer!!.collectAsState()
var lifecycle by remember {
mutableStateOf(Lifecycle.Event.ON_CREATE)
}
val activity = LocalContext.current as Activity val activity = LocalContext.current as Activity
val view = LocalView.current val view = LocalView.current
@ -93,8 +89,6 @@ fun NewPlayerUI(
windowInsetsController.systemBarsBehavior = windowInsetsController.systemBarsBehavior =
WindowInsetsControllerCompat.BEHAVIOR_SHOW_TRANSIENT_BARS_BY_SWIPE WindowInsetsControllerCompat.BEHAVIOR_SHOW_TRANSIENT_BARS_BY_SWIPE
val lifecycleOwner = LocalLifecycleOwner.current
val defaultBrightness = getDefaultBrightness(activity) val defaultBrightness = getDefaultBrightness(activity)
// Setup fullscreen // Setup fullscreen
@ -141,24 +135,6 @@ fun NewPlayerUI(
} }
} }
// Prepare stuff for the SurfaceView to which the video will be rendered
DisposableEffect(lifecycleOwner) {
val observer = LifecycleEventObserver { _, event ->
lifecycle = event
}
lifecycleOwner.lifecycle.addObserver(observer)
onDispose {
lifecycleOwner.lifecycle.removeObserver(observer)
}
}
val displayMetrics = activity.resources.displayMetrics
val screenRatio =
displayMetrics.widthPixels.toFloat() / displayMetrics.heightPixels.toFloat()
LaunchedEffect(key1 = uiState.brightness) { LaunchedEffect(key1 = uiState.brightness) {
Log.d(TAG, "New Brightnes: ${uiState.brightness}") Log.d(TAG, "New Brightnes: ${uiState.brightness}")
setScreenBrightness( setScreenBrightness(
@ -170,48 +146,7 @@ fun NewPlayerUI(
if (uiState.uiMode == UIModeState.PLACEHOLDER) { if (uiState.uiMode == UIModeState.PLACEHOLDER) {
VideoPlayerLoadingPlaceholder(uiState.embeddedUiRatio) VideoPlayerLoadingPlaceholder(uiState.embeddedUiRatio)
} else { } else {
Surface( VideoPlayerUi(viewModel = viewModel, uiState = uiState)
modifier = Modifier.then(
if (uiState.uiMode.fullscreen) Modifier.fillMaxSize()
else Modifier
.fillMaxWidth()
.aspectRatio(uiState.embeddedUiRatio)
), color = Color.Black
) {
exoPlayer?.let { exoPlayer ->
Box(contentAlignment = Alignment.Center) {
PlaySurface(
player = exoPlayer,
lifecycle = lifecycle,
fitMode = uiState.contentFitMode,
uiRatio = if (uiState.uiMode.fullscreen) screenRatio
else uiState.embeddedUiRatio,
contentRatio = uiState.contentRatio
)
}
}
// the checks if VideoPlayerControllerUI should be visible or not are done by
// The VideoPlayerControllerUI composable itself. This is because Visibility of
// the controller is more complicated than just using a simple if statement.
VideoPlayerControllerUI(
viewModel, uiState = uiState
)
AnimatedVisibility(visible = uiState.uiMode.isStreamSelect) {
StreamSelectUI(
viewModel = viewModel,
uiState = uiState,
isChapterSelect = false
)
}
AnimatedVisibility(visible = uiState.uiMode.isChapterSelect) {
StreamSelectUI(viewModel = viewModel, uiState = uiState, isChapterSelect = true)
}
}
} }
} }
} }

View File

@ -20,6 +20,7 @@
package net.newpipe.newplayer.ui.videoplayer package net.newpipe.newplayer.ui.videoplayer
import androidx.annotation.OptIn
import androidx.compose.animation.AnimatedVisibility import androidx.compose.animation.AnimatedVisibility
import androidx.compose.foundation.layout.Box import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.defaultMinSize import androidx.compose.foundation.layout.defaultMinSize
@ -38,6 +39,7 @@ 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 androidx.compose.ui.unit.dp import androidx.compose.ui.unit.dp
import androidx.media3.common.util.UnstableApi
import net.newpipe.newplayer.model.NewPlayerUIState import net.newpipe.newplayer.model.NewPlayerUIState
import net.newpipe.newplayer.model.NewPlayerViewModel import net.newpipe.newplayer.model.NewPlayerViewModel
import net.newpipe.newplayer.model.NewPlayerViewModelDummy import net.newpipe.newplayer.model.NewPlayerViewModelDummy
@ -50,6 +52,7 @@ import net.newpipe.newplayer.utils.getInsets
val CONTROLLER_UI_BACKGROUND_COLOR = Color(0x75000000) val CONTROLLER_UI_BACKGROUND_COLOR = Color(0x75000000)
val STREAMSELECT_UI_BACKGROUND_COLOR = Color(0xba000000) val STREAMSELECT_UI_BACKGROUND_COLOR = Color(0xba000000)
@OptIn(UnstableApi::class)
@Composable @Composable
fun VideoPlayerControllerUI( fun VideoPlayerControllerUI(
viewModel: NewPlayerViewModel, uiState: NewPlayerUIState viewModel: NewPlayerViewModel, uiState: NewPlayerUIState
@ -136,6 +139,7 @@ fun PreviewBackgroundSurface(
// Preview // Preview
/////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////
@OptIn(UnstableApi::class)
@Preview(device = "spec:width=1080px,height=600px,dpi=440,orientation=landscape") @Preview(device = "spec:width=1080px,height=600px,dpi=440,orientation=landscape")
@Composable @Composable
fun VideoPlayerControllerUIPreviewEmbedded() { fun VideoPlayerControllerUIPreviewEmbedded() {
@ -146,6 +150,7 @@ fun VideoPlayerControllerUIPreviewEmbedded() {
} }
} }
@OptIn(UnstableApi::class)
@Preview(device = "spec:width=2340px,height=1080px,dpi=440,orientation=landscape") @Preview(device = "spec:width=2340px,height=1080px,dpi=440,orientation=landscape")
@Composable @Composable
fun VideoPlayerControllerUIPreviewLandscape() { fun VideoPlayerControllerUIPreviewLandscape() {
@ -156,6 +161,7 @@ fun VideoPlayerControllerUIPreviewLandscape() {
} }
} }
@OptIn(UnstableApi::class)
@Preview(device = "spec:width=2340px,height=1080px,dpi=440,orientation=portrait") @Preview(device = "spec:width=2340px,height=1080px,dpi=440,orientation=portrait")
@Composable @Composable
fun VideoPlayerControllerUIPreviewPortrait() { fun VideoPlayerControllerUIPreviewPortrait() {

View File

@ -0,0 +1,123 @@
/* NewPlayer
*
* @author Christian Schabesberger
*
* Copyright (C) NewPipe e.V. 2024 <code(at)newpipe-ev.de>
*
* NewPlayer is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* NewPlayer is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with NewPlayer. If not, see <http://www.gnu.org/licenses/>.
*/
package net.newpipe.newplayer.ui.videoplayer
import android.app.Activity
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.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.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.lifecycle.Lifecycle
import androidx.media3.common.util.UnstableApi
import net.newpipe.newplayer.model.NewPlayerUIState
import net.newpipe.newplayer.model.NewPlayerViewModel
import net.newpipe.newplayer.ui.PlaySurface
import net.newpipe.newplayer.ui.streamselect.StreamSelectUI
import androidx.lifecycle.LifecycleEventObserver
@OptIn(UnstableApi::class)
@Composable
fun VideoPlayerUi(viewModel: NewPlayerViewModel, uiState: NewPlayerUIState) {
val exoPlayer by viewModel.newPlayer?.exoPlayer!!.collectAsState()
var lifecycle by remember {
mutableStateOf(Lifecycle.Event.ON_CREATE)
}
val activity = LocalContext.current as Activity
val displayMetrics = activity.resources.displayMetrics
val screenRatio =
displayMetrics.widthPixels.toFloat() / displayMetrics.heightPixels.toFloat()
val lifecycleOwner = LocalLifecycleOwner.current
// Prepare stuff for the SurfaceView to which the video will be rendered
DisposableEffect(lifecycleOwner) {
val observer = LifecycleEventObserver { _, event ->
lifecycle = event
}
lifecycleOwner.lifecycle.addObserver(observer)
onDispose {
lifecycleOwner.lifecycle.removeObserver(observer)
}
}
Surface(
modifier = Modifier.then(
if (uiState.uiMode.fullscreen) Modifier.fillMaxSize()
else Modifier
.fillMaxWidth()
.aspectRatio(uiState.embeddedUiRatio)
), color = Color.Black
) {
exoPlayer?.let { exoPlayer ->
Box(contentAlignment = Alignment.Center) {
PlaySurface(
player = exoPlayer,
lifecycle = lifecycle,
fitMode = uiState.contentFitMode,
uiRatio = if (uiState.uiMode.fullscreen) screenRatio
else uiState.embeddedUiRatio,
contentRatio = uiState.contentRatio
)
}
}
// the checks if VideoPlayerControllerUI should be visible or not are done by
// The VideoPlayerControllerUI composable itself. This is because Visibility of
// the controller is more complicated than just using a simple if statement.
VideoPlayerControllerUI(
viewModel, uiState = uiState
)
AnimatedVisibility(visible = uiState.uiMode.isStreamSelect) {
StreamSelectUI(
viewModel = viewModel,
uiState = uiState,
isChapterSelect = false
)
}
AnimatedVisibility(visible = uiState.uiMode.isChapterSelect) {
StreamSelectUI(viewModel = viewModel, uiState = uiState, isChapterSelect = true)
}
}
}