refactor: move some player view related code to OnlinePlayerView.kt

This commit is contained in:
Bnyro 2024-05-07 18:12:42 +02:00
parent 9f69770b39
commit df087054d9
5 changed files with 91 additions and 88 deletions

View File

@ -838,6 +838,9 @@ object PlayerHelper {
} }
} }
/**
* Handle basic [PlayerEvent]'s that can be handled by the player itself without context
*/
fun handlePlayerAction(player: Player, playerEvent: PlayerEvent): Boolean { fun handlePlayerAction(player: Player, playerEvent: PlayerEvent): Boolean {
return when (playerEvent) { return when (playerEvent) {
PlayerEvent.PlayPause -> { PlayerEvent.PlayPause -> {

View File

@ -14,7 +14,6 @@ import android.os.Bundle
import android.os.Handler import android.os.Handler
import android.os.Looper import android.os.Looper
import android.os.PowerManager import android.os.PowerManager
import android.text.format.DateUtils
import android.view.KeyEvent import android.view.KeyEvent
import android.view.LayoutInflater import android.view.LayoutInflater
import android.view.View import android.view.View
@ -32,7 +31,6 @@ import androidx.core.os.postDelayed
import androidx.core.view.SoftwareKeyboardControllerCompat import androidx.core.view.SoftwareKeyboardControllerCompat
import androidx.core.view.WindowCompat import androidx.core.view.WindowCompat
import androidx.core.view.isGone import androidx.core.view.isGone
import androidx.core.view.isInvisible
import androidx.core.view.isVisible import androidx.core.view.isVisible
import androidx.fragment.app.Fragment import androidx.fragment.app.Fragment
import androidx.fragment.app.activityViewModels import androidx.fragment.app.activityViewModels
@ -102,7 +100,6 @@ import com.github.libretube.ui.models.CommentsViewModel
import com.github.libretube.ui.models.PlayerViewModel import com.github.libretube.ui.models.PlayerViewModel
import com.github.libretube.ui.sheets.BaseBottomSheet import com.github.libretube.ui.sheets.BaseBottomSheet
import com.github.libretube.ui.sheets.CommentsSheet import com.github.libretube.ui.sheets.CommentsSheet
import com.github.libretube.ui.sheets.PlayingQueueSheet
import com.github.libretube.ui.sheets.StatsSheet import com.github.libretube.ui.sheets.StatsSheet
import com.github.libretube.util.NowPlayingNotification import com.github.libretube.util.NowPlayingNotification
import com.github.libretube.util.OnlineTimeFrameReceiver import com.github.libretube.util.OnlineTimeFrameReceiver
@ -160,9 +157,6 @@ class PlayerFragment : Fragment(), OnlinePlayerOptions {
Executors.newCachedThreadPool() Executors.newCachedThreadPool()
) )
// SponsorBlock
private var sponsorBlockEnabled = PlayerHelper.sponsorBlockEnabled
private val handler = Handler(Looper.getMainLooper()) private val handler = Handler(Looper.getMainLooper())
private var seekBarPreviewListener: SeekbarPreviewListener? = null private var seekBarPreviewListener: SeekbarPreviewListener? = null
@ -271,7 +265,6 @@ class PlayerFragment : Fragment(), OnlinePlayerOptions {
} }
override fun onEvents(player: Player, events: Player.Events) { override fun onEvents(player: Player, events: Player.Events) {
updateDisplayedDuration()
super.onEvents(player, events) super.onEvents(player, events)
if (events.containsAny( if (events.containsAny(
@ -522,7 +515,6 @@ class PlayerFragment : Fragment(), OnlinePlayerOptions {
playerBinding.closeImageButton.setOnClickListener { playerBinding.closeImageButton.setOnClickListener {
onManualPlayerClose() onManualPlayerClose()
} }
playerBinding.autoPlay.isVisible = true
binding.playImageView.setOnClickListener { binding.playImageView.setOnClickListener {
exoPlayer.togglePlayPauseState() exoPlayer.togglePlayPauseState()
@ -540,28 +532,12 @@ class PlayerFragment : Fragment(), OnlinePlayerOptions {
CommentsSheet().show(childFragmentManager) CommentsSheet().show(childFragmentManager)
} }
playerBinding.queueToggle.isVisible = true
playerBinding.queueToggle.setOnClickListener {
PlayingQueueSheet().show(childFragmentManager, null)
}
// FullScreen button trigger // FullScreen button trigger
// hide fullscreen button if autorotation enabled // hide fullscreen button if autorotation enabled
playerBinding.fullscreen.setOnClickListener { playerBinding.fullscreen.setOnClickListener {
toggleFullscreen() toggleFullscreen()
} }
val updateSbImageResource = {
playerBinding.sbToggle.setImageResource(
if (sponsorBlockEnabled) R.drawable.ic_sb_enabled else R.drawable.ic_sb_disabled
)
}
updateSbImageResource()
playerBinding.sbToggle.setOnClickListener {
sponsorBlockEnabled = !sponsorBlockEnabled
updateSbImageResource()
}
// share button // share button
binding.relPlayerShare.setOnClickListener { binding.relPlayerShare.setOnClickListener {
if (!this::streams.isInitialized) return@setOnClickListener if (!this::streams.isInitialized) return@setOnClickListener
@ -695,16 +671,12 @@ class PlayerFragment : Fragment(), OnlinePlayerOptions {
updateFullscreenOrientation() updateFullscreenOrientation()
commentsViewModel.setCommentSheetExpand(null) commentsViewModel.setCommentSheetExpand(null)
playerBinding.fullscreen.setImageResource(R.drawable.ic_fullscreen_exit)
playerBinding.exoTitle.isVisible = true
updateResolutionOnFullscreenChange(true) updateResolutionOnFullscreenChange(true)
openOrCloseFullscreenDialog(true) openOrCloseFullscreenDialog(true)
binding.player.updateMarginsByFullscreenMode() binding.player.updateMarginsByFullscreenMode()
updateFullscreenButtonVisibility()
} }
@SuppressLint("SourceLockedOrientationActivity") @SuppressLint("SourceLockedOrientationActivity")
@ -718,8 +690,6 @@ class PlayerFragment : Fragment(), OnlinePlayerOptions {
} }
viewModel.isFullscreen.value = false viewModel.isFullscreen.value = false
playerBinding.fullscreen.setImageResource(R.drawable.ic_fullscreen)
playerBinding.exoTitle.isInvisible = true
if (!PlayerHelper.autoFullscreenEnabled) { if (!PlayerHelper.autoFullscreenEnabled) {
mainActivity.requestedOrientation = mainActivity.screenOrientationPref mainActivity.requestedOrientation = mainActivity.screenOrientationPref
@ -729,12 +699,6 @@ class PlayerFragment : Fragment(), OnlinePlayerOptions {
updateResolutionOnFullscreenChange(false) updateResolutionOnFullscreenChange(false)
binding.player.updateMarginsByFullscreenMode() binding.player.updateMarginsByFullscreenMode()
updateFullscreenButtonVisibility()
}
private fun updateFullscreenButtonVisibility() {
playerBinding.fullscreen.isInvisible = PlayerHelper.autoFullscreenEnabled
} }
/** /**
@ -881,7 +845,7 @@ class PlayerFragment : Fragment(), OnlinePlayerOptions {
if (!exoPlayer.isPlaying || !PlayerHelper.sponsorBlockEnabled) return if (!exoPlayer.isPlaying || !PlayerHelper.sponsorBlockEnabled) return
handler.postDelayed(this::checkForSegments, 100) handler.postDelayed(this::checkForSegments, 100)
if (!sponsorBlockEnabled || viewModel.segments.isEmpty()) return if (!viewModel.sponsorBlockEnabled || viewModel.segments.isEmpty()) return
exoPlayer.checkForSegments( exoPlayer.checkForSegments(
requireContext(), requireContext(),
@ -949,7 +913,6 @@ class PlayerFragment : Fragment(), OnlinePlayerOptions {
) { ) {
setFullscreen() setFullscreen()
} }
updateFullscreenButtonVisibility()
binding.player.apply { binding.player.apply {
useController = false useController = false
@ -1000,7 +963,6 @@ class PlayerFragment : Fragment(), OnlinePlayerOptions {
withContext(Dispatchers.Main) { withContext(Dispatchers.Main) {
playerBinding.exoProgress.setSegments(viewModel.segments) playerBinding.exoProgress.setSegments(viewModel.segments)
playerBinding.sbToggle.isVisible = true playerBinding.sbToggle.isVisible = true
updateDisplayedDuration()
} }
viewModel.segments.firstOrNull { it.category == PlayerHelper.SPONSOR_HIGHLIGHT_CATEGORY } viewModel.segments.firstOrNull { it.category == PlayerHelper.SPONSOR_HIGHLIGHT_CATEGORY }
?.let { ?.let {
@ -1082,8 +1044,6 @@ class PlayerFragment : Fragment(), OnlinePlayerOptions {
this.streams.uploader this.streams.uploader
) )
syncQueueButtons()
// seekbar preview setup // seekbar preview setup
playerBinding.seekbarPreview.isGone = true playerBinding.seekbarPreview.isGone = true
seekBarPreviewListener?.let { playerBinding.exoProgress.removeListener(it) } seekBarPreviewListener?.let { playerBinding.exoProgress.removeListener(it) }
@ -1137,39 +1097,6 @@ class PlayerFragment : Fragment(), OnlinePlayerOptions {
} }
} }
/**
* Update the displayed duration of the video
*/
private fun updateDisplayedDuration() {
if (!this::streams.isInitialized || streams.livestream || _binding == null) return
val duration = exoPlayer.duration / 1000
if (duration < 0) return
val durationWithoutSegments = duration - viewModel.segments.sumOf {
val (start, end) = it.segmentStartAndEnd
end.toDouble() - start.toDouble()
}.toLong()
val durationString = DateUtils.formatElapsedTime(duration)
playerBinding.duration.text = if (durationWithoutSegments < duration) {
"$durationString (${DateUtils.formatElapsedTime(durationWithoutSegments)})"
} else {
durationString
}
}
private fun syncQueueButtons() {
if (!PlayerHelper.skipButtonsEnabled) return
// toggle the visibility of next and prev buttons based on queue and whether the player view is locked
val isPlayerLocked = binding.player.isPlayerLocked
playerBinding.skipPrev.isInvisible = !PlayingQueue.hasPrev() || isPlayerLocked
playerBinding.skipNext.isInvisible = !PlayingQueue.hasNext() || isPlayerLocked
handler.postDelayed(this::syncQueueButtons, 100)
}
private fun updatePlayPauseButton() { private fun updatePlayPauseButton() {
binding.playImageView.setImageResource(PlayerHelper.getPlayPauseActionIcon(exoPlayer)) binding.playImageView.setImageResource(PlayerHelper.getPlayPauseActionIcon(exoPlayer))
} }

View File

@ -15,7 +15,9 @@ import com.github.libretube.api.obj.Message
import com.github.libretube.api.obj.Segment import com.github.libretube.api.obj.Segment
import com.github.libretube.api.obj.Streams import com.github.libretube.api.obj.Streams
import com.github.libretube.api.obj.Subtitle import com.github.libretube.api.obj.Subtitle
import com.github.libretube.constants.PreferenceKeys
import com.github.libretube.helpers.PlayerHelper import com.github.libretube.helpers.PlayerHelper
import com.github.libretube.helpers.PreferenceHelper
import com.github.libretube.util.NowPlayingNotification import com.github.libretube.util.NowPlayingNotification
import com.github.libretube.util.deArrow import com.github.libretube.util.deArrow
import java.io.IOException import java.io.IOException
@ -51,6 +53,7 @@ class PlayerViewModel : ViewModel() {
val chaptersLiveData = MutableLiveData<List<ChapterSegment>>() val chaptersLiveData = MutableLiveData<List<ChapterSegment>>()
val chapters get() = chaptersLiveData.value.orEmpty() val chapters get() = chaptersLiveData.value.orEmpty()
var sponsorBlockEnabled = PlayerHelper.sponsorBlockEnabled
/** /**
* @return pair of the stream info and the error message if the request was not successful * @return pair of the stream info and the error message if the request was not successful

View File

@ -170,20 +170,7 @@ abstract class CustomExoPlayerView(
player?.addListener(object : Player.Listener { player?.addListener(object : Player.Listener {
override fun onEvents(player: Player, events: Player.Events) { override fun onEvents(player: Player, events: Player.Events) {
super.onEvents(player, events) super.onEvents(player, events)
if (events.containsAny( this@CustomExoPlayerView.onPlaybackEvents(player, events)
Player.EVENT_PLAYBACK_STATE_CHANGED,
Player.EVENT_IS_PLAYING_CHANGED,
Player.EVENT_PLAY_WHEN_READY_CHANGED
)
) {
binding.playPauseBTN.setImageResource(
PlayerHelper.getPlayPauseActionIcon(player)
)
// keep screen on if the video is playing
keepScreenOn = player.isPlaying == true
onPlayerEvent(player, events)
}
} }
}) })
@ -820,6 +807,23 @@ abstract class CustomExoPlayerView(
return true return true
} }
open fun onPlaybackEvents(player: Player, events: Player.Events) {
if (events.containsAny(
Player.EVENT_PLAYBACK_STATE_CHANGED,
Player.EVENT_IS_PLAYING_CHANGED,
Player.EVENT_PLAY_WHEN_READY_CHANGED
)
) {
binding.playPauseBTN.setImageResource(
PlayerHelper.getPlayPauseActionIcon(player)
)
// keep screen on if the video is playing
keepScreenOn = player.isPlaying == true
onPlayerEvent(player, events)
}
}
open fun minimizeOrExitPlayer() = Unit open fun minimizeOrExitPlayer() = Unit
abstract fun getChapters(): List<ChapterSegment> abstract fun getChapters(): List<ChapterSegment>

View File

@ -2,12 +2,15 @@ package com.github.libretube.ui.views
import android.content.Context import android.content.Context
import android.os.Bundle import android.os.Bundle
import android.text.format.DateUtils
import android.util.AttributeSet import android.util.AttributeSet
import android.view.Window import android.view.Window
import androidx.core.os.bundleOf import androidx.core.os.bundleOf
import androidx.core.view.isInvisible
import androidx.core.view.isVisible import androidx.core.view.isVisible
import androidx.lifecycle.LifecycleOwner import androidx.lifecycle.LifecycleOwner
import androidx.media3.common.C import androidx.media3.common.C
import androidx.media3.common.Player
import androidx.media3.common.util.UnstableApi import androidx.media3.common.util.UnstableApi
import androidx.media3.exoplayer.trackselection.TrackSelector import androidx.media3.exoplayer.trackselection.TrackSelector
import com.github.libretube.R import com.github.libretube.R
@ -24,6 +27,7 @@ import com.github.libretube.ui.dialogs.SubmitDeArrowDialog
import com.github.libretube.ui.dialogs.SubmitSegmentDialog import com.github.libretube.ui.dialogs.SubmitSegmentDialog
import com.github.libretube.ui.interfaces.OnlinePlayerOptions import com.github.libretube.ui.interfaces.OnlinePlayerOptions
import com.github.libretube.ui.models.PlayerViewModel import com.github.libretube.ui.models.PlayerViewModel
import com.github.libretube.ui.sheets.PlayingQueueSheet
import com.github.libretube.util.PlayingQueue import com.github.libretube.util.PlayingQueue
@UnstableApi @UnstableApi
@ -154,14 +158,39 @@ class OnlinePlayerView(
playerViewModel.isFullscreen.observe(viewLifecycleOwner) { isFullscreen -> playerViewModel.isFullscreen.observe(viewLifecycleOwner) { isFullscreen ->
WindowHelper.toggleFullscreen(activity.window, isFullscreen) WindowHelper.toggleFullscreen(activity.window, isFullscreen)
updateTopBarMargin() updateTopBarMargin()
binding.fullscreen.isInvisible = PlayerHelper.autoFullscreenEnabled
val fullscreenDrawable = if (isFullscreen) R.drawable.ic_fullscreen_exit else R.drawable.ic_fullscreen
binding.fullscreen.setImageResource(fullscreenDrawable)
binding.exoTitle.isInvisible = !isFullscreen
} }
binding.autoPlay.isVisible = true
binding.autoPlay.isChecked = PlayerHelper.autoPlayEnabled binding.autoPlay.isChecked = PlayerHelper.autoPlayEnabled
binding.autoPlay.setOnCheckedChangeListener { _, isChecked -> binding.autoPlay.setOnCheckedChangeListener { _, isChecked ->
PlayerHelper.autoPlayEnabled = isChecked PlayerHelper.autoPlayEnabled = isChecked
} }
binding.queueToggle.isVisible = true
binding.queueToggle.setOnClickListener {
PlayingQueueSheet().show(activity.supportFragmentManager, null)
}
val updateSbImageResource = {
binding.sbToggle.setImageResource(
if (playerViewModel.sponsorBlockEnabled) R.drawable.ic_sb_enabled else R.drawable.ic_sb_disabled
)
}
updateSbImageResource()
binding.sbToggle.setOnClickListener {
playerViewModel.sponsorBlockEnabled = !playerViewModel.sponsorBlockEnabled
updateSbImageResource()
}
syncQueueButtons()
binding.sbSubmit.isVisible = PreferenceHelper.getBoolean(PreferenceKeys.CONTRIBUTE_TO_SB, false) binding.sbSubmit.isVisible = PreferenceHelper.getBoolean(PreferenceKeys.CONTRIBUTE_TO_SB, false)
binding.sbSubmit.setOnClickListener { binding.sbSubmit.setOnClickListener {
val submitSegmentDialog = SubmitSegmentDialog() val submitSegmentDialog = SubmitSegmentDialog()
@ -189,6 +218,38 @@ class OnlinePlayerView(
) )
} }
private fun syncQueueButtons() {
if (!PlayerHelper.skipButtonsEnabled) return
// toggle the visibility of next and prev buttons based on queue and whether the player view is locked
binding.skipPrev.isInvisible = !PlayingQueue.hasPrev() || isPlayerLocked
binding.skipNext.isInvisible = !PlayingQueue.hasNext() || isPlayerLocked
handler.postDelayed(this::syncQueueButtons, 100)
}
/**
* Update the displayed duration of the video
*/
private fun updateDisplayedDuration() {
if (isLive) return
val duration = player?.duration?.div(1000) ?: return
if (duration < 0) return
val durationWithoutSegments = duration - playerViewModel?.segments.orEmpty().sumOf {
val (start, end) = it.segmentStartAndEnd
end.toDouble() - start.toDouble()
}.toLong()
val durationString = DateUtils.formatElapsedTime(duration)
binding.duration.text = if (durationWithoutSegments < duration) {
"$durationString (${DateUtils.formatElapsedTime(durationWithoutSegments)})"
} else {
durationString
}
}
override fun getWindow(): Window = currentWindow ?: activity.window override fun getWindow(): Window = currentWindow ?: activity.window
override fun hideController() { override fun hideController() {
@ -219,4 +280,9 @@ class OnlinePlayerView(
override fun getChapters(): List<ChapterSegment> { override fun getChapters(): List<ChapterSegment> {
return playerViewModel?.chapters.orEmpty() return playerViewModel?.chapters.orEmpty()
} }
override fun onPlaybackEvents(player: Player, events: Player.Events) {
super.onPlaybackEvents(player, events)
updateDisplayedDuration()
}
} }