remove FullScreen Activity

This commit is contained in:
Christian Schabesberger 2024-07-19 16:44:04 +02:00
parent be54682b93
commit b111b77e04
8 changed files with 118 additions and 92 deletions

View File

@ -45,6 +45,9 @@ class VideoPlayerView : FrameLayout {
get() = videoPlayerFragment.minLayoutRatio get() = videoPlayerFragment.minLayoutRatio
set(value) {videoPlayerFragment.minLayoutRatio = value} set(value) {videoPlayerFragment.minLayoutRatio = value}
var fullScreenToggleListener: FullScreenToggleListener?
set(value) {videoPlayerFragment.fullScreenToggleListener = value}
get() = videoPlayerFragment.fullScreenToggleListener
@JvmOverloads @JvmOverloads
constructor( constructor(
@ -66,4 +69,8 @@ class VideoPlayerView : FrameLayout {
} }
} }
} }
interface FullScreenToggleListener {
fun fullscreenToggle(turnOn: Boolean)
}
} }

View File

@ -1,27 +0,0 @@
package net.newpipe.newplayer.internal
import android.os.Bundle
import androidx.activity.ComponentActivity
import androidx.activity.compose.setContent
import androidx.activity.enableEdgeToEdge
import androidx.activity.viewModels
import dagger.hilt.android.AndroidEntryPoint
@AndroidEntryPoint
class VideoPlayerActivity : ComponentActivity() {
private val viewModel: net.newpipe.newplayer.internal.model.VideoPlayerViewModel by viewModels<net.newpipe.newplayer.internal.model.VideoPlayerViewModelImpl>()
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
viewModel.initUIState(intent.extras!!)
enableEdgeToEdge()
setContent {
net.newpipe.newplayer.internal.ui.theme.VideoPlayerTheme {
net.newpipe.newplayer.internal.ui.VideoPlayerUI(viewModel = viewModel)
}
}
}
}

View File

@ -36,6 +36,7 @@ import androidx.fragment.app.viewModels
import dagger.hilt.android.AndroidEntryPoint import dagger.hilt.android.AndroidEntryPoint
import net.newpipe.newplayer.NewPlayer import net.newpipe.newplayer.NewPlayer
import net.newpipe.newplayer.R import net.newpipe.newplayer.R
import net.newpipe.newplayer.VideoPlayerView
import net.newpipe.newplayer.internal.model.VideoPlayerViewModel import net.newpipe.newplayer.internal.model.VideoPlayerViewModel
import net.newpipe.newplayer.internal.model.VideoPlayerViewModelImpl import net.newpipe.newplayer.internal.model.VideoPlayerViewModelImpl
import net.newpipe.newplayer.internal.ui.VideoPlayerUI import net.newpipe.newplayer.internal.ui.VideoPlayerUI
@ -50,6 +51,8 @@ class VideoPlayerFragment() : Fragment() {
private var currentVideoRatio = 0F private var currentVideoRatio = 0F
private lateinit var composeView: ComposeView private lateinit var composeView: ComposeView
var fullScreenToggleListener: VideoPlayerView.FullScreenToggleListener? = null
var minLayoutRatio = 4F / 3F var minLayoutRatio = 4F / 3F
set(value) { set(value) {
if (value <= 0 && maxLayoutRatio < minLayoutRatio) if (value <= 0 && maxLayoutRatio < minLayoutRatio)
@ -100,7 +103,10 @@ class VideoPlayerFragment() : Fragment() {
setViewCompositionStrategy(ViewCompositionStrategy.DisposeOnViewTreeLifecycleDestroyed) setViewCompositionStrategy(ViewCompositionStrategy.DisposeOnViewTreeLifecycleDestroyed)
setContent { setContent {
VideoPlayerTheme { VideoPlayerTheme {
VideoPlayerUI(viewModel = viewModel) VideoPlayerUI(viewModel = viewModel,
{fullscreenOn ->
fullScreenToggleListener?.fullscreenToggle(fullscreenOn)
})
} }
} }
} }

View File

@ -63,7 +63,6 @@ interface VideoPlayerViewModel {
val player: Player? val player: Player?
val uiState: StateFlow<VideoPlayerUIState> val uiState: StateFlow<VideoPlayerUIState>
var listener: Listener? var listener: Listener?
val events: SharedFlow<Events>?
fun initUIState(instanceState: Bundle) fun initUIState(instanceState: Bundle)
fun play() fun play()
@ -93,12 +92,10 @@ class VideoPlayerViewModelImpl @Inject constructor(
// private // private
private val mutableUiState = MutableStateFlow(VideoPlayerUIState.DEFAULT) private val mutableUiState = MutableStateFlow(VideoPlayerUIState.DEFAULT)
private val mutableEvent = MutableSharedFlow<VideoPlayerViewModel.Events>()
private var current_video_size = VideoSize.DEFAULT private var current_video_size = VideoSize.DEFAULT
//interface //interface
override val uiState = mutableUiState.asStateFlow() override val uiState = mutableUiState.asStateFlow()
override val events: SharedFlow<VideoPlayerViewModel.Events> = mutableEvent
override var listener: VideoPlayerViewModel.Listener? = null override var listener: VideoPlayerViewModel.Listener? = null
override val player:Player? override val player:Player?
@ -106,6 +103,7 @@ class VideoPlayerViewModelImpl @Inject constructor(
init { init {
installExoPlayer() installExoPlayer()
println("gurken reinit ViewModel")
} }
private fun installExoPlayer() { private fun installExoPlayer() {
@ -180,15 +178,14 @@ class VideoPlayerViewModelImpl @Inject constructor(
} }
override fun switchToEmbeddedView() { override fun switchToEmbeddedView() {
viewModelScope.launch { mutableUiState.update {
mutableEvent.emit(VideoPlayerViewModel.Events.SwitchToEmbeddedView) it.copy(fullscreen = false)
} }
} }
override fun switchToFullscreen() { override fun switchToFullscreen() {
mutableUiState.update {
viewModelScope.launch { it.copy(fullscreen = true)
mutableEvent.emit(VideoPlayerViewModel.Events.SwitchToFullscreen)
} }
} }
@ -198,7 +195,7 @@ class VideoPlayerViewModelImpl @Inject constructor(
override val player: Player? = null override val player: Player? = null
override val uiState = MutableStateFlow(VideoPlayerUIState.DEFAULT) override val uiState = MutableStateFlow(VideoPlayerUIState.DEFAULT)
override var listener: VideoPlayerViewModel.Listener? = null override var listener: VideoPlayerViewModel.Listener? = null
override val events: SharedFlow<VideoPlayerViewModel.Events>? = null
override fun initUIState(instanceState: Bundle) { override fun initUIState(instanceState: Bundle) {
println("dummy impl") println("dummy impl")

View File

@ -22,6 +22,7 @@ package net.newpipe.newplayer.internal.ui
import android.app.Activity import android.app.Activity
import android.content.Intent import android.content.Intent
import android.content.pm.ActivityInfo
import android.view.SurfaceView import android.view.SurfaceView
import androidx.activity.compose.BackHandler import androidx.activity.compose.BackHandler
import androidx.activity.compose.ManagedActivityResultLauncher import androidx.activity.compose.ManagedActivityResultLauncher
@ -46,17 +47,16 @@ import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.viewinterop.AndroidView import androidx.compose.ui.viewinterop.AndroidView
import androidx.lifecycle.Lifecycle import androidx.lifecycle.Lifecycle
import androidx.lifecycle.LifecycleEventObserver import androidx.lifecycle.LifecycleEventObserver
import kotlinx.coroutines.flow.collectLatest
import net.newpipe.newplayer.internal.VideoPlayerActivity
import net.newpipe.newplayer.internal.model.VIDEOPLAYER_UI_STATE
import net.newpipe.newplayer.internal.model.VideoPlayerViewModel import net.newpipe.newplayer.internal.model.VideoPlayerViewModel
import net.newpipe.newplayer.internal.model.VideoPlayerViewModelImpl import net.newpipe.newplayer.internal.model.VideoPlayerViewModelImpl
import net.newpipe.newplayer.internal.ui.theme.VideoPlayerTheme import net.newpipe.newplayer.internal.ui.theme.VideoPlayerTheme
import net.newpipe.newplayer.internal.utils.LockScreenOrientation
import net.newpipe.newplayer.internal.utils.findActivity import net.newpipe.newplayer.internal.utils.findActivity
@Composable @Composable
fun VideoPlayerUI( fun VideoPlayerUI(
viewModel: VideoPlayerViewModel, viewModel: VideoPlayerViewModel,
fullscreenToggled: (Boolean) -> Unit
) { ) {
val uiState by viewModel.uiState.collectAsState() val uiState by viewModel.uiState.collectAsState()
@ -67,6 +67,8 @@ fun VideoPlayerUI(
val activity = LocalContext.current.findActivity() val activity = LocalContext.current.findActivity()
val lifecycleOwner = LocalLifecycleOwner.current val lifecycleOwner = LocalLifecycleOwner.current
// Prepare stuff for the SurfaceView to which the video will be rendered
DisposableEffect(lifecycleOwner) { DisposableEffect(lifecycleOwner) {
val observer = LifecycleEventObserver { _, event -> val observer = LifecycleEventObserver { _, event ->
lifecycle = event lifecycle = event
@ -78,8 +80,12 @@ fun VideoPlayerUI(
} }
} }
// Handle Fullscreen/Embedded view transition
if(uiState.fullscreen) {
BackHandler { BackHandler {
closeFullscreen(viewModel, activity!!) //closeFullscreen(viewModel, activity!!)
}
} }
val fullscreenLauncher = val fullscreenLauncher =
@ -90,20 +96,20 @@ fun VideoPlayerUI(
viewModel.initUIState(result.data?.extras!!) viewModel.initUIState(result.data?.extras!!)
} }
LaunchedEffect(key1 = Unit) { LaunchedEffect(key1 = uiState.fullscreen) {
viewModel.events?.collectLatest { event -> println("gurken launch fullscreen: ${uiState.fullscreen}")
when (event) {
VideoPlayerViewModel.Events.SwitchToEmbeddedView -> {
closeFullscreen(viewModel, activity!!)
} }
VideoPlayerViewModel.Events.SwitchToFullscreen -> { // Set Screen Rotation
openFullscreen(viewModel, activity!!, fullscreenLauncher) if(uiState.fullscreen) {
} if(uiState.contentRatio < 1) {
} LockScreenOrientation(orientation = ActivityInfo.SCREEN_ORIENTATION_PORTRAIT)
} else {
LockScreenOrientation(orientation = ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE)
} }
} }
// Set UI
Surface( Surface(
modifier = Modifier.fillMaxSize(), modifier = Modifier.fillMaxSize(),
color = Color.Black color = Color.Black
@ -124,8 +130,6 @@ fun VideoPlayerUI(
} }
}) })
val isPlaying = viewModel.player?.isPlaying ?: false
VideoPlayerControllerUI( VideoPlayerControllerUI(
isPlaying = uiState.playing, isPlaying = uiState.playing,
fullscreen = uiState.fullscreen, fullscreen = uiState.fullscreen,
@ -139,32 +143,10 @@ fun VideoPlayerUI(
} }
} }
fun closeFullscreen(viewModel: VideoPlayerViewModel, activity: Activity) {
val return_fullscreen_intent = Intent()
var uiState = viewModel.uiState.value
uiState.fullscreen = false
return_fullscreen_intent.putExtra(VIDEOPLAYER_UI_STATE, uiState)
activity.setResult(0, return_fullscreen_intent)
activity.finish()
}
fun openFullscreen(
viewModel: VideoPlayerViewModel,
activity: Activity,
fullscreenLauncher: ManagedActivityResultLauncher<Intent, ActivityResult>
) {
val fullscreen_activity_intent =
Intent(activity!!.findActivity(), VideoPlayerActivity::class.java)
var uiState = viewModel.uiState.value
uiState.fullscreen = true
fullscreen_activity_intent.putExtra(VIDEOPLAYER_UI_STATE, uiState)
fullscreenLauncher.launch(fullscreen_activity_intent)
}
@Preview(device = "spec:width=1080px,height=700px,dpi=440,orientation=landscape") @Preview(device = "spec:width=1080px,height=700px,dpi=440,orientation=landscape")
@Composable @Composable
fun PlayerUIPreviewEmbeded() { fun PlayerUIPreviewEmbeded() {
VideoPlayerTheme { VideoPlayerTheme {
VideoPlayerUI(viewModel = VideoPlayerViewModelImpl.dummy) VideoPlayerUI(viewModel = VideoPlayerViewModelImpl.dummy, {})
} }
} }

View File

@ -23,9 +23,14 @@ package net.newpipe.newplayer.internal.utils
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.content.Intent
import androidx.activity.compose.ManagedActivityResultLauncher
import androidx.activity.result.ActivityResult
import androidx.compose.runtime.Composable import androidx.compose.runtime.Composable
import androidx.compose.runtime.DisposableEffect import androidx.compose.runtime.DisposableEffect
import androidx.compose.ui.platform.LocalContext import androidx.compose.ui.platform.LocalContext
import net.newpipe.newplayer.internal.model.VIDEOPLAYER_UI_STATE
import net.newpipe.newplayer.internal.model.VideoPlayerViewModel
@Composable @Composable
fun LockScreenOrientation(orientation: Int) { fun LockScreenOrientation(orientation: Int) {

View File

@ -43,17 +43,48 @@ class MainActivity : AppCompatActivity() {
setContentView(R.layout.activity_main) setContentView(R.layout.activity_main)
val video_view = findViewById<VideoPlayerView>(R.id.new_player_video_view) val video_view = findViewById<VideoPlayerView>(R.id.new_player_video_view)
video_view.fullScreenToggleListener = object : VideoPlayerView.FullScreenToggleListener {
override fun fullscreenToggle(turnOn: Boolean) {
if (turnOn) {
println("gurken blub")
ViewCompat.setOnApplyWindowInsetsListener(findViewById(R.id.main)) { v, insets ->
v.setPadding(
0, 0, 0, 0
)
insets
}
} else {
println("gurken blab")
ViewCompat.setOnApplyWindowInsetsListener(findViewById(R.id.main)) { v, insets ->
val systemBars = insets.getInsets(WindowInsetsCompat.Type.systemBars())
v.setPadding(
systemBars.left,
systemBars.top,
systemBars.right,
systemBars.bottom
)
insets
}
}
}
}
newPlayer.playWhenReady = true newPlayer.playWhenReady = true
newPlayer.setStream(getString(R.string.ccc_chromebooks_video)) newPlayer.setStream(getString(R.string.ccc_chromebooks_video))
//TODO: This is a dirty hack. Fix this later on
if (getResources().configuration.orientation != Configuration.ORIENTATION_LANDSCAPE) {
ViewCompat.setOnApplyWindowInsetsListener(findViewById(R.id.main)) { v, insets -> ViewCompat.setOnApplyWindowInsetsListener(findViewById(R.id.main)) { v, insets ->
val systemBars = insets.getInsets(WindowInsetsCompat.Type.systemBars()) val systemBars = insets.getInsets(WindowInsetsCompat.Type.systemBars())
v.setPadding(systemBars.left, systemBars.top, systemBars.right, systemBars.bottom) v.setPadding(
systemBars.left,
systemBars.top,
systemBars.right,
systemBars.bottom
)
insets insets
} }
}
} }
} }

View File

@ -28,15 +28,40 @@
tools:context=".MainActivity"> tools:context=".MainActivity">
<androidx.fragment.app.FragmentContainerView <LinearLayout
android:id="@+id/player_frament_view" android:id="@+id/player_column"
android:layout_width="match_parent" android:layout_width="0dp"
android:layout_height="match_parent" android:layout_height="match_parent"
app:layout_constraintEnd_toEndOf="parent" android:orientation="vertical"
app:layout_constraintBaseline_toBottomOf="parent"
app:layout_constraintEnd_toStartOf="@+id/hw_view"
app:layout_constraintHorizontal_weight="1"
app:layout_constraintStart_toStartOf="parent" app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" app:layout_constraintTop_toTopOf="parent">
app:layout_constraintBottom_toTopOf="parent"
android:name="net.newpipe.newplayer.internal.VideoPlayerFragment" <net.newpipe.newplayer.VideoPlayerView
/> android:id="@+id/new_player_video_view"
android:name="net.newpipe.newplayer.VideoPlayerFragment"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:minHeight="50dp"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
</LinearLayout>
<TextView
android:id="@+id/hw_view"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:text="Hello World!"
app:layout_constraintHorizontal_weight="1"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toEndOf="@id/player_column"
app:layout_constraintTop_toTopOf="parent" />
</androidx.constraintlayout.widget.ConstraintLayout> </androidx.constraintlayout.widget.ConstraintLayout>