refactor: simplify playing queue listener handling

This commit is contained in:
Bnyro 2024-11-19 13:59:35 +01:00
parent 69f4c471c8
commit 4441a2bf4e
9 changed files with 66 additions and 60 deletions

View File

@ -0,0 +1,16 @@
package com.github.libretube.extensions
import androidx.annotation.OptIn
import androidx.core.os.bundleOf
import androidx.media3.common.util.UnstableApi
import androidx.media3.session.MediaController
import com.github.libretube.enums.PlayerCommand
import com.github.libretube.services.AbstractPlayerService
@OptIn(UnstableApi::class)
fun MediaController.navigateVideo(videoId: String) {
sendCustomCommand(
AbstractPlayerService.runPlayerActionCommand,
bundleOf(PlayerCommand.PLAY_VIDEO_BY_ID.name to videoId)
)
}

View File

@ -158,14 +158,18 @@ abstract class AbstractPlayerService : MediaLibraryService(), MediaLibrarySessio
} }
args.containsKey(PlayerCommand.PLAY_VIDEO_BY_ID.name) -> { args.containsKey(PlayerCommand.PLAY_VIDEO_BY_ID.name) -> {
videoId = args.getString(PlayerCommand.PLAY_VIDEO_BY_ID.name) ?: return navigateVideo(args.getString(PlayerCommand.PLAY_VIDEO_BY_ID.name) ?: return)
}
}
}
private fun navigateVideo(videoId: String) {
this.videoId = videoId
CoroutineScope(Dispatchers.IO).launch { CoroutineScope(Dispatchers.IO).launch {
startPlayback() startPlayback()
} }
} }
}
}
fun getSubtitleRoleFlags(subtitle: Subtitle?): Int { fun getSubtitleRoleFlags(subtitle: Subtitle?): Int {
return if (subtitle?.autoGenerated != true) { return if (subtitle?.autoGenerated != true) {
@ -180,11 +184,11 @@ abstract class AbstractPlayerService : MediaLibraryService(), MediaLibrarySessio
when (event) { when (event) {
PlayerEvent.Next -> { PlayerEvent.Next -> {
PlayingQueue.navigateNext() navigateVideo(PlayingQueue.getNext() ?: return)
} }
PlayerEvent.Prev -> { PlayerEvent.Prev -> {
PlayingQueue.navigatePrev() navigateVideo(PlayingQueue.getPrev() ?: return)
} }
PlayerEvent.Stop -> { PlayerEvent.Stop -> {

View File

@ -14,7 +14,6 @@ import com.github.libretube.enums.FileType
import com.github.libretube.extensions.serializable import com.github.libretube.extensions.serializable
import com.github.libretube.extensions.setMetadata import com.github.libretube.extensions.setMetadata
import com.github.libretube.extensions.toAndroidUri import com.github.libretube.extensions.toAndroidUri
import com.github.libretube.extensions.toID
import com.github.libretube.helpers.PlayerHelper import com.github.libretube.helpers.PlayerHelper
import com.github.libretube.ui.activities.MainActivity import com.github.libretube.ui.activities.MainActivity
import com.github.libretube.ui.activities.NoInternetActivity import com.github.libretube.ui.activities.NoInternetActivity
@ -67,10 +66,6 @@ open class OfflinePlayerService : AbstractPlayerService() {
PlayingQueue.clear() PlayingQueue.clear()
PlayingQueue.setOnQueueTapListener { streamItem ->
streamItem.url?.toID()?.let { playNextVideo(it) }
}
exoPlayer?.addListener(playerListener) exoPlayer?.addListener(playerListener)
fillQueue() fillQueue()

View File

@ -24,7 +24,6 @@ import com.github.libretube.db.DatabaseHelper
import com.github.libretube.enums.PlayerCommand import com.github.libretube.enums.PlayerCommand
import com.github.libretube.extensions.parcelable import com.github.libretube.extensions.parcelable
import com.github.libretube.extensions.setMetadata import com.github.libretube.extensions.setMetadata
import com.github.libretube.extensions.toID
import com.github.libretube.extensions.toastFromMainDispatcher import com.github.libretube.extensions.toastFromMainDispatcher
import com.github.libretube.extensions.toastFromMainThread import com.github.libretube.extensions.toastFromMainThread
import com.github.libretube.helpers.PlayerHelper import com.github.libretube.helpers.PlayerHelper
@ -113,10 +112,6 @@ open class OnlinePlayerService : AbstractPlayerService() {
if (!playerData.keepQueue) PlayingQueue.clear() if (!playerData.keepQueue) PlayingQueue.clear()
PlayingQueue.setOnQueueTapListener { streamItem ->
streamItem.url?.toID()?.let { playNextVideo(it) }
}
exoPlayer?.addListener(playerListener) exoPlayer?.addListener(playerListener)
} }

View File

@ -13,7 +13,9 @@ import com.github.libretube.helpers.ThemeHelper
import com.github.libretube.ui.viewholders.PlayingQueueViewHolder import com.github.libretube.ui.viewholders.PlayingQueueViewHolder
import com.github.libretube.util.PlayingQueue import com.github.libretube.util.PlayingQueue
class PlayingQueueAdapter : RecyclerView.Adapter<PlayingQueueViewHolder>() { class PlayingQueueAdapter(
private val onQueueItemSelected: (String) -> Unit
) : RecyclerView.Adapter<PlayingQueueViewHolder>() {
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): PlayingQueueViewHolder { override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): PlayingQueueViewHolder {
val binding = QueueRowBinding.inflate( val binding = QueueRowBinding.inflate(
@ -45,14 +47,17 @@ class PlayingQueueAdapter : RecyclerView.Adapter<PlayingQueueViewHolder>() {
) )
root.setOnClickListener { root.setOnClickListener {
val newVideoId = streamItem.url?.toID() ?: return@setOnClickListener
val oldPosition = PlayingQueue.currentIndex() val oldPosition = PlayingQueue.currentIndex()
// get the new position from the queue to work properly after reordering the queue // get the new position from the queue to work properly after reordering the queue
val newPosition = PlayingQueue.getStreams().indexOfFirst { val newPosition = PlayingQueue.getStreams().indexOfFirst {
it.url?.toID() == streamItem.url?.toID() it.url?.toID() == newVideoId
}.takeIf { it >= 0 } ?: return@setOnClickListener }.takeIf { it >= 0 } ?: return@setOnClickListener
PlayingQueue.updateCurrent(streamItem)
// select the new item in the queue and update the selected item in the UI // select the new item in the queue and update the selected item in the UI
PlayingQueue.onQueueItemSelected(newPosition) onQueueItemSelected(newVideoId)
notifyItemChanged(oldPosition) notifyItemChanged(oldPosition)
notifyItemChanged(newPosition) notifyItemChanged(newPosition)
} }

View File

@ -27,6 +27,7 @@ import com.github.libretube.R
import com.github.libretube.api.obj.ChapterSegment import com.github.libretube.api.obj.ChapterSegment
import com.github.libretube.constants.IntentData import com.github.libretube.constants.IntentData
import com.github.libretube.databinding.FragmentAudioPlayerBinding import com.github.libretube.databinding.FragmentAudioPlayerBinding
import com.github.libretube.extensions.navigateVideo
import com.github.libretube.extensions.normalize import com.github.libretube.extensions.normalize
import com.github.libretube.extensions.parcelableList import com.github.libretube.extensions.parcelableList
import com.github.libretube.extensions.seekBy import com.github.libretube.extensions.seekBy
@ -143,11 +144,11 @@ class AudioPlayerFragment : Fragment(), AudioPlayerOptions {
} }
binding.prev.setOnClickListener { binding.prev.setOnClickListener {
PlayingQueue.navigatePrev() playerController?.navigateVideo(PlayingQueue.getPrev() ?: return@setOnClickListener)
} }
binding.next.setOnClickListener { binding.next.setOnClickListener {
PlayingQueue.navigateNext() playerController?.navigateVideo(PlayingQueue.getNext() ?: return@setOnClickListener)
} }
listOf(binding.forwardTV, binding.rewindTV).forEach { listOf(binding.forwardTV, binding.rewindTV).forEach {
@ -160,6 +161,9 @@ class AudioPlayerFragment : Fragment(), AudioPlayerOptions {
playerController?.seekBy(PlayerHelper.seekIncrement) playerController?.seekBy(PlayerHelper.seekIncrement)
} }
childFragmentManager.setFragmentResultListener(PlayingQueueSheet.PLAYING_QUEUE_REQUEST_KEY, viewLifecycleOwner) { _, args ->
playerController?.navigateVideo(args.getString(IntentData.videoId) ?: return@setFragmentResultListener)
}
binding.openQueue.setOnClickListener { binding.openQueue.setOnClickListener {
PlayingQueueSheet().show(childFragmentManager) PlayingQueueSheet().show(childFragmentManager)
} }

View File

@ -5,9 +5,12 @@ import android.os.Bundle
import android.view.LayoutInflater import android.view.LayoutInflater
import android.view.View import android.view.View
import android.view.ViewGroup import android.view.ViewGroup
import androidx.core.os.bundleOf
import androidx.fragment.app.setFragmentResult
import androidx.media3.common.Player import androidx.media3.common.Player
import androidx.recyclerview.widget.LinearLayoutManager import androidx.recyclerview.widget.LinearLayoutManager
import com.github.libretube.R import com.github.libretube.R
import com.github.libretube.constants.IntentData
import com.github.libretube.databinding.QueueBottomSheetBinding import com.github.libretube.databinding.QueueBottomSheetBinding
import com.github.libretube.db.DatabaseHelper import com.github.libretube.db.DatabaseHelper
import com.github.libretube.db.DatabaseHolder import com.github.libretube.db.DatabaseHolder
@ -41,7 +44,9 @@ class PlayingQueueSheet : ExpandedBottomSheet() {
super.onViewCreated(view, savedInstanceState) super.onViewCreated(view, savedInstanceState)
binding.optionsRecycler.layoutManager = LinearLayoutManager(context) binding.optionsRecycler.layoutManager = LinearLayoutManager(context)
val adapter = PlayingQueueAdapter() val adapter = PlayingQueueAdapter { videoId ->
setFragmentResult(PLAYING_QUEUE_REQUEST_KEY, bundleOf(IntentData.videoId to videoId))
}
binding.optionsRecycler.adapter = adapter binding.optionsRecycler.adapter = adapter
// scroll to the currently playing video in the queue // scroll to the currently playing video in the queue
@ -201,4 +206,8 @@ class PlayingQueueSheet : ExpandedBottomSheet() {
} }
.show() .show()
} }
companion object {
const val PLAYING_QUEUE_REQUEST_KEY = "playing_queue_request_key"
}
} }

View File

@ -44,6 +44,7 @@ import com.github.libretube.databinding.DoubleTapOverlayBinding
import com.github.libretube.databinding.ExoStyledPlayerControlViewBinding import com.github.libretube.databinding.ExoStyledPlayerControlViewBinding
import com.github.libretube.databinding.PlayerGestureControlsViewBinding import com.github.libretube.databinding.PlayerGestureControlsViewBinding
import com.github.libretube.extensions.dpToPx import com.github.libretube.extensions.dpToPx
import com.github.libretube.extensions.navigateVideo
import com.github.libretube.extensions.normalize import com.github.libretube.extensions.normalize
import com.github.libretube.extensions.round import com.github.libretube.extensions.round
import com.github.libretube.extensions.seekBy import com.github.libretube.extensions.seekBy
@ -257,6 +258,14 @@ abstract class CustomExoPlayerView(
} }
} }
supportFragmentManager.setFragmentResultListener(
PlayingQueueSheet.PLAYING_QUEUE_REQUEST_KEY,
findViewTreeLifecycleOwner()!!
) { _, args ->
(player as? MediaController)?.navigateVideo(
args.getString(IntentData.videoId) ?: return@setFragmentResultListener
)
}
binding.queueToggle.setOnClickListener { binding.queueToggle.setOnClickListener {
PlayingQueueSheet().show(supportFragmentManager, null) PlayingQueueSheet().show(supportFragmentManager, null)
} }
@ -675,7 +684,8 @@ abstract class CustomExoPlayerView(
// add a margin to the top and the bottom bar in landscape mode for notches // add a margin to the top and the bottom bar in landscape mode for notches
val isForcedLandscape = val isForcedLandscape =
activity.requestedOrientation == ActivityInfo.SCREEN_ORIENTATION_SENSOR_LANDSCAPE activity.requestedOrientation == ActivityInfo.SCREEN_ORIENTATION_SENSOR_LANDSCAPE
val isInLandscape = resources.configuration.orientation == Configuration.ORIENTATION_LANDSCAPE val isInLandscape =
resources.configuration.orientation == Configuration.ORIENTATION_LANDSCAPE
val horizontalMargin = val horizontalMargin =
if (isFullscreen() && (isInLandscape || isForcedLandscape)) LANDSCAPE_MARGIN_HORIZONTAL else LANDSCAPE_MARGIN_HORIZONTAL_NONE if (isFullscreen() && (isInLandscape || isForcedLandscape)) LANDSCAPE_MARGIN_HORIZONTAL else LANDSCAPE_MARGIN_HORIZONTAL_NONE
@ -843,15 +853,16 @@ abstract class CustomExoPlayerView(
} }
KeyEvent.KEYCODE_N, KeyEvent.KEYCODE_NAVIGATE_NEXT -> { KeyEvent.KEYCODE_N, KeyEvent.KEYCODE_NAVIGATE_NEXT -> {
PlayingQueue.navigateNext() PlayingQueue.getNext()?.let { (player as? MediaController)?.navigateVideo(it) }
} }
KeyEvent.KEYCODE_P, KeyEvent.KEYCODE_NAVIGATE_PREVIOUS -> { KeyEvent.KEYCODE_P, KeyEvent.KEYCODE_NAVIGATE_PREVIOUS -> {
PlayingQueue.navigatePrev() PlayingQueue.getPrev()?.let { (player as? MediaController)?.navigateVideo(it) }
} }
KeyEvent.KEYCODE_F -> { KeyEvent.KEYCODE_F -> {
val fragmentManager = ContextHelper.unwrapActivity<MainActivity>(context).supportFragmentManager val fragmentManager =
ContextHelper.unwrapActivity<MainActivity>(context).supportFragmentManager
fragmentManager.fragments.filterIsInstance<PlayerFragment>().firstOrNull() fragmentManager.fragments.filterIsInstance<PlayerFragment>().firstOrNull()
?.toggleFullscreen() ?.toggleFullscreen()
} }

View File

@ -1,6 +1,5 @@
package com.github.libretube.util package com.github.libretube.util
import android.util.Log
import androidx.media3.common.Player import androidx.media3.common.Player
import com.github.libretube.api.PlaylistsHelper import com.github.libretube.api.PlaylistsHelper
import com.github.libretube.api.RetrofitInstance import com.github.libretube.api.RetrofitInstance
@ -20,11 +19,6 @@ object PlayingQueue {
private val queueJobs = mutableListOf<Job>() private val queueJobs = mutableListOf<Job>()
/**
* Listener that gets called when the user selects an item from the queue
*/
private var onQueueTapListener: (StreamItem) -> Unit = {}
var repeatMode: Int = Player.REPEAT_MODE_OFF var repeatMode: Int = Player.REPEAT_MODE_OFF
fun clear() { fun clear() {
@ -223,34 +217,7 @@ object PlayingQueue {
add(*streams.filter { !it.isLive }.toTypedArray(), skipExisting = true) add(*streams.filter { !it.isLive }.toTypedArray(), skipExisting = true)
} }
fun onQueueItemSelected(index: Int) {
try {
val streamItem = queue[index]
updateCurrent(streamItem)
onQueueTapListener.invoke(streamItem)
} catch (e: Exception) {
Log.e("Queue on tap", "lifecycle already ended")
}
}
fun navigatePrev() {
if (!hasPrev()) return
onQueueItemSelected(currentIndex() - 1)
}
fun navigateNext() {
if (!hasNext()) return
onQueueItemSelected(currentIndex() + 1)
}
fun setOnQueueTapListener(listener: (StreamItem) -> Unit) {
onQueueTapListener = listener
}
fun resetToDefaults() { fun resetToDefaults() {
repeatMode = Player.REPEAT_MODE_OFF repeatMode = Player.REPEAT_MODE_OFF
onQueueTapListener = {}
} }
} }