mirror of
https://github.com/libre-tube/LibreTube.git
synced 2025-04-27 23:40:33 +05:30
feat: show repeat button in audio player when playback finished
This commit is contained in:
parent
73c98368a7
commit
46acd28c26
@ -41,11 +41,11 @@ import com.github.libretube.enums.SbSkipOptions
|
||||
import com.github.libretube.extensions.updateParameters
|
||||
import com.github.libretube.obj.VideoStats
|
||||
import com.github.libretube.util.TextUtils
|
||||
import kotlinx.coroutines.runBlocking
|
||||
import java.util.Locale
|
||||
import java.util.concurrent.Executors
|
||||
import kotlin.math.absoluteValue
|
||||
import kotlin.math.roundToInt
|
||||
import kotlinx.coroutines.runBlocking
|
||||
|
||||
object PlayerHelper {
|
||||
private const val ACTION_MEDIA_CONTROL = "media_control"
|
||||
@ -334,11 +334,11 @@ object PlayerHelper {
|
||||
|
||||
fun shouldPlayNextVideo(isPlaylist: Boolean = false): Boolean {
|
||||
return autoPlayEnabled || (
|
||||
isPlaylist && PreferenceHelper.getBoolean(
|
||||
PreferenceKeys.AUTOPLAY_PLAYLISTS,
|
||||
false
|
||||
)
|
||||
)
|
||||
isPlaylist && PreferenceHelper.getBoolean(
|
||||
PreferenceKeys.AUTOPLAY_PLAYLISTS,
|
||||
false
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
private val handleAudioFocus
|
||||
@ -552,9 +552,9 @@ object PlayerHelper {
|
||||
if (currentPosition in segmentStart until segmentEnd) {
|
||||
if (sponsorBlockConfig[segment.category] == SbSkipOptions.AUTOMATIC ||
|
||||
(
|
||||
sponsorBlockConfig[segment.category] == SbSkipOptions.AUTOMATIC_ONCE &&
|
||||
!segment.skipped
|
||||
)
|
||||
sponsorBlockConfig[segment.category] == SbSkipOptions.AUTOMATIC_ONCE &&
|
||||
!segment.skipped
|
||||
)
|
||||
) {
|
||||
if (sponsorBlockNotifications) {
|
||||
runCatching {
|
||||
@ -566,9 +566,9 @@ object PlayerHelper {
|
||||
segment.skipped = true
|
||||
} else if (sponsorBlockConfig[segment.category] == SbSkipOptions.MANUAL ||
|
||||
(
|
||||
sponsorBlockConfig[segment.category] == SbSkipOptions.AUTOMATIC_ONCE &&
|
||||
segment.skipped
|
||||
)
|
||||
sponsorBlockConfig[segment.category] == SbSkipOptions.AUTOMATIC_ONCE &&
|
||||
segment.skipped
|
||||
)
|
||||
) {
|
||||
return segment
|
||||
}
|
||||
@ -598,7 +598,8 @@ object PlayerHelper {
|
||||
val chapter = chapters[index]
|
||||
// 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
|
||||
val isWithinMaxHighlightDuration = (currentPositionSeconds - chapter.start) < ChapterSegment.HIGHLIGHT_LENGTH
|
||||
val isWithinMaxHighlightDuration =
|
||||
(currentPositionSeconds - chapter.start) < ChapterSegment.HIGHLIGHT_LENGTH
|
||||
chapter.highlightDrawable == null || isWithinMaxHighlightDuration
|
||||
}
|
||||
}
|
||||
@ -752,9 +753,9 @@ object PlayerHelper {
|
||||
*/
|
||||
fun haveAudioTrackRoleFlagSet(@C.RoleFlags roleFlags: Int): Boolean {
|
||||
return isFlagSet(roleFlags, C.ROLE_FLAG_DESCRIBES_VIDEO) ||
|
||||
isFlagSet(roleFlags, C.ROLE_FLAG_DUB) ||
|
||||
isFlagSet(roleFlags, C.ROLE_FLAG_MAIN) ||
|
||||
isFlagSet(roleFlags, C.ROLE_FLAG_ALTERNATE)
|
||||
isFlagSet(roleFlags, C.ROLE_FLAG_DUB) ||
|
||||
isFlagSet(roleFlags, C.ROLE_FLAG_MAIN) ||
|
||||
isFlagSet(roleFlags, C.ROLE_FLAG_ALTERNATE)
|
||||
}
|
||||
|
||||
@androidx.annotation.OptIn(androidx.media3.common.util.UnstableApi::class)
|
||||
@ -771,4 +772,10 @@ object PlayerHelper {
|
||||
"${player.videoFormat?.width}x${player.videoFormat?.height} ${player.videoFormat?.frameRate?.toInt()}fps"
|
||||
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
|
||||
}
|
||||
}
|
||||
|
@ -100,16 +100,18 @@ class OnlinePlayerService : LifecycleService() {
|
||||
/**
|
||||
* 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
|
||||
|
||||
private val playerListener = object : Player.Listener {
|
||||
override fun onIsPlayingChanged(isPlaying: Boolean) {
|
||||
super.onIsPlayingChanged(isPlaying)
|
||||
onIsPlayingChanged?.invoke(isPlaying)
|
||||
onStateOrPlayingChanged?.invoke(isPlaying)
|
||||
}
|
||||
|
||||
override fun onPlaybackStateChanged(state: Int) {
|
||||
onStateOrPlayingChanged?.invoke(player?.isPlaying ?: false)
|
||||
|
||||
when (state) {
|
||||
Player.STATE_ENDED -> {
|
||||
if (PlayerHelper.shouldPlayNextVideo(playlistId != null) && !isTransitioning) playNextVideo()
|
||||
@ -411,12 +413,4 @@ class OnlinePlayerService : LifecycleService() {
|
||||
fun getDuration() = player?.duration
|
||||
|
||||
fun seekToPosition(position: Long) = player?.seekTo(position)
|
||||
|
||||
fun pause() {
|
||||
player?.pause()
|
||||
}
|
||||
|
||||
fun play() {
|
||||
player?.play()
|
||||
}
|
||||
}
|
||||
|
@ -30,6 +30,7 @@ import com.github.libretube.databinding.FragmentAudioPlayerBinding
|
||||
import com.github.libretube.extensions.normalize
|
||||
import com.github.libretube.extensions.seekBy
|
||||
import com.github.libretube.extensions.toID
|
||||
import com.github.libretube.extensions.togglePlayPauseState
|
||||
import com.github.libretube.helpers.AudioHelper
|
||||
import com.github.libretube.helpers.BackgroundHelper
|
||||
import com.github.libretube.helpers.ImageHelper
|
||||
@ -188,11 +189,11 @@ class AudioPlayerFragment : Fragment(), AudioPlayerOptions {
|
||||
binding.thumbnail.setOnTouchListener(listener)
|
||||
|
||||
binding.playPause.setOnClickListener {
|
||||
if (isPaused) playerService?.play() else playerService?.pause()
|
||||
playerService?.player?.togglePlayPauseState()
|
||||
}
|
||||
|
||||
binding.miniPlayerPause.setOnClickListener {
|
||||
if (isPaused) playerService?.play() else playerService?.pause()
|
||||
playerService?.player?.togglePlayPauseState()
|
||||
}
|
||||
|
||||
binding.showMore.setOnClickListener {
|
||||
@ -207,7 +208,7 @@ class AudioPlayerFragment : Fragment(), AudioPlayerOptions {
|
||||
bar.progress = audioHelper.getVolumeWithScale(bar.max)
|
||||
}
|
||||
|
||||
if (!PlayerHelper.playAutomatically) updatePlayPauseButton(false)
|
||||
if (!PlayerHelper.playAutomatically) updatePlayPauseButton()
|
||||
}
|
||||
|
||||
private fun killFragment() {
|
||||
@ -347,16 +348,18 @@ class AudioPlayerFragment : Fragment(), AudioPlayerOptions {
|
||||
handler.postDelayed(this::updateSeekBar, 200)
|
||||
}
|
||||
|
||||
private fun updatePlayPauseButton(isPlaying: Boolean) {
|
||||
val iconResource = if (isPlaying) R.drawable.ic_pause else R.drawable.ic_play
|
||||
binding.playPause.setIconResource(iconResource)
|
||||
binding.miniPlayerPause.setImageResource(iconResource)
|
||||
private fun updatePlayPauseButton() {
|
||||
playerService?.player?.let {
|
||||
val iconRes = PlayerHelper.getPlayPauseActionIcon(it)
|
||||
binding.playPause.setIconResource(iconRes)
|
||||
binding.miniPlayerPause.setImageResource(iconRes)
|
||||
}
|
||||
}
|
||||
|
||||
private fun handleServiceConnection() {
|
||||
viewModel.player = playerService?.player
|
||||
playerService?.onIsPlayingChanged = { isPlaying ->
|
||||
updatePlayPauseButton(isPlaying)
|
||||
playerService?.onStateOrPlayingChanged = { isPlaying ->
|
||||
updatePlayPauseButton()
|
||||
isPaused = !isPlaying
|
||||
}
|
||||
playerService?.onNewVideo = { streams, videoId ->
|
||||
@ -373,7 +376,7 @@ class AudioPlayerFragment : Fragment(), AudioPlayerOptions {
|
||||
|
||||
override fun onDestroy() {
|
||||
// unregister all listeners and the connected [playerService]
|
||||
playerService?.onIsPlayingChanged = null
|
||||
playerService?.onStateOrPlayingChanged = null
|
||||
runCatching {
|
||||
activity?.unbindService(connection)
|
||||
}
|
||||
@ -384,7 +387,7 @@ class AudioPlayerFragment : Fragment(), AudioPlayerOptions {
|
||||
}
|
||||
|
||||
override fun onSingleTap() {
|
||||
if (isPaused) playerService?.play() else playerService?.pause()
|
||||
playerService?.player?.togglePlayPauseState()
|
||||
}
|
||||
|
||||
override fun onLongTap() {
|
||||
|
@ -1160,13 +1160,7 @@ class PlayerFragment : Fragment(), OnlinePlayerOptions {
|
||||
}
|
||||
|
||||
private fun updatePlayPauseButton() {
|
||||
binding.playImageView.setImageResource(
|
||||
when {
|
||||
exoPlayer.isPlaying -> R.drawable.ic_pause
|
||||
exoPlayer.playbackState == Player.STATE_ENDED -> R.drawable.ic_restart
|
||||
else -> R.drawable.ic_play
|
||||
}
|
||||
)
|
||||
binding.playImageView.setImageResource(PlayerHelper.getPlayPauseActionIcon(exoPlayer))
|
||||
}
|
||||
|
||||
private suspend fun initializeHighlight(highlight: Segment) {
|
||||
|
@ -168,7 +168,7 @@ open class CustomExoPlayerView(
|
||||
Player.EVENT_PLAY_WHEN_READY_CHANGED
|
||||
)
|
||||
) {
|
||||
updatePlayPauseButton()
|
||||
binding.playPauseBTN.setImageResource(PlayerHelper.getPlayPauseActionIcon(player))
|
||||
|
||||
// keep screen on if the video is playing
|
||||
keepScreenOn = player.isPlaying == true
|
||||
@ -226,16 +226,6 @@ open class CustomExoPlayerView(
|
||||
|
||||
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) {
|
||||
var shouldShowTimeLeft = showTimeLeft ?: PreferenceHelper
|
||||
.getBoolean(PreferenceKeys.SHOW_TIME_LEFT, false)
|
||||
|
Loading…
x
Reference in New Issue
Block a user