synchronize UI mode and playMode
This commit is contained in:
parent
47ad16c03d
commit
f3d3ce380f
|
@ -35,9 +35,9 @@ import kotlinx.coroutines.flow.MutableStateFlow
|
||||||
import kotlinx.coroutines.flow.SharedFlow
|
import kotlinx.coroutines.flow.SharedFlow
|
||||||
import kotlinx.coroutines.flow.StateFlow
|
import kotlinx.coroutines.flow.StateFlow
|
||||||
import kotlinx.coroutines.flow.asSharedFlow
|
import kotlinx.coroutines.flow.asSharedFlow
|
||||||
|
import kotlinx.coroutines.flow.asStateFlow
|
||||||
import kotlinx.coroutines.flow.update
|
import kotlinx.coroutines.flow.update
|
||||||
import kotlinx.coroutines.launch
|
import kotlinx.coroutines.launch
|
||||||
import net.newpipe.newplayer.model.UIModeState
|
|
||||||
import net.newpipe.newplayer.utils.PlayList
|
import net.newpipe.newplayer.utils.PlayList
|
||||||
import kotlin.Exception
|
import kotlin.Exception
|
||||||
|
|
||||||
|
@ -63,13 +63,14 @@ interface NewPlayer {
|
||||||
var currentPosition: Long
|
var currentPosition: Long
|
||||||
var fastSeekAmountSec: Int
|
var fastSeekAmountSec: Int
|
||||||
var playBackMode: PlayMode
|
var playBackMode: PlayMode
|
||||||
var playMode : MutableStateFlow<PlayMode?>
|
var playMode: StateFlow<PlayMode?>
|
||||||
|
|
||||||
var playlist: PlayList
|
var playlist: PlayList
|
||||||
|
|
||||||
// callbacks
|
// callbacks
|
||||||
|
|
||||||
val errorFlow: SharedFlow<Exception>
|
val errorFlow: SharedFlow<Exception>
|
||||||
|
val onExoPlayerEvent: SharedFlow<Pair<Player, Player.Events>>
|
||||||
|
|
||||||
// methods
|
// methods
|
||||||
fun prepare()
|
fun prepare()
|
||||||
|
@ -78,6 +79,7 @@ interface NewPlayer {
|
||||||
fun addToPlaylist(item: String)
|
fun addToPlaylist(item: String)
|
||||||
fun playStream(item: String, playMode: PlayMode)
|
fun playStream(item: String, playMode: PlayMode)
|
||||||
fun playStream(item: String, streamVariant: String, playMode: PlayMode)
|
fun playStream(item: String, streamVariant: String, playMode: PlayMode)
|
||||||
|
fun setPlayMode(playMode: PlayMode)
|
||||||
|
|
||||||
data class Builder(val app: Application, val repository: MediaRepository) {
|
data class Builder(val app: Application, val repository: MediaRepository) {
|
||||||
private var mediaSourceFactory: MediaSource.Factory? = null
|
private var mediaSourceFactory: MediaSource.Factory? = null
|
||||||
|
@ -130,7 +132,12 @@ class NewPlayerImpl(
|
||||||
|
|
||||||
private var playerScope = CoroutineScope(Dispatchers.Main + Job())
|
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
|
override var playWhenReady: Boolean
|
||||||
set(value) {
|
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) {
|
private fun internalPlayStream(mediaItem: MediaItem, playMode: PlayMode) {
|
||||||
if (internalPlayer.playbackState == Player.STATE_IDLE) {
|
if (internalPlayer.playbackState == Player.STATE_IDLE) {
|
||||||
internalPlayer.prepare()
|
internalPlayer.prepare()
|
||||||
}
|
}
|
||||||
this.playMode.update { playMode }
|
this.mutablePlayMode.update { playMode }
|
||||||
this.internalPlayer.setMediaItem(mediaItem)
|
this.internalPlayer.setMediaItem(mediaItem)
|
||||||
this.internalPlayer.play()
|
this.internalPlayer.play()
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,6 @@
|
||||||
|
package net.newpipe.newplayer
|
||||||
|
|
||||||
|
class NewPlayerException : Exception {
|
||||||
|
constructor(message: String) : super(message)
|
||||||
|
constructor(message: String, cause: Throwable) : super(message, cause)
|
||||||
|
}
|
|
@ -96,6 +96,17 @@ enum class UIModeState {
|
||||||
else -> this
|
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 {
|
companion object {
|
||||||
fun fromPlayMode(playMode: PlayMode?) =
|
fun fromPlayMode(playMode: PlayMode?) =
|
||||||
|
|
|
@ -46,7 +46,7 @@ data class VideoPlayerUIState(
|
||||||
val DEFAULT = VideoPlayerUIState(
|
val DEFAULT = VideoPlayerUIState(
|
||||||
// TODO: replace this with the placeholder state.
|
// TODO: replace this with the placeholder state.
|
||||||
// The actual initial state upon starting to play is dictated by the NewPlayer instance
|
// The actual initial state upon starting to play is dictated by the NewPlayer instance
|
||||||
uiMode = UIModeState.EMBEDDED_VIDEO,
|
uiMode = UIModeState.PLACEHOLDER,
|
||||||
//uiMode = UIModeState.PLACEHOLDER,
|
//uiMode = UIModeState.PLACEHOLDER,
|
||||||
playing = false,
|
playing = false,
|
||||||
contentRatio = 16 / 9f,
|
contentRatio = 16 / 9f,
|
||||||
|
|
|
@ -28,7 +28,6 @@ import android.util.Log
|
||||||
import androidx.annotation.RequiresApi
|
import androidx.annotation.RequiresApi
|
||||||
import androidx.core.content.ContextCompat.getSystemService
|
import androidx.core.content.ContextCompat.getSystemService
|
||||||
import androidx.lifecycle.AndroidViewModel
|
import androidx.lifecycle.AndroidViewModel
|
||||||
import androidx.lifecycle.Lifecycle
|
|
||||||
import androidx.lifecycle.SavedStateHandle
|
import androidx.lifecycle.SavedStateHandle
|
||||||
import androidx.lifecycle.viewModelScope
|
import androidx.lifecycle.viewModelScope
|
||||||
import androidx.media3.common.Player
|
import androidx.media3.common.Player
|
||||||
|
@ -134,7 +133,6 @@ class VideoPlayerViewModelImpl @Inject constructor(
|
||||||
|
|
||||||
override fun onVideoSizeChanged(videoSize: androidx.media3.common.VideoSize) {
|
override fun onVideoSizeChanged(videoSize: androidx.media3.common.VideoSize) {
|
||||||
super.onVideoSizeChanged(videoSize)
|
super.onVideoSizeChanged(videoSize)
|
||||||
|
|
||||||
updateContentRatio(VideoSize.fromMedia3VideoSize(videoSize))
|
updateContentRatio(VideoSize.fromMedia3VideoSize(videoSize))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -150,11 +148,11 @@ class VideoPlayerViewModelImpl @Inject constructor(
|
||||||
}
|
}
|
||||||
newPlayer?.let { newPlayer ->
|
newPlayer?.let { newPlayer ->
|
||||||
viewModelScope.launch {
|
viewModelScope.launch {
|
||||||
while(true) {
|
newPlayer.playMode.collect { newMode ->
|
||||||
newPlayer.playMode.collect { mode ->
|
val currentMode = mutableUiState.value.uiMode.toPlayMode()
|
||||||
println("blub: $mode")
|
if (currentMode != newMode) {
|
||||||
mutableUiState.update {
|
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)
|
@RequiresApi(Build.VERSION_CODES.TIRAMISU)
|
||||||
override fun initUIState(instanceState: Bundle) {
|
override fun initUIState(instanceState: Bundle) {
|
||||||
|
|
||||||
val uiState =
|
val recoveredUiState =
|
||||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) instanceState.getParcelable(
|
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) instanceState.getParcelable(
|
||||||
VIDEOPLAYER_UI_STATE, VideoPlayerUIState::class.java
|
VIDEOPLAYER_UI_STATE, VideoPlayerUIState::class.java
|
||||||
)
|
)
|
||||||
else instanceState.getParcelable(VIDEOPLAYER_UI_STATE)
|
else instanceState.getParcelable(VIDEOPLAYER_UI_STATE)
|
||||||
|
|
||||||
uiState?.let { uiState ->
|
if(recoveredUiState != null) {
|
||||||
mutableUiState.update {
|
mutableUiState.update {
|
||||||
uiState
|
recoveredUiState
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -349,17 +347,25 @@ class VideoPlayerViewModelImpl @Inject constructor(
|
||||||
callbackListeners.forEach { it?.onFullscreenToggle(false) }
|
callbackListeners.forEach { it?.onFullscreenToggle(false) }
|
||||||
uiVisibilityJob?.cancel()
|
uiVisibilityJob?.cancel()
|
||||||
finishFastSeek()
|
finishFastSeek()
|
||||||
mutableUiState.update {
|
updateUiMode(UIModeState.EMBEDDED_VIDEO)
|
||||||
it.copy(uiMode = UIModeState.EMBEDDED_VIDEO)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun switchToFullscreen() {
|
override fun switchToFullscreen() {
|
||||||
callbackListeners.forEach { it?.onFullscreenToggle(true) }
|
callbackListeners.forEach { it?.onFullscreenToggle(true) }
|
||||||
uiVisibilityJob?.cancel()
|
uiVisibilityJob?.cancel()
|
||||||
finishFastSeek()
|
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 {
|
mutableUiState.update {
|
||||||
it.copy(uiMode = UIModeState.FULLSCREEN_VIDEO)
|
it.copy(uiMode = newState)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue