From f3d3ce380f6f3207a402041d808b4c40de2b2da6 Mon Sep 17 00:00:00 2001 From: Christian Schabesberger Date: Mon, 26 Aug 2024 12:09:51 +0200 Subject: [PATCH] synchronize UI mode and playMode --- .../java/net/newpipe/newplayer/NewPlayer.kt | 30 ++++++++++++---- .../newpipe/newplayer/NewPlayerException.kt | 6 ++++ .../newpipe/newplayer/model/UIModeState.kt | 11 ++++++ .../newplayer/model/VideoPlayerUIState.kt | 2 +- .../model/VideoPlayerViewModelImpl.kt | 36 +++++++++++-------- 5 files changed, 63 insertions(+), 22 deletions(-) create mode 100644 new-player/src/main/java/net/newpipe/newplayer/NewPlayerException.kt diff --git a/new-player/src/main/java/net/newpipe/newplayer/NewPlayer.kt b/new-player/src/main/java/net/newpipe/newplayer/NewPlayer.kt index 667bc79..3507424 100644 --- a/new-player/src/main/java/net/newpipe/newplayer/NewPlayer.kt +++ b/new-player/src/main/java/net/newpipe/newplayer/NewPlayer.kt @@ -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 + var playMode: StateFlow var playlist: PlayList // callbacks - val errorFlow : SharedFlow + val errorFlow: SharedFlow + val onExoPlayerEvent: SharedFlow> // 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 @@ -114,7 +116,7 @@ class NewPlayerImpl( override val repository: MediaRepository, ) : NewPlayer { - var mutableErrorFlow = MutableSharedFlow() + var mutableErrorFlow = MutableSharedFlow() override val errorFlow = mutableErrorFlow.asSharedFlow() override val bufferedPercentage: Int @@ -130,7 +132,12 @@ class NewPlayerImpl( private var playerScope = CoroutineScope(Dispatchers.Main + Job()) - override var playMode = MutableStateFlow(null) + var mutablePlayMode = MutableStateFlow(null) + override var playMode = mutablePlayMode.asStateFlow() + + var mutableOnEvent = MutableSharedFlow>() + override val onExoPlayerEvent: SharedFlow> = + 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() } diff --git a/new-player/src/main/java/net/newpipe/newplayer/NewPlayerException.kt b/new-player/src/main/java/net/newpipe/newplayer/NewPlayerException.kt new file mode 100644 index 0000000..3f05652 --- /dev/null +++ b/new-player/src/main/java/net/newpipe/newplayer/NewPlayerException.kt @@ -0,0 +1,6 @@ +package net.newpipe.newplayer + +class NewPlayerException : Exception { + constructor(message: String) : super(message) + constructor(message: String, cause: Throwable) : super(message, cause) +} \ No newline at end of file diff --git a/new-player/src/main/java/net/newpipe/newplayer/model/UIModeState.kt b/new-player/src/main/java/net/newpipe/newplayer/model/UIModeState.kt index 3b682bb..bbbfe37 100644 --- a/new-player/src/main/java/net/newpipe/newplayer/model/UIModeState.kt +++ b/new-player/src/main/java/net/newpipe/newplayer/model/UIModeState.kt @@ -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?) = diff --git a/new-player/src/main/java/net/newpipe/newplayer/model/VideoPlayerUIState.kt b/new-player/src/main/java/net/newpipe/newplayer/model/VideoPlayerUIState.kt index 61c91c6..3b4b108 100644 --- a/new-player/src/main/java/net/newpipe/newplayer/model/VideoPlayerUIState.kt +++ b/new-player/src/main/java/net/newpipe/newplayer/model/VideoPlayerUIState.kt @@ -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, diff --git a/new-player/src/main/java/net/newpipe/newplayer/model/VideoPlayerViewModelImpl.kt b/new-player/src/main/java/net/newpipe/newplayer/model/VideoPlayerViewModelImpl.kt index e99174d..d146fcd 100644 --- a/new-player/src/main/java/net/newpipe/newplayer/model/VideoPlayerViewModelImpl.kt +++ b/new-player/src/main/java/net/newpipe/newplayer/model/VideoPlayerViewModelImpl.kt @@ -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)) } @@ -148,13 +146,13 @@ class VideoPlayerViewModelImpl @Inject constructor( } }) } - newPlayer?.let{ newPlayer -> + 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() - mutableUiState.update { - it.copy(uiMode = UIModeState.FULLSCREEN_VIDEO) + 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 = newState) + } } }