Add custom notification actions

This commit is contained in:
Bnyro 2023-01-15 14:26:55 +01:00
parent 12d4ee9e8e
commit 90e87c596e

View File

@ -5,7 +5,6 @@ import android.app.NotificationManager
import android.app.PendingIntent import android.app.PendingIntent
import android.content.Context import android.content.Context
import android.content.Intent import android.content.Intent
import android.content.res.Resources
import android.graphics.Bitmap import android.graphics.Bitmap
import android.graphics.BitmapFactory import android.graphics.BitmapFactory
import android.graphics.drawable.BitmapDrawable import android.graphics.drawable.BitmapDrawable
@ -14,6 +13,10 @@ import android.os.Bundle
import android.support.v4.media.MediaDescriptionCompat import android.support.v4.media.MediaDescriptionCompat
import android.support.v4.media.MediaMetadataCompat import android.support.v4.media.MediaMetadataCompat
import android.support.v4.media.session.MediaSessionCompat import android.support.v4.media.session.MediaSessionCompat
import android.support.v4.media.session.PlaybackStateCompat
import android.util.Log
import androidx.annotation.DrawableRes
import androidx.core.app.NotificationCompat
import coil.request.ImageRequest import coil.request.ImageRequest
import com.github.libretube.R import com.github.libretube.R
import com.github.libretube.api.obj.Streams import com.github.libretube.api.obj.Streams
@ -27,6 +30,7 @@ import com.google.android.exoplayer2.Player
import com.google.android.exoplayer2.ext.mediasession.MediaSessionConnector import com.google.android.exoplayer2.ext.mediasession.MediaSessionConnector
import com.google.android.exoplayer2.ext.mediasession.TimelineQueueNavigator import com.google.android.exoplayer2.ext.mediasession.TimelineQueueNavigator
import com.google.android.exoplayer2.ui.PlayerNotificationManager import com.google.android.exoplayer2.ui.PlayerNotificationManager
import com.google.android.exoplayer2.ui.PlayerNotificationManager.CustomActionReceiver
class NowPlayingNotification( class NowPlayingNotification(
private val context: Context, private val context: Context,
@ -52,11 +56,10 @@ class NowPlayingNotification(
private var playerNotification: PlayerNotificationManager? = null private var playerNotification: PlayerNotificationManager? = null
/** /**
* The [DescriptionAdapter] is used to show title, uploaderName and thumbnail of the video in the notification * The [descriptionAdapter] is used to show title, uploaderName and thumbnail of the video in the notification
* Basic example [here](https://github.com/AnthonyMarkD/AudioPlayerSampleTest) * Basic example [here](https://github.com/AnthonyMarkD/AudioPlayerSampleTest)
*/ */
inner class DescriptionAdapter : private val descriptionAdapter = object : PlayerNotificationManager.MediaDescriptionAdapter {
PlayerNotificationManager.MediaDescriptionAdapter {
/** /**
* sets the title of the notification * sets the title of the notification
*/ */
@ -125,6 +128,51 @@ class NowPlayingNotification(
} }
} }
private val customActionReceiver = object : CustomActionReceiver {
override fun createCustomActions(
context: Context,
instanceId: Int
): MutableMap<String, NotificationCompat.Action> {
return mutableMapOf(
PREV to createNotificationAction(R.drawable.ic_prev, PREV, instanceId),
NEXT to createNotificationAction(R.drawable.ic_next, NEXT, instanceId),
REWIND to createNotificationAction(R.drawable.ic_rewind, REWIND, instanceId),
FORWARD to createNotificationAction(R.drawable.ic_forward, FORWARD, instanceId)
)
}
override fun getCustomActions(player: Player): MutableList<String> {
return mutableListOf(PREV, NEXT, REWIND, FORWARD)
}
override fun onCustomAction(player: Player, action: String, intent: Intent) {
handlePlayerAction(action)
}
}
private fun createNotificationAction(drawableRes: Int, actionName: String, instanceId: Int): NotificationCompat.Action {
val intent: Intent = Intent(actionName).setPackage(context.packageName)
val pendingIntent = PendingIntent.getBroadcast(
context,
instanceId,
intent,
PendingIntentCompat.cancelCurrentFlags
)
return NotificationCompat.Action.Builder(drawableRes, actionName, pendingIntent).build()
}
private fun createMediaSessionAction(@DrawableRes drawableRes: Int, actionName: String): MediaSessionConnector.CustomActionProvider {
return object : MediaSessionConnector.CustomActionProvider {
override fun getCustomAction(player: Player): PlaybackStateCompat.CustomAction? {
return PlaybackStateCompat.CustomAction.Builder(actionName, actionName, drawableRes).build()
}
override fun onCustomAction(player: Player, action: String, extras: Bundle?) {
handlePlayerAction(action)
}
}
}
/** /**
* Creates a [MediaSessionCompat] amd a [MediaSessionConnector] for the player * Creates a [MediaSessionCompat] amd a [MediaSessionConnector] for the player
*/ */
@ -142,22 +190,50 @@ class NowPlayingNotification(
return MediaDescriptionCompat.Builder().apply { return MediaDescriptionCompat.Builder().apply {
setTitle(streams?.title!!) setTitle(streams?.title!!)
setSubtitle(streams?.uploader) setSubtitle(streams?.uploader)
val extras = Bundle()
val appIcon = BitmapFactory.decodeResource( val appIcon = BitmapFactory.decodeResource(
Resources.getSystem(), context.resources,
R.drawable.ic_launcher_monochrome R.drawable.ic_launcher_monochrome
) )
extras.putParcelable(MediaMetadataCompat.METADATA_KEY_DISPLAY_ICON, appIcon) val extras = Bundle().apply {
extras.putString(MediaMetadataCompat.METADATA_KEY_TITLE, streams?.title!!) putParcelable(MediaMetadataCompat.METADATA_KEY_DISPLAY_ICON, appIcon)
extras.putString(MediaMetadataCompat.METADATA_KEY_ARTIST, streams?.uploader) putString(MediaMetadataCompat.METADATA_KEY_TITLE, streams?.title!!)
putString(MediaMetadataCompat.METADATA_KEY_ARTIST, streams?.uploader)
}
setIconBitmap(appIcon) setIconBitmap(appIcon)
setExtras(extras) setExtras(extras)
}.build() }.build()
} }
}) })
mediaSessionConnector.setCustomActionProviders(
createMediaSessionAction(R.drawable.ic_prev, PREV),
createMediaSessionAction(R.drawable.ic_next, NEXT),
createMediaSessionAction(R.drawable.ic_rewind, REWIND),
createMediaSessionAction(R.drawable.ic_forward, FORWARD)
)
mediaSessionConnector.setPlayer(player) mediaSessionConnector.setPlayer(player)
} }
private fun handlePlayerAction(action: String) {
when (action) {
NEXT -> {
if (PlayingQueue.hasNext()) PlayingQueue.onQueueItemSelected(
PlayingQueue.currentIndex() + 1
)
}
PREV -> {
if (PlayingQueue.hasPrev()) PlayingQueue.onQueueItemSelected(
PlayingQueue.currentIndex() - 1
)
}
REWIND -> {
player.seekTo(player.currentPosition - PlayerHelper.seekIncrement)
}
FORWARD -> {
player.seekTo(player.currentPosition + PlayerHelper.seekIncrement)
}
}
}
/** /**
* Updates or creates the [playerNotification] * Updates or creates the [playerNotification]
*/ */
@ -181,21 +257,18 @@ class NowPlayingNotification(
playerNotification = PlayerNotificationManager playerNotification = PlayerNotificationManager
.Builder(context, PLAYER_NOTIFICATION_ID, BACKGROUND_CHANNEL_ID) .Builder(context, PLAYER_NOTIFICATION_ID, BACKGROUND_CHANNEL_ID)
// set the description of the notification // set the description of the notification
.setMediaDescriptionAdapter( .setMediaDescriptionAdapter(descriptionAdapter)
DescriptionAdapter() // register the receiver for custom actions, doesn't seem to change anything
) .setCustomActionReceiver(customActionReceiver)
.build() .build().apply {
playerNotification?.apply { setPlayer(player)
setPlayer(player) setColorized(true)
setUseNextAction(false) setMediaSessionToken(mediaSession.sessionToken)
setUsePreviousAction(false) setSmallIcon(R.drawable.ic_launcher_lockscreen)
setUseStopAction(true) setUseNextAction(false)
setColorized(true) setUsePreviousAction(false)
setMediaSessionToken(mediaSession.sessionToken) setUseStopAction(true)
setSmallIcon(R.drawable.ic_launcher_lockscreen) }
setUseFastForwardActionInCompactView(true)
setUseRewindActionInCompactView(true)
}
} }
/** /**
@ -215,4 +288,11 @@ class NowPlayingNotification(
) as NotificationManager ) as NotificationManager
notificationManager.cancel(PLAYER_NOTIFICATION_ID) notificationManager.cancel(PLAYER_NOTIFICATION_ID)
} }
companion object {
private const val PREV = "prev"
private const val NEXT = "next"
private const val REWIND = "rewind"
private const val FORWARD = "forward"
}
} }