feat: show repeat button in audio player when playback finished

This commit is contained in:
Bnyro 2024-01-08 19:17:09 +01:00
parent 73c98368a7
commit 46acd28c26
5 changed files with 43 additions and 55 deletions

View File

@ -41,11 +41,11 @@ import com.github.libretube.enums.SbSkipOptions
import com.github.libretube.extensions.updateParameters import com.github.libretube.extensions.updateParameters
import com.github.libretube.obj.VideoStats import com.github.libretube.obj.VideoStats
import com.github.libretube.util.TextUtils import com.github.libretube.util.TextUtils
import kotlinx.coroutines.runBlocking
import java.util.Locale import java.util.Locale
import java.util.concurrent.Executors import java.util.concurrent.Executors
import kotlin.math.absoluteValue import kotlin.math.absoluteValue
import kotlin.math.roundToInt import kotlin.math.roundToInt
import kotlinx.coroutines.runBlocking
object PlayerHelper { object PlayerHelper {
private const val ACTION_MEDIA_CONTROL = "media_control" private const val ACTION_MEDIA_CONTROL = "media_control"
@ -598,7 +598,8 @@ object PlayerHelper {
val chapter = chapters[index] val chapter = chapters[index]
// remove the video highlight if it's already longer ago than [ChapterSegment.HIGHLIGHT_LENGTH], // remove the video highlight if it's already longer ago than [ChapterSegment.HIGHLIGHT_LENGTH],
// otherwise the SponsorBlock highlight would be shown from its starting point to the end // otherwise the SponsorBlock highlight would be shown from its starting point to the end
val isWithinMaxHighlightDuration = (currentPositionSeconds - chapter.start) < ChapterSegment.HIGHLIGHT_LENGTH val isWithinMaxHighlightDuration =
(currentPositionSeconds - chapter.start) < ChapterSegment.HIGHLIGHT_LENGTH
chapter.highlightDrawable == null || isWithinMaxHighlightDuration chapter.highlightDrawable == null || isWithinMaxHighlightDuration
} }
} }
@ -771,4 +772,10 @@ object PlayerHelper {
"${player.videoFormat?.width}x${player.videoFormat?.height} ${player.videoFormat?.frameRate?.toInt()}fps" "${player.videoFormat?.width}x${player.videoFormat?.height} ${player.videoFormat?.frameRate?.toInt()}fps"
return VideoStats(videoId, videoInfo, videoQuality, audioInfo) return VideoStats(videoId, videoInfo, videoQuality, audioInfo)
} }
fun getPlayPauseActionIcon(player: Player) = when {
player.isPlaying -> R.drawable.ic_pause
player.playbackState == Player.STATE_ENDED -> R.drawable.ic_restart
else -> R.drawable.ic_play
}
} }

View File

@ -100,16 +100,18 @@ class OnlinePlayerService : LifecycleService() {
/** /**
* Listener for passing playback state changes to the AudioPlayerFragment * Listener for passing playback state changes to the AudioPlayerFragment
*/ */
var onIsPlayingChanged: ((isPlaying: Boolean) -> Unit)? = null var onStateOrPlayingChanged: ((isPlaying: Boolean) -> Unit)? = null
var onNewVideo: ((streams: Streams, videoId: String) -> Unit)? = null var onNewVideo: ((streams: Streams, videoId: String) -> Unit)? = null
private val playerListener = object : Player.Listener { private val playerListener = object : Player.Listener {
override fun onIsPlayingChanged(isPlaying: Boolean) { override fun onIsPlayingChanged(isPlaying: Boolean) {
super.onIsPlayingChanged(isPlaying) super.onIsPlayingChanged(isPlaying)
onIsPlayingChanged?.invoke(isPlaying) onStateOrPlayingChanged?.invoke(isPlaying)
} }
override fun onPlaybackStateChanged(state: Int) { override fun onPlaybackStateChanged(state: Int) {
onStateOrPlayingChanged?.invoke(player?.isPlaying ?: false)
when (state) { when (state) {
Player.STATE_ENDED -> { Player.STATE_ENDED -> {
if (PlayerHelper.shouldPlayNextVideo(playlistId != null) && !isTransitioning) playNextVideo() if (PlayerHelper.shouldPlayNextVideo(playlistId != null) && !isTransitioning) playNextVideo()
@ -411,12 +413,4 @@ class OnlinePlayerService : LifecycleService() {
fun getDuration() = player?.duration fun getDuration() = player?.duration
fun seekToPosition(position: Long) = player?.seekTo(position) fun seekToPosition(position: Long) = player?.seekTo(position)
fun pause() {
player?.pause()
}
fun play() {
player?.play()
}
} }

View File

@ -30,6 +30,7 @@ import com.github.libretube.databinding.FragmentAudioPlayerBinding
import com.github.libretube.extensions.normalize import com.github.libretube.extensions.normalize
import com.github.libretube.extensions.seekBy import com.github.libretube.extensions.seekBy
import com.github.libretube.extensions.toID import com.github.libretube.extensions.toID
import com.github.libretube.extensions.togglePlayPauseState
import com.github.libretube.helpers.AudioHelper import com.github.libretube.helpers.AudioHelper
import com.github.libretube.helpers.BackgroundHelper import com.github.libretube.helpers.BackgroundHelper
import com.github.libretube.helpers.ImageHelper import com.github.libretube.helpers.ImageHelper
@ -188,11 +189,11 @@ class AudioPlayerFragment : Fragment(), AudioPlayerOptions {
binding.thumbnail.setOnTouchListener(listener) binding.thumbnail.setOnTouchListener(listener)
binding.playPause.setOnClickListener { binding.playPause.setOnClickListener {
if (isPaused) playerService?.play() else playerService?.pause() playerService?.player?.togglePlayPauseState()
} }
binding.miniPlayerPause.setOnClickListener { binding.miniPlayerPause.setOnClickListener {
if (isPaused) playerService?.play() else playerService?.pause() playerService?.player?.togglePlayPauseState()
} }
binding.showMore.setOnClickListener { binding.showMore.setOnClickListener {
@ -207,7 +208,7 @@ class AudioPlayerFragment : Fragment(), AudioPlayerOptions {
bar.progress = audioHelper.getVolumeWithScale(bar.max) bar.progress = audioHelper.getVolumeWithScale(bar.max)
} }
if (!PlayerHelper.playAutomatically) updatePlayPauseButton(false) if (!PlayerHelper.playAutomatically) updatePlayPauseButton()
} }
private fun killFragment() { private fun killFragment() {
@ -347,16 +348,18 @@ class AudioPlayerFragment : Fragment(), AudioPlayerOptions {
handler.postDelayed(this::updateSeekBar, 200) handler.postDelayed(this::updateSeekBar, 200)
} }
private fun updatePlayPauseButton(isPlaying: Boolean) { private fun updatePlayPauseButton() {
val iconResource = if (isPlaying) R.drawable.ic_pause else R.drawable.ic_play playerService?.player?.let {
binding.playPause.setIconResource(iconResource) val iconRes = PlayerHelper.getPlayPauseActionIcon(it)
binding.miniPlayerPause.setImageResource(iconResource) binding.playPause.setIconResource(iconRes)
binding.miniPlayerPause.setImageResource(iconRes)
}
} }
private fun handleServiceConnection() { private fun handleServiceConnection() {
viewModel.player = playerService?.player viewModel.player = playerService?.player
playerService?.onIsPlayingChanged = { isPlaying -> playerService?.onStateOrPlayingChanged = { isPlaying ->
updatePlayPauseButton(isPlaying) updatePlayPauseButton()
isPaused = !isPlaying isPaused = !isPlaying
} }
playerService?.onNewVideo = { streams, videoId -> playerService?.onNewVideo = { streams, videoId ->
@ -373,7 +376,7 @@ class AudioPlayerFragment : Fragment(), AudioPlayerOptions {
override fun onDestroy() { override fun onDestroy() {
// unregister all listeners and the connected [playerService] // unregister all listeners and the connected [playerService]
playerService?.onIsPlayingChanged = null playerService?.onStateOrPlayingChanged = null
runCatching { runCatching {
activity?.unbindService(connection) activity?.unbindService(connection)
} }
@ -384,7 +387,7 @@ class AudioPlayerFragment : Fragment(), AudioPlayerOptions {
} }
override fun onSingleTap() { override fun onSingleTap() {
if (isPaused) playerService?.play() else playerService?.pause() playerService?.player?.togglePlayPauseState()
} }
override fun onLongTap() { override fun onLongTap() {

View File

@ -1160,13 +1160,7 @@ class PlayerFragment : Fragment(), OnlinePlayerOptions {
} }
private fun updatePlayPauseButton() { private fun updatePlayPauseButton() {
binding.playImageView.setImageResource( binding.playImageView.setImageResource(PlayerHelper.getPlayPauseActionIcon(exoPlayer))
when {
exoPlayer.isPlaying -> R.drawable.ic_pause
exoPlayer.playbackState == Player.STATE_ENDED -> R.drawable.ic_restart
else -> R.drawable.ic_play
}
)
} }
private suspend fun initializeHighlight(highlight: Segment) { private suspend fun initializeHighlight(highlight: Segment) {

View File

@ -168,7 +168,7 @@ open class CustomExoPlayerView(
Player.EVENT_PLAY_WHEN_READY_CHANGED Player.EVENT_PLAY_WHEN_READY_CHANGED
) )
) { ) {
updatePlayPauseButton() binding.playPauseBTN.setImageResource(PlayerHelper.getPlayPauseActionIcon(player))
// keep screen on if the video is playing // keep screen on if the video is playing
keepScreenOn = player.isPlaying == true keepScreenOn = player.isPlaying == true
@ -226,16 +226,6 @@ open class CustomExoPlayerView(
open fun onPlayerEvent(player: Player, playerEvents: Player.Events) = Unit open fun onPlayerEvent(player: Player, playerEvents: Player.Events) = Unit
private fun updatePlayPauseButton() {
binding.playPauseBTN.setImageResource(
when {
player?.isPlaying == true -> R.drawable.ic_pause
player?.playbackState == Player.STATE_ENDED -> R.drawable.ic_restart
else -> R.drawable.ic_play
}
)
}
private fun updateDisplayedDurationType(showTimeLeft: Boolean? = null) { private fun updateDisplayedDurationType(showTimeLeft: Boolean? = null) {
var shouldShowTimeLeft = showTimeLeft ?: PreferenceHelper var shouldShowTimeLeft = showTimeLeft ?: PreferenceHelper
.getBoolean(PreferenceKeys.SHOW_TIME_LEFT, false) .getBoolean(PreferenceKeys.SHOW_TIME_LEFT, false)