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 ef03bc0..77f0d2b 100644 --- a/new-player/src/main/java/net/newpipe/newplayer/NewPlayer.kt +++ b/new-player/src/main/java/net/newpipe/newplayer/NewPlayer.kt @@ -53,6 +53,12 @@ enum class PlayMode { AUDIO_FOREGROUND, } +enum class RepeatMode { + DONT_REPEAT, + REPEAT_ALL, + REPEAT_ONE +} + private val TAG = "NewPlayer" interface NewPlayer { @@ -68,6 +74,8 @@ interface NewPlayer { var fastSeekAmountSec: Int var playBackMode: PlayMode var playMode: StateFlow + var shuffle: Boolean + var repeatMode: RepeatMode val playlist: StateFlow> @@ -154,6 +162,27 @@ class NewPlayerImpl( var mutablePlayMode = MutableStateFlow(null) 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>() override val onExoPlayerEvent: SharedFlow> = mutableOnEvent.asSharedFlow() 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 07c8f85..fd896b4 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 @@ -22,6 +22,7 @@ package net.newpipe.newplayer.model import androidx.media3.common.Player import net.newpipe.newplayer.Chapter +import net.newpipe.newplayer.RepeatMode import net.newpipe.newplayer.playerInternals.PlaylistItem import net.newpipe.newplayer.ui.ContentScale @@ -43,7 +44,7 @@ data class VideoPlayerUIState( val playList: List, val chapters: List, val shuffleEnabled: Boolean, - val repeatMode: Int, + val repeatMode: RepeatMode, val playListDurationInS: Int ) { companion object { @@ -68,7 +69,7 @@ data class VideoPlayerUIState( playList = emptyList(), chapters = emptyList(), shuffleEnabled = false, - repeatMode = Player.REPEAT_MODE_OFF, + repeatMode = RepeatMode.DONT_REPEAT, playListDurationInS = 0 ) } diff --git a/new-player/src/main/java/net/newpipe/newplayer/model/VideoPlayerViewModel.kt b/new-player/src/main/java/net/newpipe/newplayer/model/VideoPlayerViewModel.kt index 12aa255..e8900c5 100644 --- a/new-player/src/main/java/net/newpipe/newplayer/model/VideoPlayerViewModel.kt +++ b/new-player/src/main/java/net/newpipe/newplayer/model/VideoPlayerViewModel.kt @@ -26,6 +26,7 @@ import kotlinx.coroutines.flow.SharedFlow import kotlinx.coroutines.flow.StateFlow import net.newpipe.newplayer.Chapter import net.newpipe.newplayer.NewPlayer +import net.newpipe.newplayer.RepeatMode import net.newpipe.newplayer.ui.ContentScale import net.newpipe.newplayer.utils.Thumbnail @@ -60,8 +61,8 @@ interface VideoPlayerViewModel { fun closeStreamSelection() fun chapterSelected(chapter: Chapter) fun streamSelected(streamId: Int) - fun setRepeatmode(repeatMode: Int) - fun setSuffleEnabled(enabled: Boolean) + fun cycleRepeatmode() + fun toggleShuffle() fun onStorePlaylist() fun movePlaylistItem(from: Int, to: Int) fun removePlaylistItem(index: Int) 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 265d832..c7b5f87 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 @@ -45,6 +45,7 @@ import kotlinx.coroutines.launch import net.newpipe.newplayer.Chapter import net.newpipe.newplayer.utils.VideoSize import net.newpipe.newplayer.NewPlayer +import net.newpipe.newplayer.RepeatMode import net.newpipe.newplayer.ui.ContentScale import java.util.LinkedList @@ -132,7 +133,8 @@ class VideoPlayerViewModelImpl @Inject constructor( override val onBackPressed: SharedFlow = mutableOnBackPressed.asSharedFlow() private fun installNewPlayer() { - newPlayer?.internalPlayer?.let { player -> + newPlayer?.let { newPlayer -> + val player = newPlayer.internalPlayer Log.d(TAG, "Install player: ${player.videoSize.width}") player.addListener(object : Player.Listener { @@ -157,6 +159,20 @@ class VideoPlayerViewModelImpl @Inject constructor( 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 -> @@ -406,19 +422,20 @@ class VideoPlayerViewModelImpl @Inject constructor( println("stream selected: $streamId") } - override fun setRepeatmode(repeatMode: Int) { - assert( - repeatMode == Player.REPEAT_MODE_ALL - || repeatMode == Player.REPEAT_MODE_OFF - || repeatMode == Player.REPEAT_MODE_ONE - ) { - "Illegal repeat mode: $repeatMode" + override fun cycleRepeatmode() { + newPlayer?.let { + it.repeatMode = when (it.repeatMode) { + RepeatMode.DONT_REPEAT -> RepeatMode.REPEAT_ALL + RepeatMode.REPEAT_ALL -> RepeatMode.REPEAT_ONE + RepeatMode.REPEAT_ONE -> RepeatMode.DONT_REPEAT + } } - TODO("Not yet implemented") } - override fun setSuffleEnabled(enabled: Boolean) { - TODO("Not yet implemented") + override fun toggleShuffle() { + newPlayer?.let { + it.shuffle = !it.shuffle + } } override fun onStorePlaylist() { @@ -426,7 +443,7 @@ class VideoPlayerViewModelImpl @Inject constructor( } override fun movePlaylistItem(from: Int, to: Int) { - if(playlistItemToBeMoved == null) { + if (playlistItemToBeMoved == null) { playlistItemToBeMoved = from } playlistItemNewPosition = to @@ -453,7 +470,6 @@ class VideoPlayerViewModelImpl @Inject constructor( } - private fun updateUiMode(newState: UIModeState) { val newPlayMode = newState.toPlayMode() val currentPlayMode = mutableUiState.value.uiMode.toPlayMode() diff --git a/new-player/src/main/java/net/newpipe/newplayer/model/ViewoPlayerViewModelDummy.kt b/new-player/src/main/java/net/newpipe/newplayer/model/ViewoPlayerViewModelDummy.kt index 2ccee1c..d3065db 100644 --- a/new-player/src/main/java/net/newpipe/newplayer/model/ViewoPlayerViewModelDummy.kt +++ b/new-player/src/main/java/net/newpipe/newplayer/model/ViewoPlayerViewModelDummy.kt @@ -8,6 +8,7 @@ import kotlinx.coroutines.flow.SharedFlow import kotlinx.coroutines.flow.asSharedFlow import net.newpipe.newplayer.Chapter import net.newpipe.newplayer.NewPlayer +import net.newpipe.newplayer.RepeatMode import net.newpipe.newplayer.ui.ContentScale open class VideoPlayerViewModelDummy : VideoPlayerViewModel { @@ -91,12 +92,12 @@ open class VideoPlayerViewModelDummy : VideoPlayerViewModel { println("dummy impl stream selected: $streamId") } - override fun setRepeatmode(repeatMode: Int) { - println("dummy impl repeat mode: $repeatMode") + override fun cycleRepeatmode() { + println("dummy impl") } - override fun setSuffleEnabled(enabled: Boolean) { - println("dummy impl shuffle enabled: $enabled") + override fun toggleShuffle() { + println("dummy impl") } override fun onStorePlaylist() { diff --git a/new-player/src/main/java/net/newpipe/newplayer/ui/videoplayer/streamselect/StreamSelectTopBar.kt b/new-player/src/main/java/net/newpipe/newplayer/ui/videoplayer/streamselect/StreamSelectTopBar.kt index 03bd213..f7da6ea 100644 --- a/new-player/src/main/java/net/newpipe/newplayer/ui/videoplayer/streamselect/StreamSelectTopBar.kt +++ b/new-player/src/main/java/net/newpipe/newplayer/ui/videoplayer/streamselect/StreamSelectTopBar.kt @@ -46,6 +46,7 @@ import androidx.compose.ui.tooling.preview.Preview import androidx.media3.common.Player import net.newpipe.newplayer.NewPlayerException import net.newpipe.newplayer.R +import net.newpipe.newplayer.RepeatMode import net.newpipe.newplayer.model.VideoPlayerUIState import net.newpipe.newplayer.model.VideoPlayerViewModel import net.newpipe.newplayer.model.VideoPlayerViewModelDummy @@ -75,41 +76,28 @@ fun StreamSelectTopBar( ) }, actions = { IconButton( - onClick = { - 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}") - } - ) - } + onClick = viewModel::cycleRepeatmode ) { when (uiState.repeatMode) { - Player.REPEAT_MODE_OFF -> Icon( + RepeatMode.DONT_REPEAT -> Icon( imageVector = Icons.Filled.Repeat, contentDescription = stringResource(R.string.repeat_mode_no_repeat) ) - Player.REPEAT_MODE_ALL -> Icon( + RepeatMode.REPEAT_ALL -> Icon( imageVector = Icons.Filled.RepeatOn, contentDescription = stringResource(R.string.repeat_mode_repeat_all) ) - Player.REPEAT_MODE_ONE -> Icon( + RepeatMode.REPEAT_ONE -> Icon( imageVector = Icons.Filled.RepeatOneOn, contentDescription = stringResource(R.string.repeat_mode_repeat_all) ) - - else -> throw NewPlayerException("Unknown repeat mode: ${uiState.repeatMode}") } } IconButton( - onClick = { - viewModel.setSuffleEnabled(!uiState.shuffleEnabled) - } + onClick = viewModel::toggleShuffle ) { if (uiState.shuffleEnabled) { Icon(