fix project layout and don't use fullscreen acitvity anymore
This commit is contained in:
parent
b111b77e04
commit
356744814c
|
@ -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<ComposeView>(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)
|
||||
}
|
||||
}
|
|
@ -1,125 +0,0 @@
|
|||
/* 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.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<VideoPlayerViewModelImpl>()
|
||||
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<ConstraintLayout.LayoutParams> {
|
||||
val ratio = currentVideoRatio.coerceIn(minLayoutRatio, maxLayoutRatio)
|
||||
dimensionRatio = "$ratio:1"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,152 +0,0 @@
|
|||
/* 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.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, {})
|
||||
}
|
||||
}
|
|
@ -18,7 +18,7 @@
|
|||
* along with NewPlayer. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
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) {
|
||||
|
|
@ -18,7 +18,7 @@
|
|||
* along with NewPlayer. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
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 {
|
|
@ -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()
|
||||
}
|
||||
}
|
|
@ -0,0 +1,149 @@
|
|||
/* 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
|
||||
|
||||
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)
|
||||
}
|
||||
}
|
|
@ -18,7 +18,7 @@
|
|||
* along with NewPlayer. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package net.newpipe.newplayer.internal.ui.theme
|
||||
package net.newpipe.newplayer.ui.theme
|
||||
|
||||
import androidx.compose.ui.graphics.Color
|
||||
|
|
@ -18,7 +18,7 @@
|
|||
* along with NewPlayer. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package net.newpipe.newplayer.internal.ui.theme
|
||||
package net.newpipe.newplayer.ui.theme
|
||||
|
||||
import androidx.compose.material3.MaterialTheme
|
||||
import androidx.compose.material3.darkColorScheme
|
|
@ -18,7 +18,7 @@
|
|||
* along with NewPlayer. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package net.newpipe.newplayer.internal.ui.theme
|
||||
package net.newpipe.newplayer.ui.theme
|
||||
|
||||
import androidx.compose.material3.Typography
|
||||
import androidx.compose.ui.text.TextStyle
|
|
@ -1,4 +1,4 @@
|
|||
package net.newpipe.newplayer.internal.utils
|
||||
package net.newpipe.newplayer.utils
|
||||
|
||||
data class VideoSize(
|
||||
val width: Int,
|
|
@ -18,19 +18,14 @@
|
|||
* along with NewPlayer. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
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) {
|
|
@ -17,10 +17,8 @@
|
|||
You should have received a copy of the GNU General Public License
|
||||
along with NewPlayer. If not, see <http://www.gnu.org/licenses/>.
|
||||
-->
|
||||
|
||||
<androidx.fragment.app.FragmentContainerView
|
||||
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:id="@+id/video_player_fragment_container"
|
||||
<androidx.compose.ui.platform.ComposeView xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||
android:id="@+id/video_player_compose_view"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:minHeight="50dp" />
|
||||
android:layout_height="wrap_content" />
|
|
@ -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<VideoPlayerViewModelImpl>()
|
||||
|
||||
@Inject
|
||||
lateinit var newPlayer: NewPlayer
|
||||
|
||||
|
@ -43,7 +48,10 @@ class MainActivity : AppCompatActivity() {
|
|||
setContentView(R.layout.activity_main)
|
||||
|
||||
val video_view = findViewById<VideoPlayerView>(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))
|
||||
|
|
|
@ -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"
|
||||
|
|
Loading…
Reference in New Issue