replace first callbacks with mutablestate/shareflow

This commit is contained in:
Christian Schabesberger 2024-08-24 14:06:23 +02:00
parent 32075dec73
commit ea099253a1
3 changed files with 43 additions and 38 deletions

View File

@ -30,7 +30,14 @@ import androidx.media3.exoplayer.source.MediaSource
import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.Job import kotlinx.coroutines.Job
import kotlinx.coroutines.flow.MutableSharedFlow
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.SharedFlow
import kotlinx.coroutines.flow.StateFlow
import kotlinx.coroutines.flow.asSharedFlow
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
@ -56,16 +63,13 @@ interface NewPlayer {
var currentPosition: Long var currentPosition: Long
var fastSeekAmountSec: Int var fastSeekAmountSec: Int
var playBackMode: PlayMode var playBackMode: PlayMode
var playMode: PlayMode? var playMode : MutableStateFlow<PlayMode?>
var playlist: PlayList var playlist: PlayList
// calbacks // callbacks
interface Listener { val errorFlow : SharedFlow<Exception>
fun playModeChange(playMode: PlayMode) {}
fun onError(exception: Exception) {}
}
// methods // methods
fun prepare() fun prepare()
@ -74,7 +78,6 @@ 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 addCallbackListener(listener: Listener?)
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
@ -111,6 +114,9 @@ class NewPlayerImpl(
override val repository: MediaRepository, override val repository: MediaRepository,
) : NewPlayer { ) : NewPlayer {
var mutableErrorFlow = MutableSharedFlow<Exception>()
override val errorFlow = mutableErrorFlow.asSharedFlow()
override val bufferedPercentage: Int override val bufferedPercentage: Int
get() = internalPlayer.bufferedPercentage get() = internalPlayer.bufferedPercentage
override var currentPosition: Long override var currentPosition: Long
@ -122,16 +128,9 @@ class NewPlayerImpl(
override var fastSeekAmountSec: Int = 10 override var fastSeekAmountSec: Int = 10
override var playBackMode: PlayMode = PlayMode.EMBEDDED_VIDEO override var playBackMode: PlayMode = PlayMode.EMBEDDED_VIDEO
private var callbackListener: ArrayList<NewPlayer.Listener?> = ArrayList()
private var playerScope = CoroutineScope(Dispatchers.Default + Job()) private var playerScope = CoroutineScope(Dispatchers.Default + Job())
override var playMode: PlayMode? = null override var playMode = MutableStateFlow<PlayMode?>(null)
set(value) {
field = value
if (field != null) {
callbackListener.forEach { it?.playModeChange(field!!) }
}
}
override var playWhenReady: Boolean override var playWhenReady: Boolean
set(value) { set(value) {
@ -154,9 +153,7 @@ class NewPlayerImpl(
if (newUri != null) { if (newUri != null) {
TODO("Implement handing new uri on fixed error") TODO("Implement handing new uri on fixed error")
} else { } else {
callbackListener.forEach { mutableErrorFlow.emit(error)
it?.onError(error)
}
} }
} }
} }
@ -204,7 +201,7 @@ class NewPlayerImpl(
if (internalPlayer.playbackState == Player.STATE_IDLE) { if (internalPlayer.playbackState == Player.STATE_IDLE) {
internalPlayer.prepare() internalPlayer.prepare()
} }
this.playMode = playMode this.playMode.update { playMode }
} }
private suspend fun toMediaItem(item: String, streamVariant: String): MediaItem { private suspend fun toMediaItem(item: String, streamVariant: String): MediaItem {
@ -232,13 +229,7 @@ class NewPlayerImpl(
try { try {
task() task()
} catch (e: Exception) { } catch (e: Exception) {
callbackListener.forEach { mutableErrorFlow.emit(e)
it?.onError(e)
} }
} }
} }
override fun addCallbackListener(listener: NewPlayer.Listener?) {
callbackListener.add(listener)
}
}

View File

@ -21,7 +21,9 @@
package net.newpipe.newplayer.utils package net.newpipe.newplayer.utils
import androidx.media3.common.MediaMetadata
import androidx.media3.common.Player import androidx.media3.common.Player
import androidx.media3.common.Player.Listener
// TODO: This is cool, but it might still contains all raceconditions since two actors are mutating the // TODO: This is cool, but it might still contains all raceconditions since two actors are mutating the
@ -32,6 +34,8 @@ import androidx.media3.common.Player
// due to this reason some functions force the user to handle elements out of bounds exceptions. // due to this reason some functions force the user to handle elements out of bounds exceptions.
class PlayListIterator( class PlayListIterator(
val exoPlayer: Player, val exoPlayer: Player,
val fromIndex: Int, val fromIndex: Int,
@ -83,6 +87,7 @@ class PlayList(val exoPlayer: Player, val fromIndex: Int = 0, val toIndex: Int =
override val size: Int override val size: Int
get() = minOf(exoPlayer.mediaItemCount, toIndex) - fromIndex get() = minOf(exoPlayer.mediaItemCount, toIndex) - fromIndex
// TODO: This contains a race condition. When the player might change the playlist while this function runns
override fun contains(element: String): Boolean { override fun contains(element: String): Boolean {
for (i in fromIndex..minOf(exoPlayer.mediaItemCount, toIndex)) { for (i in fromIndex..minOf(exoPlayer.mediaItemCount, toIndex)) {
try { try {
@ -95,6 +100,7 @@ class PlayList(val exoPlayer: Player, val fromIndex: Int = 0, val toIndex: Int =
return false return false
} }
// TODO: This contains a race condition. When the player might change the playlist while this function runns
override fun containsAll(elements: Collection<String>): Boolean { override fun containsAll(elements: Collection<String>): Boolean {
for (element in elements) { for (element in elements) {
if (!this.contains(element)) { if (!this.contains(element)) {
@ -106,10 +112,10 @@ class PlayList(val exoPlayer: Player, val fromIndex: Int = 0, val toIndex: Int =
@Throws(IndexOutOfBoundsException::class) @Throws(IndexOutOfBoundsException::class)
override fun get(index: Int) = override fun get(index: Int) =
if (index < fromIndex || toIndex < index) if (index < 0 || toIndex < index + fromIndex)
throw IndexOutOfBoundsException("Accessed playlist item outside of permitted Playlist ListWindow: $index") throw IndexOutOfBoundsException("Accessed playlist item outside of permitted Playlist ListWindow: $index with [$fromIndex;$toIndex[")
else else
exoPlayer.getMediaItemAt(index).mediaId exoPlayer.getMediaItemAt(index + fromIndex).mediaId
override fun isEmpty() = exoPlayer.mediaItemCount == 0 || fromIndex == toIndex override fun isEmpty() = exoPlayer.mediaItemCount == 0 || fromIndex == toIndex
@ -127,14 +133,21 @@ class PlayList(val exoPlayer: Player, val fromIndex: Int = 0, val toIndex: Int =
) )
override fun lastIndexOf(element: String): Int { override fun lastIndexOf(element: String): Int {
var mediaItemCount = 0
var newMediaItemCount = minOf(toIndex, exoPlayer.mediaItemCount)
// this while loop is there to catch raceconditions
while(mediaItemCount != newMediaItemCount) {
mediaItemCount = newMediaItemCount
for (i in minOf(toIndex, exoPlayer.mediaItemCount) downTo fromIndex) { for (i in minOf(toIndex, exoPlayer.mediaItemCount) downTo fromIndex) {
try { try {
if (exoPlayer.getMediaItemAt(i).mediaId == element) { if (exoPlayer.getMediaItemAt(i).mediaId == element) {
return i - fromIndex return i - fromIndex
} }
} catch (e: IndexOutOfBoundsException) { } catch (_: IndexOutOfBoundsException) {}
return -1
} }
newMediaItemCount = minOf(toIndex, exoPlayer.mediaItemCount)
} }
return -1 return -1
} }

View File

@ -34,6 +34,7 @@ import androidx.media3.common.MediaItem
import dagger.hilt.android.AndroidEntryPoint import dagger.hilt.android.AndroidEntryPoint
import net.newpipe.newplayer.ActivityBrainSlug import net.newpipe.newplayer.ActivityBrainSlug
import net.newpipe.newplayer.NewPlayer import net.newpipe.newplayer.NewPlayer
import net.newpipe.newplayer.PlayMode
import net.newpipe.newplayer.VideoPlayerView import net.newpipe.newplayer.VideoPlayerView
import net.newpipe.newplayer.model.VideoPlayerViewModel import net.newpipe.newplayer.model.VideoPlayerViewModel
import net.newpipe.newplayer.model.VideoPlayerViewModelImpl import net.newpipe.newplayer.model.VideoPlayerViewModelImpl
@ -63,7 +64,7 @@ class MainActivity : AppCompatActivity() {
startStreamButton.setOnClickListener { startStreamButton.setOnClickListener {
newPlayer.playWhenReady = true newPlayer.playWhenReady = true
newPlayer.setStream(MediaItem.fromUri(getString(R.string.ccc_6502_video))) newPlayer.playStream(getString(R.string.ccc_6502_video), PlayMode.EMBEDDED_VIDEO)
} }
videoPlayerViewModel.newPlayer = newPlayer videoPlayerViewModel.newPlayer = newPlayer