make shuffle and repeatmode work
This commit is contained in:
parent
c0a006f238
commit
9a71e8f0b5
|
@ -53,6 +53,12 @@ enum class PlayMode {
|
||||||
AUDIO_FOREGROUND,
|
AUDIO_FOREGROUND,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
enum class RepeatMode {
|
||||||
|
DONT_REPEAT,
|
||||||
|
REPEAT_ALL,
|
||||||
|
REPEAT_ONE
|
||||||
|
}
|
||||||
|
|
||||||
private val TAG = "NewPlayer"
|
private val TAG = "NewPlayer"
|
||||||
|
|
||||||
interface NewPlayer {
|
interface NewPlayer {
|
||||||
|
@ -68,6 +74,8 @@ interface NewPlayer {
|
||||||
var fastSeekAmountSec: Int
|
var fastSeekAmountSec: Int
|
||||||
var playBackMode: PlayMode
|
var playBackMode: PlayMode
|
||||||
var playMode: StateFlow<PlayMode?>
|
var playMode: StateFlow<PlayMode?>
|
||||||
|
var shuffle: Boolean
|
||||||
|
var repeatMode: RepeatMode
|
||||||
|
|
||||||
val playlist: StateFlow<List<PlaylistItem>>
|
val playlist: StateFlow<List<PlaylistItem>>
|
||||||
|
|
||||||
|
@ -154,6 +162,27 @@ class NewPlayerImpl(
|
||||||
var mutablePlayMode = MutableStateFlow<PlayMode?>(null)
|
var mutablePlayMode = MutableStateFlow<PlayMode?>(null)
|
||||||
override var playMode = mutablePlayMode.asStateFlow()
|
override var playMode = mutablePlayMode.asStateFlow()
|
||||||
|
|
||||||
|
override var shuffle: Boolean
|
||||||
|
get() = internalPlayer.shuffleModeEnabled
|
||||||
|
set(value) {
|
||||||
|
internalPlayer.shuffleModeEnabled = value
|
||||||
|
}
|
||||||
|
|
||||||
|
override var repeatMode: RepeatMode
|
||||||
|
get() = when(internalPlayer.repeatMode) {
|
||||||
|
Player.REPEAT_MODE_OFF -> RepeatMode.DONT_REPEAT
|
||||||
|
Player.REPEAT_MODE_ALL -> RepeatMode.REPEAT_ALL
|
||||||
|
Player.REPEAT_MODE_ONE -> RepeatMode.REPEAT_ONE
|
||||||
|
else -> throw NewPlayerException("Unknown Repeatmode option returned by ExoPlayer: ${internalPlayer.repeatMode}")
|
||||||
|
}
|
||||||
|
set(value) {
|
||||||
|
when(value) {
|
||||||
|
RepeatMode.DONT_REPEAT -> internalPlayer.repeatMode = Player.REPEAT_MODE_OFF
|
||||||
|
RepeatMode.REPEAT_ALL -> internalPlayer.repeatMode = Player.REPEAT_MODE_ALL
|
||||||
|
RepeatMode.REPEAT_ONE -> internalPlayer.repeatMode = Player.REPEAT_MODE_ONE
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
var mutableOnEvent = MutableSharedFlow<Pair<Player, Player.Events>>()
|
var mutableOnEvent = MutableSharedFlow<Pair<Player, Player.Events>>()
|
||||||
override val onExoPlayerEvent: SharedFlow<Pair<Player, Player.Events>> =
|
override val onExoPlayerEvent: SharedFlow<Pair<Player, Player.Events>> =
|
||||||
mutableOnEvent.asSharedFlow()
|
mutableOnEvent.asSharedFlow()
|
||||||
|
|
|
@ -22,6 +22,7 @@ package net.newpipe.newplayer.model
|
||||||
|
|
||||||
import androidx.media3.common.Player
|
import androidx.media3.common.Player
|
||||||
import net.newpipe.newplayer.Chapter
|
import net.newpipe.newplayer.Chapter
|
||||||
|
import net.newpipe.newplayer.RepeatMode
|
||||||
import net.newpipe.newplayer.playerInternals.PlaylistItem
|
import net.newpipe.newplayer.playerInternals.PlaylistItem
|
||||||
import net.newpipe.newplayer.ui.ContentScale
|
import net.newpipe.newplayer.ui.ContentScale
|
||||||
|
|
||||||
|
@ -43,7 +44,7 @@ data class VideoPlayerUIState(
|
||||||
val playList: List<PlaylistItem>,
|
val playList: List<PlaylistItem>,
|
||||||
val chapters: List<Chapter>,
|
val chapters: List<Chapter>,
|
||||||
val shuffleEnabled: Boolean,
|
val shuffleEnabled: Boolean,
|
||||||
val repeatMode: Int,
|
val repeatMode: RepeatMode,
|
||||||
val playListDurationInS: Int
|
val playListDurationInS: Int
|
||||||
) {
|
) {
|
||||||
companion object {
|
companion object {
|
||||||
|
@ -68,7 +69,7 @@ data class VideoPlayerUIState(
|
||||||
playList = emptyList(),
|
playList = emptyList(),
|
||||||
chapters = emptyList(),
|
chapters = emptyList(),
|
||||||
shuffleEnabled = false,
|
shuffleEnabled = false,
|
||||||
repeatMode = Player.REPEAT_MODE_OFF,
|
repeatMode = RepeatMode.DONT_REPEAT,
|
||||||
playListDurationInS = 0
|
playListDurationInS = 0
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
|
@ -26,6 +26,7 @@ import kotlinx.coroutines.flow.SharedFlow
|
||||||
import kotlinx.coroutines.flow.StateFlow
|
import kotlinx.coroutines.flow.StateFlow
|
||||||
import net.newpipe.newplayer.Chapter
|
import net.newpipe.newplayer.Chapter
|
||||||
import net.newpipe.newplayer.NewPlayer
|
import net.newpipe.newplayer.NewPlayer
|
||||||
|
import net.newpipe.newplayer.RepeatMode
|
||||||
import net.newpipe.newplayer.ui.ContentScale
|
import net.newpipe.newplayer.ui.ContentScale
|
||||||
import net.newpipe.newplayer.utils.Thumbnail
|
import net.newpipe.newplayer.utils.Thumbnail
|
||||||
|
|
||||||
|
@ -60,8 +61,8 @@ interface VideoPlayerViewModel {
|
||||||
fun closeStreamSelection()
|
fun closeStreamSelection()
|
||||||
fun chapterSelected(chapter: Chapter)
|
fun chapterSelected(chapter: Chapter)
|
||||||
fun streamSelected(streamId: Int)
|
fun streamSelected(streamId: Int)
|
||||||
fun setRepeatmode(repeatMode: Int)
|
fun cycleRepeatmode()
|
||||||
fun setSuffleEnabled(enabled: Boolean)
|
fun toggleShuffle()
|
||||||
fun onStorePlaylist()
|
fun onStorePlaylist()
|
||||||
fun movePlaylistItem(from: Int, to: Int)
|
fun movePlaylistItem(from: Int, to: Int)
|
||||||
fun removePlaylistItem(index: Int)
|
fun removePlaylistItem(index: Int)
|
||||||
|
|
|
@ -45,6 +45,7 @@ import kotlinx.coroutines.launch
|
||||||
import net.newpipe.newplayer.Chapter
|
import net.newpipe.newplayer.Chapter
|
||||||
import net.newpipe.newplayer.utils.VideoSize
|
import net.newpipe.newplayer.utils.VideoSize
|
||||||
import net.newpipe.newplayer.NewPlayer
|
import net.newpipe.newplayer.NewPlayer
|
||||||
|
import net.newpipe.newplayer.RepeatMode
|
||||||
import net.newpipe.newplayer.ui.ContentScale
|
import net.newpipe.newplayer.ui.ContentScale
|
||||||
import java.util.LinkedList
|
import java.util.LinkedList
|
||||||
|
|
||||||
|
@ -132,7 +133,8 @@ class VideoPlayerViewModelImpl @Inject constructor(
|
||||||
override val onBackPressed: SharedFlow<Unit> = mutableOnBackPressed.asSharedFlow()
|
override val onBackPressed: SharedFlow<Unit> = mutableOnBackPressed.asSharedFlow()
|
||||||
|
|
||||||
private fun installNewPlayer() {
|
private fun installNewPlayer() {
|
||||||
newPlayer?.internalPlayer?.let { player ->
|
newPlayer?.let { newPlayer ->
|
||||||
|
val player = newPlayer.internalPlayer
|
||||||
Log.d(TAG, "Install player: ${player.videoSize.width}")
|
Log.d(TAG, "Install player: ${player.videoSize.width}")
|
||||||
|
|
||||||
player.addListener(object : Player.Listener {
|
player.addListener(object : Player.Listener {
|
||||||
|
@ -157,6 +159,20 @@ class VideoPlayerViewModelImpl @Inject constructor(
|
||||||
it.copy(isLoading = isLoading)
|
it.copy(isLoading = isLoading)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
override fun onRepeatModeChanged(repeatMode: Int) {
|
||||||
|
super.onRepeatModeChanged(repeatMode)
|
||||||
|
mutableUiState.update {
|
||||||
|
it.copy(repeatMode = newPlayer.repeatMode)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onShuffleModeEnabledChanged(shuffleModeEnabled: Boolean) {
|
||||||
|
super.onShuffleModeEnabledChanged(shuffleModeEnabled)
|
||||||
|
mutableUiState.update {
|
||||||
|
it.copy(shuffleEnabled = newPlayer.shuffle)
|
||||||
|
}
|
||||||
|
}
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
newPlayer?.let { newPlayer ->
|
newPlayer?.let { newPlayer ->
|
||||||
|
@ -406,19 +422,20 @@ class VideoPlayerViewModelImpl @Inject constructor(
|
||||||
println("stream selected: $streamId")
|
println("stream selected: $streamId")
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun setRepeatmode(repeatMode: Int) {
|
override fun cycleRepeatmode() {
|
||||||
assert(
|
newPlayer?.let {
|
||||||
repeatMode == Player.REPEAT_MODE_ALL
|
it.repeatMode = when (it.repeatMode) {
|
||||||
|| repeatMode == Player.REPEAT_MODE_OFF
|
RepeatMode.DONT_REPEAT -> RepeatMode.REPEAT_ALL
|
||||||
|| repeatMode == Player.REPEAT_MODE_ONE
|
RepeatMode.REPEAT_ALL -> RepeatMode.REPEAT_ONE
|
||||||
) {
|
RepeatMode.REPEAT_ONE -> RepeatMode.DONT_REPEAT
|
||||||
"Illegal repeat mode: $repeatMode"
|
}
|
||||||
}
|
}
|
||||||
TODO("Not yet implemented")
|
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun setSuffleEnabled(enabled: Boolean) {
|
override fun toggleShuffle() {
|
||||||
TODO("Not yet implemented")
|
newPlayer?.let {
|
||||||
|
it.shuffle = !it.shuffle
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onStorePlaylist() {
|
override fun onStorePlaylist() {
|
||||||
|
@ -453,7 +470,6 @@ class VideoPlayerViewModelImpl @Inject constructor(
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
private fun updateUiMode(newState: UIModeState) {
|
private fun updateUiMode(newState: UIModeState) {
|
||||||
val newPlayMode = newState.toPlayMode()
|
val newPlayMode = newState.toPlayMode()
|
||||||
val currentPlayMode = mutableUiState.value.uiMode.toPlayMode()
|
val currentPlayMode = mutableUiState.value.uiMode.toPlayMode()
|
||||||
|
|
|
@ -8,6 +8,7 @@ import kotlinx.coroutines.flow.SharedFlow
|
||||||
import kotlinx.coroutines.flow.asSharedFlow
|
import kotlinx.coroutines.flow.asSharedFlow
|
||||||
import net.newpipe.newplayer.Chapter
|
import net.newpipe.newplayer.Chapter
|
||||||
import net.newpipe.newplayer.NewPlayer
|
import net.newpipe.newplayer.NewPlayer
|
||||||
|
import net.newpipe.newplayer.RepeatMode
|
||||||
import net.newpipe.newplayer.ui.ContentScale
|
import net.newpipe.newplayer.ui.ContentScale
|
||||||
|
|
||||||
open class VideoPlayerViewModelDummy : VideoPlayerViewModel {
|
open class VideoPlayerViewModelDummy : VideoPlayerViewModel {
|
||||||
|
@ -91,12 +92,12 @@ open class VideoPlayerViewModelDummy : VideoPlayerViewModel {
|
||||||
println("dummy impl stream selected: $streamId")
|
println("dummy impl stream selected: $streamId")
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun setRepeatmode(repeatMode: Int) {
|
override fun cycleRepeatmode() {
|
||||||
println("dummy impl repeat mode: $repeatMode")
|
println("dummy impl")
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun setSuffleEnabled(enabled: Boolean) {
|
override fun toggleShuffle() {
|
||||||
println("dummy impl shuffle enabled: $enabled")
|
println("dummy impl")
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onStorePlaylist() {
|
override fun onStorePlaylist() {
|
||||||
|
|
|
@ -46,6 +46,7 @@ import androidx.compose.ui.tooling.preview.Preview
|
||||||
import androidx.media3.common.Player
|
import androidx.media3.common.Player
|
||||||
import net.newpipe.newplayer.NewPlayerException
|
import net.newpipe.newplayer.NewPlayerException
|
||||||
import net.newpipe.newplayer.R
|
import net.newpipe.newplayer.R
|
||||||
|
import net.newpipe.newplayer.RepeatMode
|
||||||
import net.newpipe.newplayer.model.VideoPlayerUIState
|
import net.newpipe.newplayer.model.VideoPlayerUIState
|
||||||
import net.newpipe.newplayer.model.VideoPlayerViewModel
|
import net.newpipe.newplayer.model.VideoPlayerViewModel
|
||||||
import net.newpipe.newplayer.model.VideoPlayerViewModelDummy
|
import net.newpipe.newplayer.model.VideoPlayerViewModelDummy
|
||||||
|
@ -75,41 +76,28 @@ fun StreamSelectTopBar(
|
||||||
)
|
)
|
||||||
}, actions = {
|
}, actions = {
|
||||||
IconButton(
|
IconButton(
|
||||||
onClick = {
|
onClick = viewModel::cycleRepeatmode
|
||||||
viewModel.setRepeatmode(
|
|
||||||
when (uiState.repeatMode) {
|
|
||||||
Player.REPEAT_MODE_OFF -> Player.REPEAT_MODE_ALL
|
|
||||||
Player.REPEAT_MODE_ALL -> Player.REPEAT_MODE_ONE
|
|
||||||
Player.REPEAT_MODE_ONE -> Player.REPEAT_MODE_OFF
|
|
||||||
else -> throw NewPlayerException("Unknown repeat mode: ${uiState.repeatMode}")
|
|
||||||
}
|
|
||||||
)
|
|
||||||
}
|
|
||||||
) {
|
) {
|
||||||
when (uiState.repeatMode) {
|
when (uiState.repeatMode) {
|
||||||
Player.REPEAT_MODE_OFF -> Icon(
|
RepeatMode.DONT_REPEAT -> Icon(
|
||||||
imageVector = Icons.Filled.Repeat,
|
imageVector = Icons.Filled.Repeat,
|
||||||
contentDescription = stringResource(R.string.repeat_mode_no_repeat)
|
contentDescription = stringResource(R.string.repeat_mode_no_repeat)
|
||||||
)
|
)
|
||||||
|
|
||||||
Player.REPEAT_MODE_ALL -> Icon(
|
RepeatMode.REPEAT_ALL -> Icon(
|
||||||
imageVector = Icons.Filled.RepeatOn,
|
imageVector = Icons.Filled.RepeatOn,
|
||||||
contentDescription = stringResource(R.string.repeat_mode_repeat_all)
|
contentDescription = stringResource(R.string.repeat_mode_repeat_all)
|
||||||
)
|
)
|
||||||
|
|
||||||
Player.REPEAT_MODE_ONE -> Icon(
|
RepeatMode.REPEAT_ONE -> Icon(
|
||||||
imageVector = Icons.Filled.RepeatOneOn,
|
imageVector = Icons.Filled.RepeatOneOn,
|
||||||
contentDescription = stringResource(R.string.repeat_mode_repeat_all)
|
contentDescription = stringResource(R.string.repeat_mode_repeat_all)
|
||||||
)
|
)
|
||||||
|
|
||||||
else -> throw NewPlayerException("Unknown repeat mode: ${uiState.repeatMode}")
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
IconButton(
|
IconButton(
|
||||||
onClick = {
|
onClick = viewModel::toggleShuffle
|
||||||
viewModel.setSuffleEnabled(!uiState.shuffleEnabled)
|
|
||||||
}
|
|
||||||
) {
|
) {
|
||||||
if (uiState.shuffleEnabled) {
|
if (uiState.shuffleEnabled) {
|
||||||
Icon(
|
Icon(
|
||||||
|
|
Loading…
Reference in New Issue