switch to fullscreen and back without dropping frames
This commit is contained in:
parent
0290eaf9ea
commit
19a48c45a6
|
@ -18,8 +18,6 @@
|
|||
* along with NewPlayer. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
import java.util.regex.Pattern.compile
|
||||
|
||||
plugins {
|
||||
alias(libs.plugins.android.application)
|
||||
alias(libs.plugins.jetbrains.kotlin.android)
|
||||
|
|
|
@ -6,6 +6,7 @@
|
|||
|
||||
<application
|
||||
android:name=".NewPlayerApp"
|
||||
android:enableOnBackInvokedCallback="true"
|
||||
android:allowBackup="true"
|
||||
android:dataExtractionRules="@xml/data_extraction_rules"
|
||||
android:fullBackupContent="@xml/backup_rules"
|
||||
|
@ -13,8 +14,7 @@
|
|||
android:label="@string/app_name"
|
||||
android:roundIcon="@mipmap/ic_launcher_round"
|
||||
android:supportsRtl="true"
|
||||
android:theme="@style/Theme.NewPlayer"
|
||||
tools:targetApi="31">
|
||||
android:theme="@style/Theme.NewPlayer">
|
||||
<activity
|
||||
android:name=".VideoPlayerActivity"
|
||||
android:exported="false"
|
||||
|
|
|
@ -19,6 +19,9 @@ class VideoPlayerActivity : ComponentActivity() {
|
|||
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
super.onCreate(savedInstanceState)
|
||||
|
||||
viewModel.initUIState(intent.extras!!)
|
||||
|
||||
enableEdgeToEdge()
|
||||
setContent {
|
||||
VideoPlayerTheme {
|
||||
|
|
|
@ -56,7 +56,7 @@ class VideoPlayerFragment() : Fragment() {
|
|||
insetsController.systemBarsBehavior = WindowInsetsControllerCompat.BEHAVIOR_SHOW_TRANSIENT_BARS_BY_SWIPE
|
||||
|
||||
if (viewModel.uiState.value.fullscreen) {
|
||||
println("gurken fragment created for fullscreen")
|
||||
//println("gurken fragment created for fullscreen")
|
||||
//insetsController.hide(WindowInsetsCompat.Type.systemBars())
|
||||
}
|
||||
|
||||
|
@ -72,7 +72,7 @@ class VideoPlayerFragment() : Fragment() {
|
|||
}
|
||||
|
||||
override fun switchToFullscreen() {
|
||||
println("gurken fullscreen")
|
||||
//println("gurken fullscreen")
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -80,7 +80,7 @@ class VideoPlayerFragment() : Fragment() {
|
|||
setViewCompositionStrategy(ViewCompositionStrategy.DisposeOnViewTreeLifecycleDestroyed)
|
||||
setContent {
|
||||
VideoPlayerTheme {
|
||||
VideoPlayerUI(viewModel = viewModel, isFullscreen = false)
|
||||
VideoPlayerUI(viewModel = viewModel)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -21,7 +21,10 @@
|
|||
package net.newpipe.newplayer.model
|
||||
|
||||
import android.app.Application
|
||||
import android.os.Build
|
||||
import android.os.Bundle
|
||||
import android.os.Parcelable
|
||||
import androidx.annotation.RequiresApi
|
||||
import androidx.lifecycle.AndroidViewModel
|
||||
import androidx.lifecycle.SavedStateHandle
|
||||
import androidx.lifecycle.viewModelScope
|
||||
|
@ -62,6 +65,7 @@ interface VideoPlayerViewModel {
|
|||
var listener: Listener?
|
||||
val events: SharedFlow<Events>?
|
||||
|
||||
fun initUIState(instanceState: Bundle)
|
||||
fun preparePlayer()
|
||||
fun play()
|
||||
fun pause()
|
||||
|
@ -99,6 +103,7 @@ class VideoPlayerViewModelImpl @Inject constructor(
|
|||
|
||||
override val events: SharedFlow<VideoPlayerViewModel.Events> = mutableEvent
|
||||
|
||||
|
||||
override val uiState = mutableUiState.asStateFlow()
|
||||
|
||||
override var listener: VideoPlayerViewModel.Listener? = null
|
||||
|
@ -143,6 +148,22 @@ class VideoPlayerViewModelImpl @Inject constructor(
|
|||
})
|
||||
}
|
||||
|
||||
@RequiresApi(Build.VERSION_CODES.TIRAMISU)
|
||||
override fun initUIState(instanceState: Bundle) {
|
||||
|
||||
val uiState =
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU)
|
||||
instanceState.getParcelable(VIDEOPLAYER_UI_STATE, VideoPlayerUIState::class.java)
|
||||
else
|
||||
instanceState.getParcelable(VIDEOPLAYER_UI_STATE)
|
||||
|
||||
uiState?.let { uiState ->
|
||||
mutableUiState.update {
|
||||
uiState
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override fun preparePlayer() {
|
||||
if (player.playbackState == Player.STATE_IDLE) {
|
||||
player.prepare()
|
||||
|
@ -172,19 +193,12 @@ class VideoPlayerViewModelImpl @Inject constructor(
|
|||
viewModelScope.launch {
|
||||
mutableEvent.emit(VideoPlayerViewModel.Events.SwitchToEmbeddedView)
|
||||
}
|
||||
//mutableUiState.update {
|
||||
// it.copy(fullscreen = false)
|
||||
//}
|
||||
}
|
||||
|
||||
override fun switchToFullscreen() {
|
||||
viewModelScope.launch {
|
||||
mutableEvent.emit(VideoPlayerViewModel.Events.SwitchToFullscreen)
|
||||
}
|
||||
//mutableUiState.update {
|
||||
// it.copy(fullscreen = true)
|
||||
//}
|
||||
//listener?.switchToFullscreen()
|
||||
}
|
||||
|
||||
companion object {
|
||||
|
@ -194,6 +208,10 @@ class VideoPlayerViewModelImpl @Inject constructor(
|
|||
override var listener: VideoPlayerViewModel.Listener? = null
|
||||
override val events: SharedFlow<VideoPlayerViewModel.Events>? = null
|
||||
|
||||
override fun initUIState(instanceState: Bundle) {
|
||||
println("dummy impl")
|
||||
}
|
||||
|
||||
override fun preparePlayer() {
|
||||
println("dummy impl")
|
||||
}
|
||||
|
|
|
@ -20,8 +20,15 @@
|
|||
|
||||
package net.newpipe.newplayer.ui
|
||||
|
||||
import android.app.Activity
|
||||
import android.content.Intent
|
||||
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.IntentSenderRequest
|
||||
import androidx.activity.result.contract.ActivityResultContracts
|
||||
import androidx.compose.foundation.layout.fillMaxSize
|
||||
import androidx.compose.material3.Surface
|
||||
import androidx.compose.runtime.Composable
|
||||
|
@ -40,6 +47,7 @@ import androidx.compose.ui.tooling.preview.Preview
|
|||
import androidx.compose.ui.viewinterop.AndroidView
|
||||
import androidx.lifecycle.Lifecycle
|
||||
import androidx.lifecycle.LifecycleEventObserver
|
||||
import androidx.lifecycle.VIEW_MODEL_STORE_OWNER_KEY
|
||||
import kotlinx.coroutines.flow.collectLatest
|
||||
import net.newpipe.newplayer.VideoPlayerActivity
|
||||
import net.newpipe.newplayer.model.VIDEOPLAYER_UI_STATE
|
||||
|
@ -72,19 +80,27 @@ fun VideoPlayerUI(
|
|||
}
|
||||
}
|
||||
|
||||
BackHandler {
|
||||
closeFullscreen(viewModel, activity!!)
|
||||
}
|
||||
|
||||
val fullscreenLauncher =
|
||||
rememberLauncherForActivityResult(
|
||||
contract = ActivityResultContracts.StartActivityForResult()
|
||||
) { result ->
|
||||
println("gurken returned for result")
|
||||
viewModel.initUIState(result.data?.extras!!)
|
||||
}
|
||||
|
||||
LaunchedEffect(key1 = Unit) {
|
||||
viewModel.events?.collectLatest { event ->
|
||||
when (event) {
|
||||
VideoPlayerViewModel.Events.SwitchToEmbeddedView -> {
|
||||
activity?.finish()
|
||||
closeFullscreen(viewModel, activity!!)
|
||||
}
|
||||
|
||||
VideoPlayerViewModel.Events.SwitchToFullscreen -> {
|
||||
val fullscreen_activity_intent =
|
||||
Intent(activity!!.findActivity(), VideoPlayerActivity::class.java)
|
||||
fullscreen_activity_intent.putExtra(VIDEOPLAYER_UI_STATE, viewModel.uiState.value)
|
||||
activity.startActivity(fullscreen_activity_intent)
|
||||
|
||||
openFullscreen(viewModel, activity!!, fullscreenLauncher)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -97,14 +113,18 @@ fun VideoPlayerUI(
|
|||
AndroidView(
|
||||
modifier = Modifier.fillMaxSize(),
|
||||
factory = { context ->
|
||||
SurfaceView(context).also {
|
||||
viewModel.player?.setVideoSurfaceView(it)
|
||||
SurfaceView(context).also { view ->
|
||||
viewModel.player?.setVideoSurfaceView(view)
|
||||
}
|
||||
}, update = {
|
||||
}, update = { view ->
|
||||
when (lifecycle) {
|
||||
Lifecycle.Event.ON_PAUSE -> {
|
||||
println("gurken state on pause")
|
||||
viewModel.pause()
|
||||
}
|
||||
|
||||
Lifecycle.Event.ON_RESUME -> {
|
||||
println("gurken resume")
|
||||
viewModel.player?.setVideoSurfaceView(view)
|
||||
}
|
||||
|
||||
else -> Unit
|
||||
|
@ -112,7 +132,7 @@ fun VideoPlayerUI(
|
|||
})
|
||||
|
||||
val isPlaying = viewModel.player!!.isPlaying
|
||||
println("is Player playing: $isPlaying")
|
||||
|
||||
VideoPlayerControllerUI(
|
||||
isPlaying = uiState.playing,
|
||||
fullscreen = uiState.fullscreen,
|
||||
|
@ -126,6 +146,28 @@ 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")
|
||||
@Composable
|
||||
fun PlayerUIPreviewEmbeded() {
|
||||
|
|
|
@ -19,7 +19,7 @@
|
|||
|
||||
[versions]
|
||||
agp = "8.5.0"
|
||||
kotlin = "2.0.0"
|
||||
kotlin = "2.0.20-Beta2"
|
||||
coreKtx = "1.13.1"
|
||||
junit = "4.13.2"
|
||||
junitVersion = "1.2.1"
|
||||
|
@ -30,16 +30,16 @@ androidx = "1.9.0"
|
|||
constraintlayout = "2.1.4"
|
||||
material3 = "1.2.1"
|
||||
uiTooling = "1.6.8"
|
||||
materialIconsExtendedAndroid = "1.7.0-beta04"
|
||||
materialIconsExtendedAndroid = "1.7.0-beta05"
|
||||
media3Ui = "1.4.0-beta01"
|
||||
hiltAndroid = "2.51.1"
|
||||
hiltCompiler = "1.2.0"
|
||||
hiltNavigationCompose = "1.2.0"
|
||||
lifecycleViewmodelCompose = "2.8.3"
|
||||
kspVersion = "1.9.0-1.0.13"
|
||||
kspVersion = "2.0.20-Beta2-1.0.23"
|
||||
fragmentKtx = "1.8.1"
|
||||
lifecycleRuntimeKtx = "2.8.3"
|
||||
composeBom = "2024.04.01"
|
||||
composeBom = "2024.06.01"
|
||||
kotlinParcelize = "2.0.20-Beta2"
|
||||
|
||||
[libraries]
|
||||
|
@ -65,7 +65,7 @@ androidx-lifecycle-viewmodel-compose = { group = "androidx.lifecycle", name = "l
|
|||
androidx-foundation = { group = "androidx.compose.foundation", name = "foundation", version.ref = "uiTooling" }
|
||||
androidx-fragment-ktx = { group = "androidx.fragment", name = "fragment-ktx", version.ref = "fragmentKtx" }
|
||||
androidx-lifecycle-runtime-ktx = { group = "androidx.lifecycle", name = "lifecycle-runtime-ktx", version.ref = "lifecycleRuntimeKtx" }
|
||||
androidx-compose-bom = { group = "androidx.compose", name = "compose-bom", version.ref = "composeBom" }
|
||||
androidx-compose-bom = { group = "androidx.compose", name = "compose-bom", version = "2024.06.00" }
|
||||
androidx-ui = { group = "androidx.compose.ui", name = "ui" }
|
||||
androidx-ui-graphics = { group = "androidx.compose.ui", name = "ui-graphics" }
|
||||
androidx-ui-tooling-preview = { group = "androidx.compose.ui", name = "ui-tooling-preview" }
|
||||
|
|
Loading…
Reference in New Issue