make goto next or prev streem buttons work
This commit is contained in:
parent
106060625c
commit
58e0ad9f09
|
@ -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()
|
||||||
|
|
|
@ -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
|
||||||
|
)
|
||||||
|
)
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -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() {
|
||||||
|
|
|
@ -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,
|
||||||
) {
|
) {
|
||||||
|
|
||||||
|
Box(modifier = Modifier.size(80.dp)) {
|
||||||
|
androidx.compose.animation.AnimatedVisibility(
|
||||||
|
uiState.currentPlaylistItemIndex != 0,
|
||||||
|
enter = fadeIn(animationSpec = tween(200)),
|
||||||
|
exit = fadeOut(animationSpec = tween(400))
|
||||||
|
|
||||||
|
) {
|
||||||
CenterControllButton(
|
CenterControllButton(
|
||||||
buttonModifier = Modifier.size(80.dp),
|
buttonModifier = Modifier.fillMaxSize(),
|
||||||
iconModifier = Modifier.size(40.dp),
|
iconModifier = Modifier.size(40.dp),
|
||||||
icon = Icons.Filled.SkipPrevious,
|
icon = Icons.Filled.SkipPrevious,
|
||||||
contentDescription = stringResource(R.string.widget_description_previous_stream),
|
contentDescription = stringResource(R.string.widget_description_previous_stream),
|
||||||
onClick = viewModel::prevStream
|
onClick = viewModel::prevStream
|
||||||
)
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
CenterControllButton(
|
CenterControllButton(
|
||||||
buttonModifier = Modifier.size(80.dp),
|
buttonModifier = Modifier.size(80.dp),
|
||||||
|
@ -78,15 +94,22 @@ 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)) {
|
||||||
|
androidx.compose.animation.AnimatedVisibility(
|
||||||
|
uiState.currentPlaylistItemIndex < uiState.playList.size - 1,
|
||||||
|
enter = fadeIn(animationSpec = tween(200)),
|
||||||
|
exit = fadeOut(animationSpec = tween(400))
|
||||||
|
) {
|
||||||
CenterControllButton(
|
CenterControllButton(
|
||||||
buttonModifier = Modifier.size(80.dp),
|
buttonModifier = Modifier.fillMaxSize(),
|
||||||
iconModifier = Modifier.size(40.dp),
|
iconModifier = Modifier.size(40.dp),
|
||||||
icon = Icons.Filled.SkipNext,
|
icon = Icons.Filled.SkipNext,
|
||||||
contentDescription = stringResource(R.string.widget_description_next_stream),
|
contentDescription = stringResource(R.string.widget_description_next_stream),
|
||||||
onClick = viewModel::nextStream
|
onClick = viewModel::nextStream
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
|
@ -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
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
|
@ -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)
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
|
|
Loading…
Reference in New Issue