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 {
return when (playerEvent) {
PlayerEvent.PlayPause -> {

View File

@ -14,7 +14,6 @@ import android.os.Bundle
import android.os.Handler
import android.os.Looper
import android.os.PowerManager
import android.text.format.DateUtils
import android.view.KeyEvent
import android.view.LayoutInflater
import android.view.View
@ -32,7 +31,6 @@ import androidx.core.os.postDelayed
import androidx.core.view.SoftwareKeyboardControllerCompat
import androidx.core.view.WindowCompat
import androidx.core.view.isGone
import androidx.core.view.isInvisible
import androidx.core.view.isVisible
import androidx.fragment.app.Fragment
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.sheets.BaseBottomSheet
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.util.NowPlayingNotification
import com.github.libretube.util.OnlineTimeFrameReceiver
@ -160,9 +157,6 @@ class PlayerFragment : Fragment(), OnlinePlayerOptions {
Executors.newCachedThreadPool()
)
// SponsorBlock
private var sponsorBlockEnabled = PlayerHelper.sponsorBlockEnabled
private val handler = Handler(Looper.getMainLooper())
private var seekBarPreviewListener: SeekbarPreviewListener? = null
@ -271,7 +265,6 @@ class PlayerFragment : Fragment(), OnlinePlayerOptions {
}
override fun onEvents(player: Player, events: Player.Events) {
updateDisplayedDuration()
super.onEvents(player, events)
if (events.containsAny(
@ -522,7 +515,6 @@ class PlayerFragment : Fragment(), OnlinePlayerOptions {
playerBinding.closeImageButton.setOnClickListener {
onManualPlayerClose()
}
playerBinding.autoPlay.isVisible = true
binding.playImageView.setOnClickListener {
exoPlayer.togglePlayPauseState()
@ -540,28 +532,12 @@ class PlayerFragment : Fragment(), OnlinePlayerOptions {
CommentsSheet().show(childFragmentManager)
}
playerBinding.queueToggle.isVisible = true
playerBinding.queueToggle.setOnClickListener {
PlayingQueueSheet().show(childFragmentManager, null)
}
// FullScreen button trigger
// hide fullscreen button if autorotation enabled
playerBinding.fullscreen.setOnClickListener {
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
binding.relPlayerShare.setOnClickListener {
if (!this::streams.isInitialized) return@setOnClickListener
@ -695,16 +671,12 @@ class PlayerFragment : Fragment(), OnlinePlayerOptions {
updateFullscreenOrientation()
commentsViewModel.setCommentSheetExpand(null)
playerBinding.fullscreen.setImageResource(R.drawable.ic_fullscreen_exit)
playerBinding.exoTitle.isVisible = true
updateResolutionOnFullscreenChange(true)
openOrCloseFullscreenDialog(true)
binding.player.updateMarginsByFullscreenMode()
updateFullscreenButtonVisibility()
}
@SuppressLint("SourceLockedOrientationActivity")
@ -718,8 +690,6 @@ class PlayerFragment : Fragment(), OnlinePlayerOptions {
}
viewModel.isFullscreen.value = false
playerBinding.fullscreen.setImageResource(R.drawable.ic_fullscreen)
playerBinding.exoTitle.isInvisible = true
if (!PlayerHelper.autoFullscreenEnabled) {
mainActivity.requestedOrientation = mainActivity.screenOrientationPref
@ -729,12 +699,6 @@ class PlayerFragment : Fragment(), OnlinePlayerOptions {
updateResolutionOnFullscreenChange(false)
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
handler.postDelayed(this::checkForSegments, 100)
if (!sponsorBlockEnabled || viewModel.segments.isEmpty()) return
if (!viewModel.sponsorBlockEnabled || viewModel.segments.isEmpty()) return
exoPlayer.checkForSegments(
requireContext(),
@ -949,7 +913,6 @@ class PlayerFragment : Fragment(), OnlinePlayerOptions {
) {
setFullscreen()
}
updateFullscreenButtonVisibility()
binding.player.apply {
useController = false
@ -1000,7 +963,6 @@ class PlayerFragment : Fragment(), OnlinePlayerOptions {
withContext(Dispatchers.Main) {
playerBinding.exoProgress.setSegments(viewModel.segments)
playerBinding.sbToggle.isVisible = true
updateDisplayedDuration()
}
viewModel.segments.firstOrNull { it.category == PlayerHelper.SPONSOR_HIGHLIGHT_CATEGORY }
?.let {
@ -1082,8 +1044,6 @@ class PlayerFragment : Fragment(), OnlinePlayerOptions {
this.streams.uploader
)
syncQueueButtons()
// seekbar preview setup
playerBinding.seekbarPreview.isGone = true
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() {
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.Streams
import com.github.libretube.api.obj.Subtitle
import com.github.libretube.constants.PreferenceKeys
import com.github.libretube.helpers.PlayerHelper
import com.github.libretube.helpers.PreferenceHelper
import com.github.libretube.util.NowPlayingNotification
import com.github.libretube.util.deArrow
import java.io.IOException
@ -51,6 +53,7 @@ class PlayerViewModel : ViewModel() {
val chaptersLiveData = MutableLiveData<List<ChapterSegment>>()
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

View File

@ -170,20 +170,7 @@ abstract class CustomExoPlayerView(
player?.addListener(object : Player.Listener {
override fun onEvents(player: Player, events: Player.Events) {
super.onEvents(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)
}
this@CustomExoPlayerView.onPlaybackEvents(player, events)
}
})
@ -820,6 +807,23 @@ abstract class CustomExoPlayerView(
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
abstract fun getChapters(): List<ChapterSegment>

View File

@ -2,12 +2,15 @@ package com.github.libretube.ui.views
import android.content.Context
import android.os.Bundle
import android.text.format.DateUtils
import android.util.AttributeSet
import android.view.Window
import androidx.core.os.bundleOf
import androidx.core.view.isInvisible
import androidx.core.view.isVisible
import androidx.lifecycle.LifecycleOwner
import androidx.media3.common.C
import androidx.media3.common.Player
import androidx.media3.common.util.UnstableApi
import androidx.media3.exoplayer.trackselection.TrackSelector
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.interfaces.OnlinePlayerOptions
import com.github.libretube.ui.models.PlayerViewModel
import com.github.libretube.ui.sheets.PlayingQueueSheet
import com.github.libretube.util.PlayingQueue
@UnstableApi
@ -154,14 +158,39 @@ class OnlinePlayerView(
playerViewModel.isFullscreen.observe(viewLifecycleOwner) { isFullscreen ->
WindowHelper.toggleFullscreen(activity.window, isFullscreen)
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.setOnCheckedChangeListener { _, 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.setOnClickListener {
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 hideController() {
@ -219,4 +280,9 @@ class OnlinePlayerView(
override fun getChapters(): List<ChapterSegment> {
return playerViewModel?.chapters.orEmpty()
}
override fun onPlaybackEvents(player: Player, events: Player.Events) {
super.onPlaybackEvents(player, events)
updateDisplayedDuration()
}
}