From 356744814caaffedaf225790e3ac785cac984fc6 Mon Sep 17 00:00:00 2001 From: Christian Schabesberger Date: Mon, 22 Jul 2024 15:44:10 +0200 Subject: [PATCH] fix project layout and don't use fullscreen acitvity anymore --- .../net/newpipe/newplayer/VideoPlayerView.kt | 44 ++--- .../newplayer/internal/VideoPlayerFragment.kt | 125 -------------- .../newplayer/internal/ui/VideoPlayerUI.kt | 152 ------------------ .../model/VideoPlayerViewModel.kt | 25 +-- .../ui/VideoPlayerControllerUI.kt | 8 +- .../ui/VideoPlayerLoadingPlaceholder.kt | 36 +++++ .../net/newpipe/newplayer/ui/VideoPlayerUI.kt | 149 +++++++++++++++++ .../{internal => }/ui/theme/Color.kt | 2 +- .../{internal => }/ui/theme/Theme.kt | 2 +- .../newplayer/{internal => }/ui/theme/Type.kt | 2 +- .../{internal => }/utils/VideoSize.kt | 2 +- .../newplayer/{internal => }/utils/utils.kt | 7 +- .../src/main/res/layout/video_player_view.xml | 10 +- .../newpipe/newplayer/testapp/MainActivity.kt | 13 +- .../src/main/res/layout/activity_main.xml | 2 +- 15 files changed, 237 insertions(+), 342 deletions(-) delete mode 100644 new-player/src/main/java/net/newpipe/newplayer/internal/VideoPlayerFragment.kt delete mode 100644 new-player/src/main/java/net/newpipe/newplayer/internal/ui/VideoPlayerUI.kt rename new-player/src/main/java/net/newpipe/newplayer/{internal => }/model/VideoPlayerViewModel.kt (94%) rename new-player/src/main/java/net/newpipe/newplayer/{internal => }/ui/VideoPlayerControllerUI.kt (98%) create mode 100644 new-player/src/main/java/net/newpipe/newplayer/ui/VideoPlayerLoadingPlaceholder.kt create mode 100644 new-player/src/main/java/net/newpipe/newplayer/ui/VideoPlayerUI.kt rename new-player/src/main/java/net/newpipe/newplayer/{internal => }/ui/theme/Color.kt (97%) rename new-player/src/main/java/net/newpipe/newplayer/{internal => }/ui/theme/Theme.kt (98%) rename new-player/src/main/java/net/newpipe/newplayer/{internal => }/ui/theme/Type.kt (97%) rename new-player/src/main/java/net/newpipe/newplayer/{internal => }/utils/VideoSize.kt (94%) rename new-player/src/main/java/net/newpipe/newplayer/{internal => }/utils/utils.kt (83%) diff --git a/new-player/src/main/java/net/newpipe/newplayer/VideoPlayerView.kt b/new-player/src/main/java/net/newpipe/newplayer/VideoPlayerView.kt index 25e2dd7..3e739cd 100644 --- a/new-player/src/main/java/net/newpipe/newplayer/VideoPlayerView.kt +++ b/new-player/src/main/java/net/newpipe/newplayer/VideoPlayerView.kt @@ -20,34 +20,21 @@ package net.newpipe.newplayer -import android.app.Activity import android.content.Context import android.util.AttributeSet import android.view.LayoutInflater import android.widget.FrameLayout -import androidx.appcompat.app.AppCompatActivity -import androidx.fragment.app.FragmentContainer -import androidx.fragment.app.FragmentContainerView +import androidx.compose.ui.platform.ComposeView +import androidx.compose.ui.platform.ViewCompositionStrategy import dagger.hilt.android.AndroidEntryPoint -import net.newpipe.newplayer.internal.VideoPlayerFragment +import net.newpipe.newplayer.model.VideoPlayerViewModel +import net.newpipe.newplayer.ui.VideoPlayerUI +import net.newpipe.newplayer.ui.theme.VideoPlayerTheme @AndroidEntryPoint class VideoPlayerView : FrameLayout { - val videoPlayerFragment:VideoPlayerFragment - - var maxLayoutRatio: Float - get() = videoPlayerFragment.maxLayoutRatio - set(value) {videoPlayerFragment.maxLayoutRatio=value} - - - var minLayoutRatio: Float - get() = videoPlayerFragment.minLayoutRatio - set(value) {videoPlayerFragment.minLayoutRatio = value} - - var fullScreenToggleListener: FullScreenToggleListener? - set(value) {videoPlayerFragment.fullScreenToggleListener = value} - get() = videoPlayerFragment.fullScreenToggleListener + var viewModel: VideoPlayerViewModel? = null @JvmOverloads constructor( @@ -56,21 +43,16 @@ class VideoPlayerView : FrameLayout { defStyleAttr: Int = 0 ) : super(context, attrs, defStyleAttr) { val view = LayoutInflater.from(context).inflate(R.layout.video_player_view, this) + val composeView = view.findViewById(R.id.video_player_compose_view) - videoPlayerFragment = VideoPlayerFragment() - when (context) { - is AppCompatActivity -> { - context.supportFragmentManager.beginTransaction() - .add(R.id.video_player_fragment_container, videoPlayerFragment).commit() - } - - else -> { - throw Exception("The context that should host the NewPlayer Embedded VideoPlayerView is not an AppCompatActivity: $context") + composeView.apply { + setViewCompositionStrategy(ViewCompositionStrategy.DisposeOnViewTreeLifecycleDestroyed) + setContent { + VideoPlayerTheme { + VideoPlayerUI(viewModel = viewModel) + } } } - } - interface FullScreenToggleListener { - fun fullscreenToggle(turnOn: Boolean) } } \ No newline at end of file diff --git a/new-player/src/main/java/net/newpipe/newplayer/internal/VideoPlayerFragment.kt b/new-player/src/main/java/net/newpipe/newplayer/internal/VideoPlayerFragment.kt deleted file mode 100644 index c338da1..0000000 --- a/new-player/src/main/java/net/newpipe/newplayer/internal/VideoPlayerFragment.kt +++ /dev/null @@ -1,125 +0,0 @@ -/* 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.internal - -import android.os.Bundle -import android.util.Log -import android.view.LayoutInflater -import android.view.View -import android.view.ViewGroup -import androidx.compose.ui.platform.ComposeView -import androidx.compose.ui.platform.ViewCompositionStrategy -import androidx.constraintlayout.widget.ConstraintLayout -import androidx.core.view.WindowCompat -import androidx.core.view.WindowInsetsControllerCompat -import androidx.core.view.updateLayoutParams -import androidx.fragment.app.Fragment -import androidx.fragment.app.viewModels -import dagger.hilt.android.AndroidEntryPoint -import net.newpipe.newplayer.NewPlayer -import net.newpipe.newplayer.R -import net.newpipe.newplayer.VideoPlayerView -import net.newpipe.newplayer.internal.model.VideoPlayerViewModel -import net.newpipe.newplayer.internal.model.VideoPlayerViewModelImpl -import net.newpipe.newplayer.internal.ui.VideoPlayerUI -import net.newpipe.newplayer.internal.ui.theme.VideoPlayerTheme - -private const val TAG = "VideoPlayerFragment" - -@AndroidEntryPoint -class VideoPlayerFragment() : Fragment() { - - private val viewModel: VideoPlayerViewModel by viewModels() - private var currentVideoRatio = 0F - private lateinit var composeView: ComposeView - - var fullScreenToggleListener: VideoPlayerView.FullScreenToggleListener? = null - - var minLayoutRatio = 4F / 3F - set(value) { - if (value <= 0 && maxLayoutRatio < minLayoutRatio) - Log.e( - TAG, - "minLayoutRatio can not be 0 or smaller or bigger then maxLayoutRatio. Ignore: $value" - ) - else { - field = value - updateViewRatio() - } - } - - var maxLayoutRatio = 16F / 9F - set(value) { - if (value <= 0 && value < minLayoutRatio) - Log.e( - TAG, - "maxLayoutRatio can not be 0 smaller ans smaller then minLayoutRatio. Ignore: $value" - ) - else { - field = value - updateViewRatio() - } - } - - override fun onCreateView( - inflater: LayoutInflater, - container: ViewGroup?, - savedInstanceState: Bundle? - ): View? { - val window = activity?.window!! - val insetsController = WindowCompat.getInsetsController(window, window.decorView) - insetsController.systemBarsBehavior = - WindowInsetsControllerCompat.BEHAVIOR_SHOW_TRANSIENT_BARS_BY_SWIPE - - val view = inflater.inflate(R.layout.video_player_framgent, container, false) - composeView = view.findViewById(R.id.player_copose_view) - - viewModel.listener = object : VideoPlayerViewModel.Listener { - override fun requestUpdateLayoutRatio(videoRatio: Float) { - currentVideoRatio = videoRatio - updateViewRatio() - } - } - - composeView.apply { - setViewCompositionStrategy(ViewCompositionStrategy.DisposeOnViewTreeLifecycleDestroyed) - setContent { - VideoPlayerTheme { - VideoPlayerUI(viewModel = viewModel, - {fullscreenOn -> - fullScreenToggleListener?.fullscreenToggle(fullscreenOn) - }) - } - } - } - - return view - } - - private fun updateViewRatio() { - if(this::composeView.isInitialized) { - composeView.updateLayoutParams { - val ratio = currentVideoRatio.coerceIn(minLayoutRatio, maxLayoutRatio) - dimensionRatio = "$ratio:1" - } - } - } -} \ No newline at end of file diff --git a/new-player/src/main/java/net/newpipe/newplayer/internal/ui/VideoPlayerUI.kt b/new-player/src/main/java/net/newpipe/newplayer/internal/ui/VideoPlayerUI.kt deleted file mode 100644 index fc79f1b..0000000 --- a/new-player/src/main/java/net/newpipe/newplayer/internal/ui/VideoPlayerUI.kt +++ /dev/null @@ -1,152 +0,0 @@ -/* 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.internal.ui - -import android.app.Activity -import android.content.Intent -import android.content.pm.ActivityInfo -import android.view.SurfaceView -import androidx.activity.compose.BackHandler -import androidx.activity.compose.ManagedActivityResultLauncher -import androidx.activity.compose.rememberLauncherForActivityResult -import androidx.activity.result.ActivityResult -import androidx.activity.result.contract.ActivityResultContracts -import androidx.compose.foundation.layout.fillMaxSize -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.Modifier -import androidx.compose.ui.graphics.Color -import androidx.compose.ui.platform.LocalContext -import androidx.compose.ui.platform.LocalLifecycleOwner -import androidx.compose.ui.tooling.preview.Preview -import androidx.compose.ui.viewinterop.AndroidView -import androidx.lifecycle.Lifecycle -import androidx.lifecycle.LifecycleEventObserver -import net.newpipe.newplayer.internal.model.VideoPlayerViewModel -import net.newpipe.newplayer.internal.model.VideoPlayerViewModelImpl -import net.newpipe.newplayer.internal.ui.theme.VideoPlayerTheme -import net.newpipe.newplayer.internal.utils.LockScreenOrientation -import net.newpipe.newplayer.internal.utils.findActivity - -@Composable -fun VideoPlayerUI( - viewModel: VideoPlayerViewModel, - fullscreenToggled: (Boolean) -> Unit -) { - val uiState by viewModel.uiState.collectAsState() - - var lifecycle by remember { - mutableStateOf(Lifecycle.Event.ON_CREATE) - } - - val activity = LocalContext.current.findActivity() - - 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) - } - } - - - // Handle Fullscreen/Embedded view transition - if(uiState.fullscreen) { - BackHandler { - //closeFullscreen(viewModel, activity!!) - } - } - - val fullscreenLauncher = - rememberLauncherForActivityResult( - contract = ActivityResultContracts.StartActivityForResult() - ) { result -> - println("gurken returned for result") - viewModel.initUIState(result.data?.extras!!) - } - - LaunchedEffect(key1 = uiState.fullscreen) { - println("gurken launch fullscreen: ${uiState.fullscreen}") - } - - // Set Screen Rotation - if(uiState.fullscreen) { - if(uiState.contentRatio < 1) { - LockScreenOrientation(orientation = ActivityInfo.SCREEN_ORIENTATION_PORTRAIT) - } else { - LockScreenOrientation(orientation = ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE) - } - } - - // Set UI - Surface( - modifier = Modifier.fillMaxSize(), - color = Color.Black - ) { - AndroidView( - modifier = Modifier.fillMaxSize(), - factory = { context -> - SurfaceView(context).also { view -> - viewModel.player?.setVideoSurfaceView(view) - } - }, update = { view -> - when (lifecycle) { - Lifecycle.Event.ON_RESUME -> { - viewModel.player?.setVideoSurfaceView(view) - } - - else -> Unit - } - }) - - VideoPlayerControllerUI( - isPlaying = uiState.playing, - fullscreen = uiState.fullscreen, - play = viewModel::play, - pause = viewModel::pause, - prevStream = viewModel::prevStream, - nextStream = viewModel::nextStream, - switchToFullscreen = viewModel::switchToFullscreen, - switchToEmbeddedView = viewModel::switchToEmbeddedView - ) - } -} - -@Preview(device = "spec:width=1080px,height=700px,dpi=440,orientation=landscape") -@Composable -fun PlayerUIPreviewEmbeded() { - VideoPlayerTheme { - VideoPlayerUI(viewModel = VideoPlayerViewModelImpl.dummy, {}) - } -} \ No newline at end of file diff --git a/new-player/src/main/java/net/newpipe/newplayer/internal/model/VideoPlayerViewModel.kt b/new-player/src/main/java/net/newpipe/newplayer/model/VideoPlayerViewModel.kt similarity index 94% rename from new-player/src/main/java/net/newpipe/newplayer/internal/model/VideoPlayerViewModel.kt rename to new-player/src/main/java/net/newpipe/newplayer/model/VideoPlayerViewModel.kt index 0077b46..18175e7 100644 --- a/new-player/src/main/java/net/newpipe/newplayer/internal/model/VideoPlayerViewModel.kt +++ b/new-player/src/main/java/net/newpipe/newplayer/model/VideoPlayerViewModel.kt @@ -18,7 +18,7 @@ * along with NewPlayer. If not, see . */ -package net.newpipe.newplayer.internal.model +package net.newpipe.newplayer.model import android.app.Application import android.os.Build @@ -27,18 +27,14 @@ import android.os.Parcelable import androidx.annotation.RequiresApi import androidx.lifecycle.AndroidViewModel import androidx.lifecycle.SavedStateHandle -import androidx.lifecycle.viewModelScope import androidx.media3.common.Player import dagger.hilt.android.lifecycle.HiltViewModel -import kotlinx.coroutines.flow.MutableSharedFlow import kotlinx.coroutines.flow.MutableStateFlow -import kotlinx.coroutines.flow.SharedFlow import javax.inject.Inject import kotlinx.coroutines.flow.StateFlow import kotlinx.coroutines.flow.asStateFlow import kotlinx.coroutines.flow.update -import kotlinx.coroutines.launch -import net.newpipe.newplayer.internal.utils.VideoSize +import net.newpipe.newplayer.utils.VideoSize import kotlinx.parcelize.Parcelize import net.newpipe.newplayer.NewPlayer @@ -87,7 +83,6 @@ interface VideoPlayerViewModel { class VideoPlayerViewModelImpl @Inject constructor( private val savedStateHandle: SavedStateHandle, application: Application, - override var newPlayer: NewPlayer? ) : AndroidViewModel(application), VideoPlayerViewModel { // private @@ -95,17 +90,18 @@ class VideoPlayerViewModelImpl @Inject constructor( private var current_video_size = VideoSize.DEFAULT //interface + override var newPlayer: NewPlayer? = null + set(value) { + field = value + installExoPlayer() + } + override val uiState = mutableUiState.asStateFlow() override var listener: VideoPlayerViewModel.Listener? = null override val player:Player? get() = newPlayer?.player - init { - installExoPlayer() - println("gurken reinit ViewModel") - } - private fun installExoPlayer() { player?.let { player -> player.addListener(object : Player.Listener { @@ -145,6 +141,11 @@ class VideoPlayerViewModelImpl @Inject constructor( } } + override fun onCleared() { + super.onCleared() + println("gurken viewmodel cleared") + } + @RequiresApi(Build.VERSION_CODES.TIRAMISU) override fun initUIState(instanceState: Bundle) { diff --git a/new-player/src/main/java/net/newpipe/newplayer/internal/ui/VideoPlayerControllerUI.kt b/new-player/src/main/java/net/newpipe/newplayer/ui/VideoPlayerControllerUI.kt similarity index 98% rename from new-player/src/main/java/net/newpipe/newplayer/internal/ui/VideoPlayerControllerUI.kt rename to new-player/src/main/java/net/newpipe/newplayer/ui/VideoPlayerControllerUI.kt index c42a159..55c4cb4 100644 --- a/new-player/src/main/java/net/newpipe/newplayer/internal/ui/VideoPlayerControllerUI.kt +++ b/new-player/src/main/java/net/newpipe/newplayer/ui/VideoPlayerControllerUI.kt @@ -18,7 +18,7 @@ * along with NewPlayer. If not, see . */ - package net.newpipe.newplayer.internal.ui + package net.newpipe.newplayer.ui import androidx.activity.compose.BackHandler import androidx.compose.foundation.background @@ -78,8 +78,8 @@ import androidx.compose.ui.unit.DpOffset import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.sp import net.newpipe.newplayer.R -import net.newpipe.newplayer.internal.ui.theme.VideoPlayerTheme -import net.newpipe.newplayer.internal.ui.theme.video_player_onSurface +import net.newpipe.newplayer.ui.theme.VideoPlayerTheme +import net.newpipe.newplayer.ui.theme.video_player_onSurface @Composable fun VideoPlayerControllerUI( @@ -392,7 +392,7 @@ fun PreviewBackgroundSurface( } } -@Preview(device = "spec:width=1080px,height=700px,dpi=440,orientation=landscape") +@Preview(device = "spec:width=1080px,height=600px,dpi=440,orientation=landscape") @Composable fun VideoPlayerControllerUIPreviewEmbeded() { VideoPlayerTheme { diff --git a/new-player/src/main/java/net/newpipe/newplayer/ui/VideoPlayerLoadingPlaceholder.kt b/new-player/src/main/java/net/newpipe/newplayer/ui/VideoPlayerLoadingPlaceholder.kt new file mode 100644 index 0000000..f155f12 --- /dev/null +++ b/new-player/src/main/java/net/newpipe/newplayer/ui/VideoPlayerLoadingPlaceholder.kt @@ -0,0 +1,36 @@ +package net.newpipe.newplayer.ui + +import androidx.compose.foundation.layout.Box +import androidx.compose.foundation.layout.fillMaxSize +import androidx.compose.material3.CircularProgressIndicator +import androidx.compose.material3.Surface +import androidx.compose.runtime.Composable +import androidx.compose.ui.graphics.Color +import androidx.compose.foundation.layout.fillMaxWidth +import androidx.compose.foundation.layout.height +import androidx.compose.foundation.layout.width +import androidx.compose.ui.Alignment +import androidx.compose.ui.Modifier +import androidx.compose.ui.tooling.preview.Preview +import androidx.compose.ui.unit.dp +import net.newpipe.newplayer.ui.theme.VideoPlayerTheme + +@Composable +fun VideoPlayerLoadingPlaceholder() { + Surface( + modifier = Modifier.fillMaxWidth().height(200.dp), + color = Color.Black + ) { + Box { + CircularProgressIndicator(modifier = Modifier.width(64.dp).align((Alignment.Center))) + } + } +} + +@Preview(device = "spec:width=1080px,height=600px,dpi=440,orientation=landscape") +@Composable +fun VideoPlayerLoaidingPlaceholderPreview() { + VideoPlayerTheme { + VideoPlayerLoadingPlaceholder() + } +} \ 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 new file mode 100644 index 0000000..595be65 --- /dev/null +++ b/new-player/src/main/java/net/newpipe/newplayer/ui/VideoPlayerUI.kt @@ -0,0 +1,149 @@ +/* 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 + +import android.content.pm.ActivityInfo +import android.view.SurfaceView +import androidx.activity.compose.BackHandler +import androidx.activity.compose.rememberLauncherForActivityResult +import androidx.activity.result.contract.ActivityResultContracts +import androidx.compose.foundation.layout.fillMaxSize +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.Modifier +import androidx.compose.ui.graphics.Color +import androidx.compose.ui.platform.LocalContext +import androidx.compose.ui.platform.LocalLifecycleOwner +import androidx.compose.ui.tooling.preview.Preview +import androidx.compose.ui.viewinterop.AndroidView +import androidx.lifecycle.Lifecycle +import androidx.lifecycle.LifecycleEventObserver +import net.newpipe.newplayer.model.VideoPlayerViewModel +import net.newpipe.newplayer.model.VideoPlayerViewModelImpl +import net.newpipe.newplayer.ui.theme.VideoPlayerTheme +import net.newpipe.newplayer.utils.LockScreenOrientation +import net.newpipe.newplayer.utils.findActivity + +@Composable +fun VideoPlayerUI( + viewModel: VideoPlayerViewModel?, +) { + if (viewModel?.player == null) { + VideoPlayerLoadingPlaceholder() + } else { + val uiState by viewModel.uiState.collectAsState() + + var lifecycle by remember { + mutableStateOf(Lifecycle.Event.ON_CREATE) + } + + 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) + } + } + + + // Handle Fullscreen/Embedded view transition + if (uiState.fullscreen) { + BackHandler { + //closeFullscreen(viewModel, activity!!) + } + } + + val fullscreenLauncher = + rememberLauncherForActivityResult( + contract = ActivityResultContracts.StartActivityForResult() + ) { result -> + println("gurken returned for result") + viewModel.initUIState(result.data?.extras!!) + } + + LaunchedEffect(key1 = uiState.fullscreen) { + println("gurken launch fullscreen: ${uiState.fullscreen}") + } + + // Set Screen Rotation + if (uiState.fullscreen) { + if (uiState.contentRatio < 1) { + LockScreenOrientation(orientation = ActivityInfo.SCREEN_ORIENTATION_PORTRAIT) + } else { + LockScreenOrientation(orientation = ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE) + } + } + + // Set UI + Surface( + modifier = Modifier.fillMaxSize(), + color = Color.Black + ) { + AndroidView( + modifier = Modifier.fillMaxSize(), + factory = { context -> + SurfaceView(context).also { view -> + viewModel.player?.setVideoSurfaceView(view) + } + }, update = { view -> + when (lifecycle) { + Lifecycle.Event.ON_RESUME -> { + viewModel.player?.setVideoSurfaceView(view) + } + + else -> Unit + } + }) + + VideoPlayerControllerUI( + isPlaying = uiState.playing, + fullscreen = uiState.fullscreen, + play = viewModel::play, + pause = viewModel::pause, + prevStream = viewModel::prevStream, + nextStream = viewModel::nextStream, + switchToFullscreen = viewModel::switchToFullscreen, + switchToEmbeddedView = viewModel::switchToEmbeddedView + ) + } + } +} + +@Preview(device = "spec:width=1080px,height=700px,dpi=440,orientation=landscape") +@Composable +fun PlayerUIPreviewEmbeded() { + VideoPlayerTheme { + VideoPlayerUI(viewModel = VideoPlayerViewModelImpl.dummy) + } +} \ No newline at end of file diff --git a/new-player/src/main/java/net/newpipe/newplayer/internal/ui/theme/Color.kt b/new-player/src/main/java/net/newpipe/newplayer/ui/theme/Color.kt similarity index 97% rename from new-player/src/main/java/net/newpipe/newplayer/internal/ui/theme/Color.kt rename to new-player/src/main/java/net/newpipe/newplayer/ui/theme/Color.kt index c906b6b..b2c22dd 100644 --- a/new-player/src/main/java/net/newpipe/newplayer/internal/ui/theme/Color.kt +++ b/new-player/src/main/java/net/newpipe/newplayer/ui/theme/Color.kt @@ -18,7 +18,7 @@ * along with NewPlayer. If not, see . */ -package net.newpipe.newplayer.internal.ui.theme +package net.newpipe.newplayer.ui.theme import androidx.compose.ui.graphics.Color diff --git a/new-player/src/main/java/net/newpipe/newplayer/internal/ui/theme/Theme.kt b/new-player/src/main/java/net/newpipe/newplayer/ui/theme/Theme.kt similarity index 98% rename from new-player/src/main/java/net/newpipe/newplayer/internal/ui/theme/Theme.kt rename to new-player/src/main/java/net/newpipe/newplayer/ui/theme/Theme.kt index 6991169..e6ad9c9 100644 --- a/new-player/src/main/java/net/newpipe/newplayer/internal/ui/theme/Theme.kt +++ b/new-player/src/main/java/net/newpipe/newplayer/ui/theme/Theme.kt @@ -18,7 +18,7 @@ * along with NewPlayer. If not, see . */ -package net.newpipe.newplayer.internal.ui.theme +package net.newpipe.newplayer.ui.theme import androidx.compose.material3.MaterialTheme import androidx.compose.material3.darkColorScheme diff --git a/new-player/src/main/java/net/newpipe/newplayer/internal/ui/theme/Type.kt b/new-player/src/main/java/net/newpipe/newplayer/ui/theme/Type.kt similarity index 97% rename from new-player/src/main/java/net/newpipe/newplayer/internal/ui/theme/Type.kt rename to new-player/src/main/java/net/newpipe/newplayer/ui/theme/Type.kt index 369d8e7..9a42dda 100644 --- a/new-player/src/main/java/net/newpipe/newplayer/internal/ui/theme/Type.kt +++ b/new-player/src/main/java/net/newpipe/newplayer/ui/theme/Type.kt @@ -18,7 +18,7 @@ * along with NewPlayer. If not, see . */ -package net.newpipe.newplayer.internal.ui.theme +package net.newpipe.newplayer.ui.theme import androidx.compose.material3.Typography import androidx.compose.ui.text.TextStyle diff --git a/new-player/src/main/java/net/newpipe/newplayer/internal/utils/VideoSize.kt b/new-player/src/main/java/net/newpipe/newplayer/utils/VideoSize.kt similarity index 94% rename from new-player/src/main/java/net/newpipe/newplayer/internal/utils/VideoSize.kt rename to new-player/src/main/java/net/newpipe/newplayer/utils/VideoSize.kt index ecc1846..8f4c3f8 100644 --- a/new-player/src/main/java/net/newpipe/newplayer/internal/utils/VideoSize.kt +++ b/new-player/src/main/java/net/newpipe/newplayer/utils/VideoSize.kt @@ -1,4 +1,4 @@ -package net.newpipe.newplayer.internal.utils +package net.newpipe.newplayer.utils data class VideoSize( val width: Int, diff --git a/new-player/src/main/java/net/newpipe/newplayer/internal/utils/utils.kt b/new-player/src/main/java/net/newpipe/newplayer/utils/utils.kt similarity index 83% rename from new-player/src/main/java/net/newpipe/newplayer/internal/utils/utils.kt rename to new-player/src/main/java/net/newpipe/newplayer/utils/utils.kt index 920971c..b023625 100644 --- a/new-player/src/main/java/net/newpipe/newplayer/internal/utils/utils.kt +++ b/new-player/src/main/java/net/newpipe/newplayer/utils/utils.kt @@ -18,19 +18,14 @@ * along with NewPlayer. If not, see . */ -package net.newpipe.newplayer.internal.utils +package net.newpipe.newplayer.utils import android.app.Activity import android.content.Context import android.content.ContextWrapper -import android.content.Intent -import androidx.activity.compose.ManagedActivityResultLauncher -import androidx.activity.result.ActivityResult import androidx.compose.runtime.Composable import androidx.compose.runtime.DisposableEffect import androidx.compose.ui.platform.LocalContext -import net.newpipe.newplayer.internal.model.VIDEOPLAYER_UI_STATE -import net.newpipe.newplayer.internal.model.VideoPlayerViewModel @Composable fun LockScreenOrientation(orientation: Int) { diff --git a/new-player/src/main/res/layout/video_player_view.xml b/new-player/src/main/res/layout/video_player_view.xml index 5ef0d2e..02c4b84 100644 --- a/new-player/src/main/res/layout/video_player_view.xml +++ b/new-player/src/main/res/layout/video_player_view.xml @@ -17,10 +17,8 @@ You should have received a copy of the GNU General Public License along with NewPlayer. If not, see . --> - - \ No newline at end of file + android:layout_height="wrap_content" /> \ No newline at end of file diff --git a/test-app/src/main/java/net/newpipe/newplayer/testapp/MainActivity.kt b/test-app/src/main/java/net/newpipe/newplayer/testapp/MainActivity.kt index 2f6b2c9..9ca0fb3 100644 --- a/test-app/src/main/java/net/newpipe/newplayer/testapp/MainActivity.kt +++ b/test-app/src/main/java/net/newpipe/newplayer/testapp/MainActivity.kt @@ -20,20 +20,25 @@ package net.newpipe.newplayer.testapp -import android.content.res.Configuration import android.os.Bundle import androidx.activity.enableEdgeToEdge +import androidx.activity.viewModels import androidx.appcompat.app.AppCompatActivity import androidx.core.view.ViewCompat import androidx.core.view.WindowInsetsCompat +import androidx.lifecycle.viewmodel.compose.viewModel import dagger.hilt.android.AndroidEntryPoint import net.newpipe.newplayer.NewPlayer import net.newpipe.newplayer.VideoPlayerView +import net.newpipe.newplayer.model.VideoPlayerViewModel +import net.newpipe.newplayer.model.VideoPlayerViewModelImpl import javax.inject.Inject @AndroidEntryPoint class MainActivity : AppCompatActivity() { + val videoPlayerViewModel: VideoPlayerViewModel by viewModels() + @Inject lateinit var newPlayer: NewPlayer @@ -43,7 +48,10 @@ class MainActivity : AppCompatActivity() { setContentView(R.layout.activity_main) val video_view = findViewById(R.id.new_player_video_view) + video_view.viewModel = videoPlayerViewModel + videoPlayerViewModel.newPlayer = newPlayer + /* video_view.fullScreenToggleListener = object : VideoPlayerView.FullScreenToggleListener { override fun fullscreenToggle(turnOn: Boolean) { if (turnOn) { @@ -70,7 +78,10 @@ class MainActivity : AppCompatActivity() { } } + + } + */ newPlayer.playWhenReady = true newPlayer.setStream(getString(R.string.ccc_chromebooks_video)) diff --git a/test-app/src/main/res/layout/activity_main.xml b/test-app/src/main/res/layout/activity_main.xml index dcf0e7f..6c19d3a 100644 --- a/test-app/src/main/res/layout/activity_main.xml +++ b/test-app/src/main/res/layout/activity_main.xml @@ -31,7 +31,7 @@ android:id="@+id/new_player_video_view" android:layout_width="match_parent" android:layout_height="wrap_content" - android:minHeight="50dp" + android:minHeight="200dp" app:layout_constraintEnd_toEndOf="parent" app:layout_constraintStart_toStartOf="parent" app:layout_constraintTop_toTopOf="parent"