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, Rewind,
Next, Next,
Prev, Prev,
Background Background,
Stop
} }

View File

@ -1,6 +1,7 @@
package com.github.libretube.helpers package com.github.libretube.helpers
import android.app.Activity import android.app.Activity
import android.content.BroadcastReceiver
import android.content.Context import android.content.Context
import android.content.Intent import android.content.Intent
import android.content.pm.ActivityInfo 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.db.obj.WatchPosition
import com.github.libretube.enums.PlayerEvent import com.github.libretube.enums.PlayerEvent
import com.github.libretube.enums.SbSkipOptions 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.extensions.updateParameters
import com.github.libretube.obj.VideoStats import com.github.libretube.obj.VideoStats
import com.github.libretube.util.PlayingQueue import com.github.libretube.util.PlayingQueue
@ -800,4 +803,25 @@ object PlayerHelper {
DatabaseHolder.Database.watchPositionDao().insert(watchPosition) 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 package com.github.libretube.services
import android.content.BroadcastReceiver
import android.content.Context
import android.content.Intent import android.content.Intent
import android.content.IntentFilter
import android.os.Handler import android.os.Handler
import android.os.IBinder import android.os.IBinder
import android.os.Looper import android.os.Looper
import androidx.annotation.OptIn import androidx.annotation.OptIn
import androidx.core.app.NotificationCompat import androidx.core.app.NotificationCompat
import androidx.core.app.ServiceCompat import androidx.core.app.ServiceCompat
import androidx.core.content.ContextCompat
import androidx.lifecycle.LifecycleService import androidx.lifecycle.LifecycleService
import androidx.lifecycle.lifecycleScope import androidx.lifecycle.lifecycleScope
import androidx.media3.common.C 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.db.obj.DownloadWithItems
import com.github.libretube.enums.FileType import com.github.libretube.enums.FileType
import com.github.libretube.enums.NotificationId 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.toAndroidUri
import com.github.libretube.extensions.updateParameters import com.github.libretube.extensions.updateParameters
import com.github.libretube.helpers.PlayerHelper 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() { override fun onCreate() {
super.onCreate() super.onCreate()
@ -85,6 +105,13 @@ class OfflinePlayerService : LifecycleService() {
.build() .build()
startForeground(NotificationId.PLAYER_PLAYBACK.id, notification) 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 { override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int {
@ -189,6 +216,7 @@ class OfflinePlayerService : LifecycleService() {
nowPlayingNotification = null nowPlayingNotification = null
watchPositionTimer.destroy() watchPositionTimer.destroy()
unregisterReceiver(playerActionReceiver)
ServiceCompat.stopForeground(this, ServiceCompat.STOP_FOREGROUND_REMOVE) ServiceCompat.stopForeground(this, ServiceCompat.STOP_FOREGROUND_REMOVE)
stopSelf() stopSelf()

View File

@ -1,7 +1,10 @@
package com.github.libretube.services package com.github.libretube.services
import android.app.Notification import android.app.Notification
import android.content.BroadcastReceiver
import android.content.Context
import android.content.Intent import android.content.Intent
import android.content.IntentFilter
import android.os.Binder import android.os.Binder
import android.os.Handler import android.os.Handler
import android.os.IBinder import android.os.IBinder
@ -9,6 +12,7 @@ import android.os.Looper
import android.widget.Toast import android.widget.Toast
import androidx.core.app.NotificationCompat import androidx.core.app.NotificationCompat
import androidx.core.app.ServiceCompat import androidx.core.app.ServiceCompat
import androidx.core.content.ContextCompat
import androidx.core.net.toUri import androidx.core.net.toUri
import androidx.lifecycle.LifecycleService import androidx.lifecycle.LifecycleService
import androidx.lifecycle.lifecycleScope import androidx.lifecycle.lifecycleScope
@ -30,7 +34,9 @@ import com.github.libretube.constants.IntentData
import com.github.libretube.constants.PreferenceKeys import com.github.libretube.constants.PreferenceKeys
import com.github.libretube.db.DatabaseHelper import com.github.libretube.db.DatabaseHelper
import com.github.libretube.enums.NotificationId import com.github.libretube.enums.NotificationId
import com.github.libretube.enums.PlayerEvent
import com.github.libretube.extensions.parcelableExtra import com.github.libretube.extensions.parcelableExtra
import com.github.libretube.extensions.serializableExtra
import com.github.libretube.extensions.setMetadata import com.github.libretube.extensions.setMetadata
import com.github.libretube.extensions.toID import com.github.libretube.extensions.toID
import com.github.libretube.extensions.updateParameters 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 * Setting the required [Notification] for running as a foreground service
*/ */
@ -172,6 +200,13 @@ class OnlinePlayerService : LifecycleService() {
.build() .build()
startForeground(NotificationId.PLAYER_PLAYBACK.id, notification) 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, this,
) to MimeTypes.APPLICATION_MPD ) to MimeTypes.APPLICATION_MPD
} else { } 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() val mediaItem = MediaItem.Builder()
@ -395,6 +431,7 @@ class OnlinePlayerService : LifecycleService() {
player?.release() player?.release()
watchPositionTimer.destroy() watchPositionTimer.destroy()
unregisterReceiver(playerActionReceiver)
// called when the user pressed stop in the notification // called when the user pressed stop in the notification
// stop the service from being in the foreground and remove 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.db.obj.DownloadChapter
import com.github.libretube.enums.FileType import com.github.libretube.enums.FileType
import com.github.libretube.enums.PlayerEvent import com.github.libretube.enums.PlayerEvent
import com.github.libretube.extensions.seekBy
import com.github.libretube.extensions.serializableExtra import com.github.libretube.extensions.serializableExtra
import com.github.libretube.extensions.toAndroidUri import com.github.libretube.extensions.toAndroidUri
import com.github.libretube.extensions.togglePlayPauseState
import com.github.libretube.extensions.updateParameters import com.github.libretube.extensions.updateParameters
import com.github.libretube.helpers.PlayerHelper import com.github.libretube.helpers.PlayerHelper
import com.github.libretube.helpers.WindowHelper import com.github.libretube.helpers.WindowHelper
@ -115,21 +113,8 @@ class OfflinePlayerActivity : BaseActivity() {
private val playerActionReceiver = object : BroadcastReceiver() { private val playerActionReceiver = object : BroadcastReceiver() {
override fun onReceive(context: Context, intent: Intent) { override fun onReceive(context: Context, intent: Intent) {
when (intent.serializableExtra<PlayerEvent>(PlayerHelper.CONTROL_TYPE) ?: return) { val event = intent.serializableExtra<PlayerEvent>(PlayerHelper.CONTROL_TYPE) ?: return
PlayerEvent.PlayPause -> { PlayerHelper.handlePlayerAction(player, event)
player.togglePlayPauseState()
}
PlayerEvent.Forward -> {
player.seekBy(PlayerHelper.seekIncrement)
}
PlayerEvent.Rewind -> {
player.seekBy(-PlayerHelper.seekIncrement)
}
else -> Unit
}
} }
} }

View File

@ -66,7 +66,6 @@ import com.github.libretube.enums.PlayerEvent
import com.github.libretube.enums.ShareObjectType import com.github.libretube.enums.ShareObjectType
import com.github.libretube.extensions.formatShort import com.github.libretube.extensions.formatShort
import com.github.libretube.extensions.parcelable import com.github.libretube.extensions.parcelable
import com.github.libretube.extensions.seekBy
import com.github.libretube.extensions.serializableExtra import com.github.libretube.extensions.serializableExtra
import com.github.libretube.extensions.setMetadata import com.github.libretube.extensions.setMetadata
import com.github.libretube.extensions.toID import com.github.libretube.extensions.toID
@ -204,23 +203,19 @@ class PlayerFragment : Fragment(), OnlinePlayerOptions {
*/ */
private val playerActionReceiver = object : BroadcastReceiver() { private val playerActionReceiver = object : BroadcastReceiver() {
override fun onReceive(context: Context, intent: Intent) { override fun onReceive(context: Context, intent: Intent) {
when (intent.serializableExtra<PlayerEvent>(PlayerHelper.CONTROL_TYPE) ?: return) { val event = intent.serializableExtra<PlayerEvent>(PlayerHelper.CONTROL_TYPE) ?: return
PlayerEvent.PlayPause -> {
exoPlayer.togglePlayPauseState()
}
PlayerEvent.Forward -> { if (PlayerHelper.handlePlayerAction(exoPlayer, event)) return
exoPlayer.seekBy(PlayerHelper.seekIncrement)
}
PlayerEvent.Rewind -> {
exoPlayer.seekBy(-PlayerHelper.seekIncrement)
}
when (event) {
PlayerEvent.Next -> { PlayerEvent.Next -> {
playNextVideo(PlayingQueue.getNext()) playNextVideo(PlayingQueue.getNext())
} }
PlayerEvent.Prev -> {
playNextVideo(PlayingQueue.getPrev())
}
PlayerEvent.Background -> { PlayerEvent.Background -> {
playOnBackground() playOnBackground()
// wait some time in order for the service to get started properly // 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.NotificationManager
import android.app.PendingIntent import android.app.PendingIntent
import android.content.BroadcastReceiver
import android.content.Context import android.content.Context
import android.content.Intent import android.content.Intent
import android.content.IntentFilter
import android.graphics.Bitmap import android.graphics.Bitmap
import android.os.Build import android.os.Build
import android.os.Bundle import android.os.Bundle
@ -14,7 +12,6 @@ import android.support.v4.media.session.PlaybackStateCompat
import androidx.annotation.DrawableRes import androidx.annotation.DrawableRes
import androidx.core.app.NotificationCompat import androidx.core.app.NotificationCompat
import androidx.core.app.PendingIntentCompat import androidx.core.app.PendingIntentCompat
import androidx.core.content.ContextCompat
import androidx.core.content.getSystemService import androidx.core.content.getSystemService
import androidx.core.graphics.drawable.toBitmap import androidx.core.graphics.drawable.toBitmap
import androidx.media.app.NotificationCompat.MediaStyle 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.R
import com.github.libretube.constants.IntentData import com.github.libretube.constants.IntentData
import com.github.libretube.enums.NotificationId 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.toMediaMetadataCompat
import com.github.libretube.extensions.togglePlayPauseState
import com.github.libretube.helpers.BackgroundHelper
import com.github.libretube.helpers.ImageHelper import com.github.libretube.helpers.ImageHelper
import com.github.libretube.helpers.PlayerHelper import com.github.libretube.helpers.PlayerHelper
import com.github.libretube.obj.PlayerNotificationData import com.github.libretube.obj.PlayerNotificationData
import com.github.libretube.services.OfflinePlayerService
import com.github.libretube.ui.activities.MainActivity import com.github.libretube.ui.activities.MainActivity
import java.util.UUID import java.util.UUID
@ -133,14 +127,14 @@ class NowPlayingNotification(
private val legacyNotificationButtons private val legacyNotificationButtons
get() = listOf( get() = listOf(
createNotificationAction(R.drawable.ic_prev_outlined, PREV), createNotificationAction(R.drawable.ic_prev_outlined, PlayerEvent.Prev.name),
createNotificationAction( createNotificationAction(
if (player.isPlaying) R.drawable.ic_pause else R.drawable.ic_play, 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_next_outlined, PlayerEvent.Next.name),
createNotificationAction(R.drawable.ic_rewind_md, REWIND), createNotificationAction(R.drawable.ic_rewind_md, PlayerEvent.Rewind.name),
createNotificationAction(R.drawable.ic_forward_md, FORWARD) createNotificationAction(R.drawable.ic_forward_md, PlayerEvent.Forward.name)
) )
private fun createNotificationAction( private fun createNotificationAction(
@ -165,38 +159,38 @@ class NowPlayingNotification(
if (this::mediaSession.isInitialized) return if (this::mediaSession.isInitialized) return
val sessionCallback = object : MediaSessionCompat.Callback() { val sessionCallback = object : MediaSessionCompat.Callback() {
override fun onSkipToNext() {
handlePlayerAction(NEXT)
super.onSkipToNext()
}
override fun onSkipToPrevious() {
handlePlayerAction(PREV)
super.onSkipToPrevious()
}
override fun onRewind() { override fun onRewind() {
handlePlayerAction(REWIND) handlePlayerAction(PlayerEvent.Rewind)
super.onRewind() super.onRewind()
} }
override fun onFastForward() { override fun onFastForward() {
handlePlayerAction(FORWARD) handlePlayerAction(PlayerEvent.Forward)
super.onFastForward() super.onFastForward()
} }
override fun onPlay() { override fun onPlay() {
handlePlayerAction(PLAY_PAUSE) handlePlayerAction(PlayerEvent.PlayPause)
super.onPlay() super.onPlay()
} }
override fun onPause() { override fun onPause() {
handlePlayerAction(PLAY_PAUSE) handlePlayerAction(PlayerEvent.PlayPause)
super.onPause() super.onPause()
} }
override fun onSkipToNext() {
handlePlayerAction(PlayerEvent.Next)
super.onSkipToNext()
}
override fun onSkipToPrevious() {
handlePlayerAction(PlayerEvent.Prev)
super.onSkipToPrevious()
}
override fun onStop() { override fun onStop() {
handlePlayerAction(STOP) handlePlayerAction(PlayerEvent.Stop)
super.onStop() super.onStop()
} }
@ -206,7 +200,7 @@ class NowPlayingNotification(
} }
override fun onCustomAction(action: String, extras: Bundle?) { override fun onCustomAction(action: String, extras: Bundle?) {
handlePlayerAction(action) runCatching { handlePlayerAction(PlayerEvent.valueOf(action)) }
super.onCustomAction(action, extras) super.onCustomAction(action, extras)
} }
} }
@ -272,47 +266,20 @@ class NowPlayingNotification(
return PlaybackStateCompat.Builder() return PlaybackStateCompat.Builder()
.setActions(stateActions) .setActions(stateActions)
.addCustomAction(createMediaSessionAction(R.drawable.ic_rewind_md, REWIND)) .addCustomAction(createMediaSessionAction(R.drawable.ic_rewind_md, PlayerEvent.Rewind.name))
.addCustomAction(createMediaSessionAction(R.drawable.ic_forward_md, FORWARD)) .addCustomAction(createMediaSessionAction(R.drawable.ic_forward_md, PlayerEvent.Forward.name))
.setState(state, player.currentPosition, player.playbackParameters.speed) .setState(state, player.currentPosition, player.playbackParameters.speed)
.build() .build()
} }
private fun handlePlayerAction(action: String) { /**
when (action) { * Forward the action to the responsible notification owner (e.g. PlayerFragment)
NEXT -> { */
PlayingQueue.navigateNext() private fun handlePlayerAction(action: PlayerEvent) {
} val intent = Intent(PlayerHelper.getIntentActionName(context))
.setPackage(context.packageName)
PREV -> { .putExtra(PlayerHelper.CONTROL_TYPE, action)
PlayingQueue.navigatePrev() context.sendBroadcast(intent)
}
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
}
}
}
} }
/** /**
@ -329,7 +296,6 @@ class NowPlayingNotification(
if (notificationBuilder == null) { if (notificationBuilder == null) {
createMediaSession() createMediaSession()
createNotificationBuilder() createNotificationBuilder()
createActionReceiver()
// update the notification each time the player continues playing or pauses // update the notification each time the player continues playing or pauses
player.addListener(object : Player.Listener { player.addListener(object : Player.Listener {
override fun onIsPlayingChanged(isPlaying: Boolean) { override fun onIsPlayingChanged(isPlaying: Boolean) {
@ -349,7 +315,7 @@ class NowPlayingNotification(
notificationBuilder = NotificationCompat.Builder(context, PLAYER_CHANNEL_NAME) notificationBuilder = NotificationCompat.Builder(context, PLAYER_CHANNEL_NAME)
.setSmallIcon(R.drawable.ic_launcher_lockscreen) .setSmallIcon(R.drawable.ic_launcher_lockscreen)
.setContentIntent(createCurrentContentIntent()) .setContentIntent(createCurrentContentIntent())
.setDeleteIntent(createIntent(STOP)) .setDeleteIntent(createIntent(PlayerEvent.Stop.name))
.setStyle( .setStyle(
MediaStyle() MediaStyle()
.setMediaSession(mediaSession.sessionToken) .setMediaSession(mediaSession.sessionToken)
@ -374,36 +340,12 @@ class NowPlayingNotification(
nManager.notify(NotificationId.PLAYER_PLAYBACK.id, notification) 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] * Destroy the [NowPlayingNotification]
*/ */
fun destroySelf() { fun destroySelf() {
mediaSession.release() mediaSession.release()
runCatching {
context.unregisterReceiver(notificationActionReceiver)
}
nManager.cancel(NotificationId.PLAYER_PLAYBACK.id) nManager.cancel(NotificationId.PLAYER_PLAYBACK.id)
} }
@ -416,13 +358,6 @@ class NowPlayingNotification(
} }
companion object { 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 { enum class NowPlayingNotificationType {
VIDEO_ONLINE, VIDEO_ONLINE,
VIDEO_OFFLINE, VIDEO_OFFLINE,