Merge pull request #5925 from Bnyro/master

refactor: simplify notification/player actions logic
This commit is contained in:
Bnyro 2024-04-18 18:48:47 +02:00 committed by GitHub
commit 221a33c623
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
7 changed files with 134 additions and 129 deletions

View File

@ -6,5 +6,6 @@ enum class PlayerEvent {
Rewind,
Next,
Prev,
Background
Background,
Stop
}

View File

@ -1,6 +1,7 @@
package com.github.libretube.helpers
import android.app.Activity
import android.content.BroadcastReceiver
import android.content.Context
import android.content.Intent
import android.content.pm.ActivityInfo
@ -38,6 +39,8 @@ import com.github.libretube.db.DatabaseHolder
import com.github.libretube.db.obj.WatchPosition
import com.github.libretube.enums.PlayerEvent
import com.github.libretube.enums.SbSkipOptions
import com.github.libretube.extensions.seekBy
import com.github.libretube.extensions.togglePlayPauseState
import com.github.libretube.extensions.updateParameters
import com.github.libretube.obj.VideoStats
import com.github.libretube.util.PlayingQueue
@ -800,4 +803,25 @@ object PlayerHelper {
DatabaseHolder.Database.watchPositionDao().insert(watchPosition)
}
}
fun handlePlayerAction(player: Player, playerEvent: PlayerEvent): Boolean {
return when (playerEvent) {
PlayerEvent.PlayPause -> {
player.togglePlayPauseState()
true
}
PlayerEvent.Forward -> {
player.seekBy(PlayerHelper.seekIncrement)
true
}
PlayerEvent.Rewind -> {
player.seekBy(-PlayerHelper.seekIncrement)
true
}
else -> false
}
}
}

View File

@ -1,12 +1,16 @@
package com.github.libretube.services
import android.content.BroadcastReceiver
import android.content.Context
import android.content.Intent
import android.content.IntentFilter
import android.os.Handler
import android.os.IBinder
import android.os.Looper
import androidx.annotation.OptIn
import androidx.core.app.NotificationCompat
import androidx.core.app.ServiceCompat
import androidx.core.content.ContextCompat
import androidx.lifecycle.LifecycleService
import androidx.lifecycle.lifecycleScope
import androidx.media3.common.C
@ -22,6 +26,8 @@ import com.github.libretube.db.DatabaseHolder
import com.github.libretube.db.obj.DownloadWithItems
import com.github.libretube.enums.FileType
import com.github.libretube.enums.NotificationId
import com.github.libretube.enums.PlayerEvent
import com.github.libretube.extensions.serializableExtra
import com.github.libretube.extensions.toAndroidUri
import com.github.libretube.extensions.updateParameters
import com.github.libretube.helpers.PlayerHelper
@ -75,6 +81,20 @@ class OfflinePlayerService : LifecycleService() {
}
}
private val playerActionReceiver = object : BroadcastReceiver() {
override fun onReceive(context: Context, intent: Intent) {
val event = intent.serializableExtra<PlayerEvent>(PlayerHelper.CONTROL_TYPE) ?: return
val player = player ?: return
if (PlayerHelper.handlePlayerAction(player, event)) return
when (event) {
PlayerEvent.Stop -> onDestroy()
else -> Unit
}
}
}
override fun onCreate() {
super.onCreate()
@ -85,6 +105,13 @@ class OfflinePlayerService : LifecycleService() {
.build()
startForeground(NotificationId.PLAYER_PLAYBACK.id, notification)
ContextCompat.registerReceiver(
this,
playerActionReceiver,
IntentFilter(PlayerHelper.getIntentActionName(this)),
ContextCompat.RECEIVER_NOT_EXPORTED
)
}
override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int {
@ -189,6 +216,7 @@ class OfflinePlayerService : LifecycleService() {
nowPlayingNotification = null
watchPositionTimer.destroy()
unregisterReceiver(playerActionReceiver)
ServiceCompat.stopForeground(this, ServiceCompat.STOP_FOREGROUND_REMOVE)
stopSelf()

View File

@ -1,7 +1,10 @@
package com.github.libretube.services
import android.app.Notification
import android.content.BroadcastReceiver
import android.content.Context
import android.content.Intent
import android.content.IntentFilter
import android.os.Binder
import android.os.Handler
import android.os.IBinder
@ -9,6 +12,7 @@ import android.os.Looper
import android.widget.Toast
import androidx.core.app.NotificationCompat
import androidx.core.app.ServiceCompat
import androidx.core.content.ContextCompat
import androidx.core.net.toUri
import androidx.lifecycle.LifecycleService
import androidx.lifecycle.lifecycleScope
@ -30,7 +34,9 @@ import com.github.libretube.constants.IntentData
import com.github.libretube.constants.PreferenceKeys
import com.github.libretube.db.DatabaseHelper
import com.github.libretube.enums.NotificationId
import com.github.libretube.enums.PlayerEvent
import com.github.libretube.extensions.parcelableExtra
import com.github.libretube.extensions.serializableExtra
import com.github.libretube.extensions.setMetadata
import com.github.libretube.extensions.toID
import com.github.libretube.extensions.updateParameters
@ -159,6 +165,28 @@ class OnlinePlayerService : LifecycleService() {
}
}
private val playerActionReceiver = object : BroadcastReceiver() {
override fun onReceive(context: Context, intent: Intent) {
val event = intent.serializableExtra<PlayerEvent>(PlayerHelper.CONTROL_TYPE) ?: return
val player = player ?: return
if (PlayerHelper.handlePlayerAction(player, event)) return
when (event) {
PlayerEvent.Next -> {
PlayingQueue.navigateNext()
}
PlayerEvent.Prev -> {
PlayingQueue.navigatePrev()
}
PlayerEvent.Stop -> {
onDestroy()
}
else -> Unit
}
}
}
/**
* Setting the required [Notification] for running as a foreground service
*/
@ -172,6 +200,13 @@ class OnlinePlayerService : LifecycleService() {
.build()
startForeground(NotificationId.PLAYER_PLAYBACK.id, notification)
ContextCompat.registerReceiver(
this,
playerActionReceiver,
IntentFilter(PlayerHelper.getIntentActionName(this)),
ContextCompat.RECEIVER_NOT_EXPORTED
)
}
/**
@ -338,7 +373,8 @@ class OnlinePlayerService : LifecycleService() {
this,
) to MimeTypes.APPLICATION_MPD
} else {
ProxyHelper.unwrapStreamUrl(streams.hls.orEmpty()).toUri() to MimeTypes.APPLICATION_M3U8
ProxyHelper.unwrapStreamUrl(streams.hls.orEmpty())
.toUri() to MimeTypes.APPLICATION_M3U8
}
val mediaItem = MediaItem.Builder()
@ -395,6 +431,7 @@ class OnlinePlayerService : LifecycleService() {
player?.release()
watchPositionTimer.destroy()
unregisterReceiver(playerActionReceiver)
// called when the user pressed stop in the notification
// stop the service from being in the foreground and remove the notification

View File

@ -36,10 +36,8 @@ import com.github.libretube.db.DatabaseHolder.Database
import com.github.libretube.db.obj.DownloadChapter
import com.github.libretube.enums.FileType
import com.github.libretube.enums.PlayerEvent
import com.github.libretube.extensions.seekBy
import com.github.libretube.extensions.serializableExtra
import com.github.libretube.extensions.toAndroidUri
import com.github.libretube.extensions.togglePlayPauseState
import com.github.libretube.extensions.updateParameters
import com.github.libretube.helpers.PlayerHelper
import com.github.libretube.helpers.WindowHelper
@ -115,21 +113,8 @@ class OfflinePlayerActivity : BaseActivity() {
private val playerActionReceiver = object : BroadcastReceiver() {
override fun onReceive(context: Context, intent: Intent) {
when (intent.serializableExtra<PlayerEvent>(PlayerHelper.CONTROL_TYPE) ?: return) {
PlayerEvent.PlayPause -> {
player.togglePlayPauseState()
}
PlayerEvent.Forward -> {
player.seekBy(PlayerHelper.seekIncrement)
}
PlayerEvent.Rewind -> {
player.seekBy(-PlayerHelper.seekIncrement)
}
else -> Unit
}
val event = intent.serializableExtra<PlayerEvent>(PlayerHelper.CONTROL_TYPE) ?: return
PlayerHelper.handlePlayerAction(player, event)
}
}

View File

@ -66,7 +66,6 @@ import com.github.libretube.enums.PlayerEvent
import com.github.libretube.enums.ShareObjectType
import com.github.libretube.extensions.formatShort
import com.github.libretube.extensions.parcelable
import com.github.libretube.extensions.seekBy
import com.github.libretube.extensions.serializableExtra
import com.github.libretube.extensions.setMetadata
import com.github.libretube.extensions.toID
@ -204,23 +203,19 @@ class PlayerFragment : Fragment(), OnlinePlayerOptions {
*/
private val playerActionReceiver = object : BroadcastReceiver() {
override fun onReceive(context: Context, intent: Intent) {
when (intent.serializableExtra<PlayerEvent>(PlayerHelper.CONTROL_TYPE) ?: return) {
PlayerEvent.PlayPause -> {
exoPlayer.togglePlayPauseState()
}
val event = intent.serializableExtra<PlayerEvent>(PlayerHelper.CONTROL_TYPE) ?: return
PlayerEvent.Forward -> {
exoPlayer.seekBy(PlayerHelper.seekIncrement)
}
PlayerEvent.Rewind -> {
exoPlayer.seekBy(-PlayerHelper.seekIncrement)
}
if (PlayerHelper.handlePlayerAction(exoPlayer, event)) return
when (event) {
PlayerEvent.Next -> {
playNextVideo(PlayingQueue.getNext())
}
PlayerEvent.Prev -> {
playNextVideo(PlayingQueue.getPrev())
}
PlayerEvent.Background -> {
playOnBackground()
// wait some time in order for the service to get started properly

View File

@ -2,10 +2,8 @@ package com.github.libretube.util
import android.app.NotificationManager
import android.app.PendingIntent
import android.content.BroadcastReceiver
import android.content.Context
import android.content.Intent
import android.content.IntentFilter
import android.graphics.Bitmap
import android.os.Build
import android.os.Bundle
@ -14,7 +12,6 @@ import android.support.v4.media.session.PlaybackStateCompat
import androidx.annotation.DrawableRes
import androidx.core.app.NotificationCompat
import androidx.core.app.PendingIntentCompat
import androidx.core.content.ContextCompat
import androidx.core.content.getSystemService
import androidx.core.graphics.drawable.toBitmap
import androidx.media.app.NotificationCompat.MediaStyle
@ -26,14 +23,11 @@ import com.github.libretube.LibreTubeApp.Companion.PLAYER_CHANNEL_NAME
import com.github.libretube.R
import com.github.libretube.constants.IntentData
import com.github.libretube.enums.NotificationId
import com.github.libretube.extensions.seekBy
import com.github.libretube.enums.PlayerEvent
import com.github.libretube.extensions.toMediaMetadataCompat
import com.github.libretube.extensions.togglePlayPauseState
import com.github.libretube.helpers.BackgroundHelper
import com.github.libretube.helpers.ImageHelper
import com.github.libretube.helpers.PlayerHelper
import com.github.libretube.obj.PlayerNotificationData
import com.github.libretube.services.OfflinePlayerService
import com.github.libretube.ui.activities.MainActivity
import java.util.UUID
@ -133,14 +127,14 @@ class NowPlayingNotification(
private val legacyNotificationButtons
get() = listOf(
createNotificationAction(R.drawable.ic_prev_outlined, PREV),
createNotificationAction(R.drawable.ic_prev_outlined, PlayerEvent.Prev.name),
createNotificationAction(
if (player.isPlaying) R.drawable.ic_pause else R.drawable.ic_play,
PLAY_PAUSE
PlayerEvent.PlayPause.name
),
createNotificationAction(R.drawable.ic_next_outlined, NEXT),
createNotificationAction(R.drawable.ic_rewind_md, REWIND),
createNotificationAction(R.drawable.ic_forward_md, FORWARD)
createNotificationAction(R.drawable.ic_next_outlined, PlayerEvent.Next.name),
createNotificationAction(R.drawable.ic_rewind_md, PlayerEvent.Rewind.name),
createNotificationAction(R.drawable.ic_forward_md, PlayerEvent.Forward.name)
)
private fun createNotificationAction(
@ -165,38 +159,38 @@ class NowPlayingNotification(
if (this::mediaSession.isInitialized) return
val sessionCallback = object : MediaSessionCompat.Callback() {
override fun onSkipToNext() {
handlePlayerAction(NEXT)
super.onSkipToNext()
}
override fun onSkipToPrevious() {
handlePlayerAction(PREV)
super.onSkipToPrevious()
}
override fun onRewind() {
handlePlayerAction(REWIND)
handlePlayerAction(PlayerEvent.Rewind)
super.onRewind()
}
override fun onFastForward() {
handlePlayerAction(FORWARD)
handlePlayerAction(PlayerEvent.Forward)
super.onFastForward()
}
override fun onPlay() {
handlePlayerAction(PLAY_PAUSE)
handlePlayerAction(PlayerEvent.PlayPause)
super.onPlay()
}
override fun onPause() {
handlePlayerAction(PLAY_PAUSE)
handlePlayerAction(PlayerEvent.PlayPause)
super.onPause()
}
override fun onSkipToNext() {
handlePlayerAction(PlayerEvent.Next)
super.onSkipToNext()
}
override fun onSkipToPrevious() {
handlePlayerAction(PlayerEvent.Prev)
super.onSkipToPrevious()
}
override fun onStop() {
handlePlayerAction(STOP)
handlePlayerAction(PlayerEvent.Stop)
super.onStop()
}
@ -206,7 +200,7 @@ class NowPlayingNotification(
}
override fun onCustomAction(action: String, extras: Bundle?) {
handlePlayerAction(action)
runCatching { handlePlayerAction(PlayerEvent.valueOf(action)) }
super.onCustomAction(action, extras)
}
}
@ -272,47 +266,20 @@ class NowPlayingNotification(
return PlaybackStateCompat.Builder()
.setActions(stateActions)
.addCustomAction(createMediaSessionAction(R.drawable.ic_rewind_md, REWIND))
.addCustomAction(createMediaSessionAction(R.drawable.ic_forward_md, FORWARD))
.addCustomAction(createMediaSessionAction(R.drawable.ic_rewind_md, PlayerEvent.Rewind.name))
.addCustomAction(createMediaSessionAction(R.drawable.ic_forward_md, PlayerEvent.Forward.name))
.setState(state, player.currentPosition, player.playbackParameters.speed)
.build()
}
private fun handlePlayerAction(action: String) {
when (action) {
NEXT -> {
PlayingQueue.navigateNext()
}
PREV -> {
PlayingQueue.navigatePrev()
}
REWIND -> {
player.seekBy(-PlayerHelper.seekIncrement)
}
FORWARD -> {
player.seekBy(PlayerHelper.seekIncrement)
}
PLAY_PAUSE -> {
player.togglePlayPauseState()
}
STOP -> {
when (notificationType) {
NowPlayingNotificationType.AUDIO_ONLINE -> BackgroundHelper.stopBackgroundPlay(
context
)
NowPlayingNotificationType.AUDIO_OFFLINE -> BackgroundHelper.stopBackgroundPlay(
context,
OfflinePlayerService::class.java
)
else -> Unit
}
}
}
/**
* Forward the action to the responsible notification owner (e.g. PlayerFragment)
*/
private fun handlePlayerAction(action: PlayerEvent) {
val intent = Intent(PlayerHelper.getIntentActionName(context))
.setPackage(context.packageName)
.putExtra(PlayerHelper.CONTROL_TYPE, action)
context.sendBroadcast(intent)
}
/**
@ -329,7 +296,6 @@ class NowPlayingNotification(
if (notificationBuilder == null) {
createMediaSession()
createNotificationBuilder()
createActionReceiver()
// update the notification each time the player continues playing or pauses
player.addListener(object : Player.Listener {
override fun onIsPlayingChanged(isPlaying: Boolean) {
@ -349,7 +315,7 @@ class NowPlayingNotification(
notificationBuilder = NotificationCompat.Builder(context, PLAYER_CHANNEL_NAME)
.setSmallIcon(R.drawable.ic_launcher_lockscreen)
.setContentIntent(createCurrentContentIntent())
.setDeleteIntent(createIntent(STOP))
.setDeleteIntent(createIntent(PlayerEvent.Stop.name))
.setStyle(
MediaStyle()
.setMediaSession(mediaSession.sessionToken)
@ -374,36 +340,12 @@ class NowPlayingNotification(
nManager.notify(NotificationId.PLAYER_PLAYBACK.id, notification)
}
private val notificationActionReceiver = object : BroadcastReceiver() {
override fun onReceive(context: Context, intent: Intent) {
handlePlayerAction(intent.action ?: return)
}
}
private fun createActionReceiver() {
val filter = IntentFilter().apply {
listOf(PREV, NEXT, REWIND, FORWARD, PLAY_PAUSE, STOP).forEach {
addAction(it)
}
}
ContextCompat.registerReceiver(
context,
notificationActionReceiver,
filter,
ContextCompat.RECEIVER_NOT_EXPORTED
)
}
/**
* Destroy the [NowPlayingNotification]
*/
fun destroySelf() {
mediaSession.release()
runCatching {
context.unregisterReceiver(notificationActionReceiver)
}
nManager.cancel(NotificationId.PLAYER_PLAYBACK.id)
}
@ -416,13 +358,6 @@ class NowPlayingNotification(
}
companion object {
private const val PREV = "prev"
private const val NEXT = "next"
private const val REWIND = "rewind"
private const val FORWARD = "forward"
private const val PLAY_PAUSE = "play_pause"
private const val STOP = "stop"
enum class NowPlayingNotificationType {
VIDEO_ONLINE,
VIDEO_OFFLINE,