diff --git a/new-player/src/main/java/net/newpipe/newplayer/ui/NewPlayerUI.kt b/new-player/src/main/java/net/newpipe/newplayer/ui/NewPlayerUI.kt index 94057ab..d99e724 100644 --- a/new-player/src/main/java/net/newpipe/newplayer/ui/NewPlayerUI.kt +++ b/new-player/src/main/java/net/newpipe/newplayer/ui/NewPlayerUI.kt @@ -53,7 +53,7 @@ import androidx.core.view.WindowCompat import androidx.core.view.WindowInsetsCompat import androidx.core.view.WindowInsetsControllerCompat import androidx.lifecycle.Lifecycle -import androidx.lifecycle.LifecycleEventObserver + import androidx.media3.common.Player import androidx.media3.common.util.UnstableApi 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.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 import net.newpipe.newplayer.utils.setScreenBrightness @@ -79,11 +80,6 @@ fun NewPlayerUI( VideoPlayerLoadingPlaceholder(viewModel.uiState.collectAsState().value.embeddedUiRatio) } else { 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 view = LocalView.current @@ -93,8 +89,6 @@ fun NewPlayerUI( windowInsetsController.systemBarsBehavior = WindowInsetsControllerCompat.BEHAVIOR_SHOW_TRANSIENT_BARS_BY_SWIPE - val lifecycleOwner = LocalLifecycleOwner.current - val defaultBrightness = getDefaultBrightness(activity) // 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) { Log.d(TAG, "New Brightnes: ${uiState.brightness}") setScreenBrightness( @@ -170,48 +146,7 @@ fun NewPlayerUI( if (uiState.uiMode == UIModeState.PLACEHOLDER) { VideoPlayerLoadingPlaceholder(uiState.embeddedUiRatio) } else { - 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) - } - } + VideoPlayerUi(viewModel = viewModel, uiState = uiState) } } } diff --git a/new-player/src/main/java/net/newpipe/newplayer/ui/videoplayer/VideoPlayerControllerUI.kt b/new-player/src/main/java/net/newpipe/newplayer/ui/videoplayer/VideoPlayerControllerUI.kt index b052a43..93d5f41 100644 --- a/new-player/src/main/java/net/newpipe/newplayer/ui/videoplayer/VideoPlayerControllerUI.kt +++ b/new-player/src/main/java/net/newpipe/newplayer/ui/videoplayer/VideoPlayerControllerUI.kt @@ -20,6 +20,7 @@ package net.newpipe.newplayer.ui.videoplayer +import androidx.annotation.OptIn import androidx.compose.animation.AnimatedVisibility import androidx.compose.foundation.layout.Box 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.tooling.preview.Preview import androidx.compose.ui.unit.dp +import androidx.media3.common.util.UnstableApi import net.newpipe.newplayer.model.NewPlayerUIState import net.newpipe.newplayer.model.NewPlayerViewModel import net.newpipe.newplayer.model.NewPlayerViewModelDummy @@ -50,6 +52,7 @@ import net.newpipe.newplayer.utils.getInsets val CONTROLLER_UI_BACKGROUND_COLOR = Color(0x75000000) val STREAMSELECT_UI_BACKGROUND_COLOR = Color(0xba000000) +@OptIn(UnstableApi::class) @Composable fun VideoPlayerControllerUI( viewModel: NewPlayerViewModel, uiState: NewPlayerUIState @@ -136,6 +139,7 @@ fun PreviewBackgroundSurface( // Preview /////////////////////////////////////////////////////////////////// +@OptIn(UnstableApi::class) @Preview(device = "spec:width=1080px,height=600px,dpi=440,orientation=landscape") @Composable fun VideoPlayerControllerUIPreviewEmbedded() { @@ -146,6 +150,7 @@ fun VideoPlayerControllerUIPreviewEmbedded() { } } +@OptIn(UnstableApi::class) @Preview(device = "spec:width=2340px,height=1080px,dpi=440,orientation=landscape") @Composable fun VideoPlayerControllerUIPreviewLandscape() { @@ -156,6 +161,7 @@ fun VideoPlayerControllerUIPreviewLandscape() { } } +@OptIn(UnstableApi::class) @Preview(device = "spec:width=2340px,height=1080px,dpi=440,orientation=portrait") @Composable fun VideoPlayerControllerUIPreviewPortrait() { diff --git a/new-player/src/main/java/net/newpipe/newplayer/ui/videoplayer/VideoPlayerUI.kt b/new-player/src/main/java/net/newpipe/newplayer/ui/videoplayer/VideoPlayerUI.kt new file mode 100644 index 0000000..3cfb752 --- /dev/null +++ b/new-player/src/main/java/net/newpipe/newplayer/ui/videoplayer/VideoPlayerUI.kt @@ -0,0 +1,123 @@ +/* NewPlayer + * + * @author Christian Schabesberger + * + * Copyright (C) NewPipe e.V. 2024 + * + * 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 . + */ + +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) + + } + } +} \ No newline at end of file