synchronize UI mode and playMode

This commit is contained in:
Christian Schabesberger 2024-08-26 12:09:51 +02:00
parent 47ad16c03d
commit f3d3ce380f
5 changed files with 63 additions and 22 deletions

View File

@ -35,9 +35,9 @@ import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.SharedFlow
import kotlinx.coroutines.flow.StateFlow
import kotlinx.coroutines.flow.asSharedFlow
import kotlinx.coroutines.flow.asStateFlow
import kotlinx.coroutines.flow.update
import kotlinx.coroutines.launch
import net.newpipe.newplayer.model.UIModeState
import net.newpipe.newplayer.utils.PlayList
import kotlin.Exception
@ -63,13 +63,14 @@ interface NewPlayer {
var currentPosition: Long
var fastSeekAmountSec: Int
var playBackMode: PlayMode
var playMode : MutableStateFlow<PlayMode?>
var playMode: StateFlow<PlayMode?>
var playlist: PlayList
// callbacks
val errorFlow: SharedFlow<Exception>
val onExoPlayerEvent: SharedFlow<Pair<Player, Player.Events>>
// methods
fun prepare()
@ -78,6 +79,7 @@ interface NewPlayer {
fun addToPlaylist(item: String)
fun playStream(item: String, playMode: PlayMode)
fun playStream(item: String, streamVariant: String, playMode: PlayMode)
fun setPlayMode(playMode: PlayMode)
data class Builder(val app: Application, val repository: MediaRepository) {
private var mediaSourceFactory: MediaSource.Factory? = null
@ -130,7 +132,12 @@ class NewPlayerImpl(
private var playerScope = CoroutineScope(Dispatchers.Main + Job())
override var playMode = MutableStateFlow<PlayMode?>(null)
var mutablePlayMode = MutableStateFlow<PlayMode?>(null)
override var playMode = mutablePlayMode.asStateFlow()
var mutableOnEvent = MutableSharedFlow<Pair<Player, Player.Events>>()
override val onExoPlayerEvent: SharedFlow<Pair<Player, Player.Events>> =
mutableOnEvent.asSharedFlow()
override var playWhenReady: Boolean
set(value) {
@ -157,6 +164,13 @@ class NewPlayerImpl(
}
}
}
override fun onEvents(player: Player, events: Player.Events) {
super.onEvents(player, events)
launchJobAndCollectError {
mutableOnEvent.emit(Pair(player, events))
}
}
})
}
@ -197,11 +211,15 @@ class NewPlayerImpl(
}
}
override fun setPlayMode(playMode: PlayMode) {
this.mutablePlayMode.update { playMode }
}
private fun internalPlayStream(mediaItem: MediaItem, playMode: PlayMode) {
if (internalPlayer.playbackState == Player.STATE_IDLE) {
internalPlayer.prepare()
}
this.playMode.update { playMode }
this.mutablePlayMode.update { playMode }
this.internalPlayer.setMediaItem(mediaItem)
this.internalPlayer.play()
}

View File

@ -0,0 +1,6 @@
package net.newpipe.newplayer
class NewPlayerException : Exception {
constructor(message: String) : super(message)
constructor(message: String, cause: Throwable) : super(message, cause)
}

View File

@ -96,6 +96,17 @@ enum class UIModeState {
else -> this
}
fun toPlayMode() = when(this) {
PLACEHOLDER -> null
EMBEDDED_VIDEO -> PlayMode.EMBEDDED_VIDEO
EMBEDDED_VIDEO_CONTROLLER_UI -> PlayMode.EMBEDDED_VIDEO
EMBEDDED_VIDEO_CHAPTER_SELECT -> PlayMode.EMBEDDED_VIDEO
EMBEDDED_VIDEO_STREAM_SELECT -> PlayMode.EMBEDDED_VIDEO
FULLSCREEN_VIDEO -> PlayMode.FULLSCREEN_VIDEO
FULLSCREEN_VIDEO_CONTROLLER_UI -> PlayMode.FULLSCREEN_VIDEO
FULLSCREEN_VIDEO_CHAPTER_SELECT -> PlayMode.FULLSCREEN_VIDEO
FULLSCREEN_VIDEO_STREAM_SELECT -> PlayMode.FULLSCREEN_VIDEO
}
companion object {
fun fromPlayMode(playMode: PlayMode?) =

View File

@ -46,7 +46,7 @@ data class VideoPlayerUIState(
val DEFAULT = VideoPlayerUIState(
// TODO: replace this with the placeholder state.
// The actual initial state upon starting to play is dictated by the NewPlayer instance
uiMode = UIModeState.EMBEDDED_VIDEO,
uiMode = UIModeState.PLACEHOLDER,
//uiMode = UIModeState.PLACEHOLDER,
playing = false,
contentRatio = 16 / 9f,

View File

@ -28,7 +28,6 @@ import android.util.Log
import androidx.annotation.RequiresApi
import androidx.core.content.ContextCompat.getSystemService
import androidx.lifecycle.AndroidViewModel
import androidx.lifecycle.Lifecycle
import androidx.lifecycle.SavedStateHandle
import androidx.lifecycle.viewModelScope
import androidx.media3.common.Player
@ -134,7 +133,6 @@ class VideoPlayerViewModelImpl @Inject constructor(
override fun onVideoSizeChanged(videoSize: androidx.media3.common.VideoSize) {
super.onVideoSizeChanged(videoSize)
updateContentRatio(VideoSize.fromMedia3VideoSize(videoSize))
}
@ -150,11 +148,11 @@ class VideoPlayerViewModelImpl @Inject constructor(
}
newPlayer?.let { newPlayer ->
viewModelScope.launch {
while(true) {
newPlayer.playMode.collect { mode ->
println("blub: $mode")
newPlayer.playMode.collect { newMode ->
val currentMode = mutableUiState.value.uiMode.toPlayMode()
if (currentMode != newMode) {
mutableUiState.update {
it.copy(uiMode = UIModeState.fromPlayMode(mode))
it.copy(uiMode = UIModeState.fromPlayMode(newMode))
}
}
}
@ -184,15 +182,15 @@ class VideoPlayerViewModelImpl @Inject constructor(
@RequiresApi(Build.VERSION_CODES.TIRAMISU)
override fun initUIState(instanceState: Bundle) {
val uiState =
val recoveredUiState =
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 ->
if(recoveredUiState != null) {
mutableUiState.update {
uiState
recoveredUiState
}
}
}
@ -349,17 +347,25 @@ class VideoPlayerViewModelImpl @Inject constructor(
callbackListeners.forEach { it?.onFullscreenToggle(false) }
uiVisibilityJob?.cancel()
finishFastSeek()
mutableUiState.update {
it.copy(uiMode = UIModeState.EMBEDDED_VIDEO)
}
updateUiMode(UIModeState.EMBEDDED_VIDEO)
}
override fun switchToFullscreen() {
callbackListeners.forEach { it?.onFullscreenToggle(true) }
uiVisibilityJob?.cancel()
finishFastSeek()
updateUiMode(UIModeState.FULLSCREEN_VIDEO)
}
private fun updateUiMode(newState: UIModeState) {
val newPlayMode = newState.toPlayMode()
val currentPlayMode = mutableUiState.value.uiMode.toPlayMode()
if(newPlayMode != currentPlayMode) {
newPlayer?.setPlayMode(newPlayMode!!)
} else {
mutableUiState.update {
it.copy(uiMode = UIModeState.FULLSCREEN_VIDEO)
it.copy(uiMode = newState)
}
}
}