make goto next or prev streem buttons work

This commit is contained in:
Christian Schabesberger 2024-09-06 15:42:19 +02:00
parent 106060625c
commit 58e0ad9f09
5 changed files with 122 additions and 80 deletions

View File

@ -79,6 +79,7 @@ interface NewPlayer {
val playlist: StateFlow<List<PlaylistItem>> val playlist: StateFlow<List<PlaylistItem>>
val currentlyPlaying: StateFlow<PlaylistItem?> val currentlyPlaying: StateFlow<PlaylistItem?>
var currentlyPlayingPlaylistItem: Int
val currentChapters: StateFlow<List<Chapter>> val currentChapters: StateFlow<List<Chapter>>
@ -96,7 +97,6 @@ interface NewPlayer {
fun removePlaylistItem(index: Int) fun removePlaylistItem(index: Int)
fun playStream(item: String, playMode: PlayMode) fun playStream(item: String, playMode: PlayMode)
fun selectChapter(index: Int) fun selectChapter(index: Int)
fun selectPlaylistItem(index: Int)
fun playStream(item: String, streamVariant: String, playMode: PlayMode) fun playStream(item: String, streamVariant: String, playMode: PlayMode)
data class Builder(val app: Application, val repository: MediaRepository) { data class Builder(val app: Application, val repository: MediaRepository) {
@ -209,6 +209,15 @@ class NewPlayerImpl(
private val mutableCurrentChapter = MutableStateFlow<List<Chapter>>(emptyList()) private val mutableCurrentChapter = MutableStateFlow<List<Chapter>>(emptyList())
override val currentChapters: StateFlow<List<Chapter>> = mutableCurrentChapter.asStateFlow() override val currentChapters: StateFlow<List<Chapter>> = mutableCurrentChapter.asStateFlow()
override var currentlyPlayingPlaylistItem: Int
get() = internalPlayer.currentMediaItemIndex
set(value) {
assert(value in 0..<playlist.value.size) {
throw NewPlayerException("Playlist item selection out of bound: selected item index: $value, available chapters: ${playlist.value.size}")
}
internalPlayer.seekTo(value, 0)
}
init { init {
internalPlayer.addListener(object : Player.Listener { internalPlayer.addListener(object : Player.Listener {
override fun onPlayerError(error: PlaybackException) { override fun onPlayerError(error: PlaybackException) {
@ -349,13 +358,6 @@ class NewPlayerImpl(
currentPosition = chapter.chapterStartInMs currentPosition = chapter.chapterStartInMs
} }
override fun selectPlaylistItem(index: Int) {
assert(index in 0..<playlist.value.size) {
throw NewPlayerException("Playlist item selection out of bound: selected item index: $index, available chapters: ${playlist.value.size}")
}
internalPlayer.seekTo(index, 0)
}
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()

View File

@ -46,7 +46,8 @@ data class VideoPlayerUIState(
val shuffleEnabled: Boolean, val shuffleEnabled: Boolean,
val repeatMode: RepeatMode, val repeatMode: RepeatMode,
val playListDurationInS: Int, val playListDurationInS: Int,
val currentlyPlaying: PlaylistItem val currentlyPlaying: PlaylistItem,
val currentPlaylistItemIndex: Int
) { ) {
companion object { companion object {
val DEFAULT = VideoPlayerUIState( val DEFAULT = VideoPlayerUIState(
@ -70,7 +71,8 @@ data class VideoPlayerUIState(
shuffleEnabled = false, shuffleEnabled = false,
repeatMode = RepeatMode.DONT_REPEAT, repeatMode = RepeatMode.DONT_REPEAT,
playListDurationInS = 0, playListDurationInS = 0,
currentlyPlaying = PlaylistItem.DEFAULT currentlyPlaying = PlaylistItem.DEFAULT,
currentPlaylistItemIndex = 0
) )
val DUMMY = DEFAULT.copy( val DUMMY = DEFAULT.copy(
@ -87,7 +89,51 @@ data class VideoPlayerUIState(
brightness = 0.2f, brightness = 0.2f,
shuffleEnabled = true, shuffleEnabled = true,
playListDurationInS = 5493, playListDurationInS = 5493,
currentlyPlaying = PlaylistItem.DUMMY currentlyPlaying = PlaylistItem.DUMMY,
currentPlaylistItemIndex = 1,
chapters = arrayListOf(
Chapter(
chapterStartInMs = 5000,
chapterTitle = "First Chapter",
thumbnail = null
),
Chapter(
chapterStartInMs = 10000,
chapterTitle = "Second Chapter",
thumbnail = null
),
Chapter(
chapterStartInMs = 20000,
chapterTitle = "Third Chapter",
thumbnail = null
)
),
playList = arrayListOf(
PlaylistItem(
id = "6502",
title = "Stream 1",
creator = "The Creator",
lengthInS = 6 * 60 + 5,
thumbnail = null,
uniqueId = 0
),
PlaylistItem(
id = "6502",
title = "Stream 2",
creator = "The Creator 2",
lengthInS = 2 * 60 + 5,
thumbnail = null,
uniqueId = 1
),
PlaylistItem(
id = "6502",
title = "Stream 3",
creator = "The Creator 3",
lengthInS = 29 * 60 + 5,
thumbnail = null,
uniqueId = 2
)
)
) )
} }
} }

View File

@ -30,6 +30,7 @@ import androidx.core.content.ContextCompat.getSystemService
import androidx.lifecycle.AndroidViewModel import androidx.lifecycle.AndroidViewModel
import androidx.lifecycle.SavedStateHandle import androidx.lifecycle.SavedStateHandle
import androidx.lifecycle.viewModelScope import androidx.lifecycle.viewModelScope
import androidx.media3.common.MediaItem
import androidx.media3.common.Player import androidx.media3.common.Player
import dagger.hilt.android.lifecycle.HiltViewModel import dagger.hilt.android.lifecycle.HiltViewModel
import kotlinx.coroutines.Job import kotlinx.coroutines.Job
@ -42,7 +43,6 @@ import javax.inject.Inject
import kotlinx.coroutines.flow.asStateFlow 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.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.RepeatMode
@ -194,13 +194,20 @@ class VideoPlayerViewModelImpl @Inject constructor(
} }
viewModelScope.launch { viewModelScope.launch {
newPlayer.playlist.collect { playlist -> newPlayer.playlist.collect { playlist ->
mutableUiState.update { it.copy(playList = playlist) } mutableUiState.update {
it.copy(
playList = playlist,
)
}
} }
} }
viewModelScope.launch { viewModelScope.launch {
newPlayer.currentlyPlaying.collect { playlistItem -> newPlayer.currentlyPlaying.collect { playlistItem ->
mutableUiState.update { mutableUiState.update {
it.copy(currentlyPlaying = playlistItem ?: PlaylistItem.DEFAULT) it.copy(
currentlyPlaying = playlistItem ?: PlaylistItem.DEFAULT,
currentPlaylistItemIndex = newPlayer.currentlyPlayingPlaylistItem
)
} }
} }
} }
@ -261,12 +268,20 @@ class VideoPlayerViewModelImpl @Inject constructor(
override fun prevStream() { override fun prevStream() {
resetHideUiDelayedJob() resetHideUiDelayedJob()
Log.e(TAG, "imeplement prev stream") newPlayer?.let { newPlayer ->
if (0 <= newPlayer.currentlyPlayingPlaylistItem - 1) {
newPlayer.currentlyPlayingPlaylistItem -= 1
}
}
} }
override fun nextStream() { override fun nextStream() {
resetHideUiDelayedJob() resetHideUiDelayedJob()
Log.e(TAG, "implement next stream") newPlayer?.let { newPlayer ->
if (newPlayer.currentlyPlayingPlaylistItem + 1 < newPlayer.internalPlayer.mediaItemCount) {
newPlayer.currentlyPlayingPlaylistItem += 1
}
}
} }
override fun showUi() { override fun showUi() {
@ -427,7 +442,7 @@ class VideoPlayerViewModelImpl @Inject constructor(
if (selectChapter) uiState.value.uiMode.getChapterSelectUiState() if (selectChapter) uiState.value.uiMode.getChapterSelectUiState()
else uiState.value.uiMode.getStreamSelectUiState() else uiState.value.uiMode.getStreamSelectUiState()
) )
if(selectChapter) { if (selectChapter) {
resetProgressUpdatePeriodicallyJob() resetProgressUpdatePeriodicallyJob()
} else { } else {
resetPlaylistProgressUpdaterJob() resetPlaylistProgressUpdaterJob()
@ -468,8 +483,7 @@ class VideoPlayerViewModelImpl @Inject constructor(
} }
override fun streamSelected(streamId: Int) { override fun streamSelected(streamId: Int) {
println("gurken stream selected: $streamId") newPlayer?.currentlyPlayingPlaylistItem = streamId
newPlayer?.selectPlaylistItem(streamId)
} }
override fun cycleRepeatMode() { override fun cycleRepeatMode() {

View File

@ -21,9 +21,15 @@
package net.newpipe.newplayer.ui.videoplayer package net.newpipe.newplayer.ui.videoplayer
import androidx.compose.animation.AnimatedVisibility import androidx.compose.animation.AnimatedVisibility
import androidx.compose.animation.core.tween
import androidx.compose.animation.fadeIn
import androidx.compose.animation.fadeOut
import androidx.compose.foundation.layout.Arrangement import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.BoxScope
import androidx.compose.foundation.layout.PaddingValues import androidx.compose.foundation.layout.PaddingValues
import androidx.compose.foundation.layout.Row import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.size import androidx.compose.foundation.layout.size
import androidx.compose.material.icons.Icons import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.filled.Pause import androidx.compose.material.icons.filled.Pause
@ -33,6 +39,7 @@ import androidx.compose.material.icons.filled.SkipPrevious
import androidx.compose.material3.Button import androidx.compose.material3.Button
import androidx.compose.material3.ButtonDefaults import androidx.compose.material3.ButtonDefaults
import androidx.compose.material3.Icon import androidx.compose.material3.Icon
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Surface import androidx.compose.material3.Surface
import androidx.compose.runtime.Composable import androidx.compose.runtime.Composable
import androidx.compose.ui.Alignment import androidx.compose.ui.Alignment
@ -60,13 +67,22 @@ fun CenterUI(
modifier = modifier, modifier = modifier,
) { ) {
CenterControllButton( Box(modifier = Modifier.size(80.dp)) {
buttonModifier = Modifier.size(80.dp), androidx.compose.animation.AnimatedVisibility(
iconModifier = Modifier.size(40.dp), uiState.currentPlaylistItemIndex != 0,
icon = Icons.Filled.SkipPrevious, enter = fadeIn(animationSpec = tween(200)),
contentDescription = stringResource(R.string.widget_description_previous_stream), exit = fadeOut(animationSpec = tween(400))
onClick = viewModel::prevStream
) ) {
CenterControllButton(
buttonModifier = Modifier.fillMaxSize(),
iconModifier = Modifier.size(40.dp),
icon = Icons.Filled.SkipPrevious,
contentDescription = stringResource(R.string.widget_description_previous_stream),
onClick = viewModel::prevStream
)
}
}
CenterControllButton( CenterControllButton(
buttonModifier = Modifier.size(80.dp), buttonModifier = Modifier.size(80.dp),
@ -78,14 +94,21 @@ fun CenterUI(
), ),
onClick = if (uiState.playing) viewModel::pause else viewModel::play onClick = if (uiState.playing) viewModel::pause else viewModel::play
) )
Box(modifier = Modifier.size(80.dp)) {
CenterControllButton( androidx.compose.animation.AnimatedVisibility(
buttonModifier = Modifier.size(80.dp), uiState.currentPlaylistItemIndex < uiState.playList.size - 1,
iconModifier = Modifier.size(40.dp), enter = fadeIn(animationSpec = tween(200)),
icon = Icons.Filled.SkipNext, exit = fadeOut(animationSpec = tween(400))
contentDescription = stringResource(R.string.widget_description_next_stream), ) {
onClick = viewModel::nextStream CenterControllButton(
) buttonModifier = Modifier.fillMaxSize(),
iconModifier = Modifier.size(40.dp),
icon = Icons.Filled.SkipNext,
contentDescription = stringResource(R.string.widget_description_next_stream),
onClick = viewModel::nextStream
)
}
}
} }
} }
@ -124,7 +147,8 @@ fun VideoPlayerControllerUICenterUIPreview() {
viewModel = VideoPlayerViewModelDummy(), viewModel = VideoPlayerViewModelDummy(),
uiState = VideoPlayerUIState.DUMMY.copy( uiState = VideoPlayerUIState.DUMMY.copy(
isLoading = false, isLoading = false,
playing = true playing = true,
currentPlaylistItemIndex = 1
) )
) )
} }

View File

@ -172,25 +172,7 @@ fun VideoPlayerChannelSelectUIPreview() {
StreamSelectUI( StreamSelectUI(
isChapterSelect = true, isChapterSelect = true,
viewModel = VideoPlayerViewModelDummy(), viewModel = VideoPlayerViewModelDummy(),
uiState = VideoPlayerUIState.DEFAULT.copy( uiState = VideoPlayerUIState.DUMMY
chapters = arrayListOf(
Chapter(
chapterStartInMs = 5000,
chapterTitle = "First Chapter",
thumbnail = null
),
Chapter(
chapterStartInMs = 10000,
chapterTitle = "Second Chapter",
thumbnail = null
),
Chapter(
chapterStartInMs = 20000,
chapterTitle = "Third Chapter",
thumbnail = null
),
)
)
) )
} }
} }
@ -205,32 +187,6 @@ fun VideoPlayerStreamSelectUIPreview() {
isChapterSelect = false, isChapterSelect = false,
viewModel = VideoPlayerViewModelDummy(), viewModel = VideoPlayerViewModelDummy(),
uiState = VideoPlayerUIState.DUMMY.copy( uiState = VideoPlayerUIState.DUMMY.copy(
playList = arrayListOf(
PlaylistItem(
id = "6502",
title = "Stream 1",
creator = "The Creator",
lengthInS = 6 * 60 + 5,
thumbnail = null,
uniqueId = 0
),
PlaylistItem(
id = "6502",
title = "Stream 2",
creator = "The Creator 2",
lengthInS = 2 * 60 + 5,
thumbnail = null,
uniqueId = 1
),
PlaylistItem(
id = "6502",
title = "Stream 3",
creator = "The Creator 3",
lengthInS = 29 * 60 + 5,
thumbnail = null,
uniqueId = 2
)
),
currentlyPlaying = PlaylistItem.DUMMY.copy(uniqueId = 1) currentlyPlaying = PlaylistItem.DUMMY.copy(uniqueId = 1)
) )
) )