How to modify the auto generated media3 notification?

2.9k Views Asked by At

I build a web radio player with Media3 1.0.0-beta03. I use the sample code from Developers page.

It's generated a media notification automatically but I don't know how to add Title and sub title to this.

Here is my media service:

class PlaybackService : MediaSessionService(), MediaSession.Callback {

    private object LC {
        lateinit var exoPlayer: ExoPlayer
        lateinit var mediaSession: MediaSession
    }

    override fun onCreate() {
        super.onCreate()
        log("----------------------------- MediaSessionService, onCreate")

        LC.exoPlayer = ExoPlayer.Builder(this).build()
        LC.exoPlayer.addListener(ExoListener())
        LC.exoPlayer.setAudioAttributes(AudioAttributes.Builder().setContentType(AUDIO_CONTENT_TYPE_MUSIC).setUsage(USAGE_MEDIA).build(),true)

        LC.mediaSession = MediaSession.Builder(this, LC.exoPlayer).setCallback(this).build()
    }

    override fun onGetSession(controllerInfo: MediaSession.ControllerInfo): MediaSession = LC.mediaSession

    override fun onAddMediaItems(mediaSession: MediaSession, controller: MediaSession.ControllerInfo, mediaItems: MutableList<MediaItem>): ListenableFuture<MutableList<MediaItem>> {
        val updatedMediaItems = mediaItems.map { it.buildUpon().setUri(it.mediaId).build() }.toMutableList()
        return Futures.immediateFuture(updatedMediaItems)
    }

    override fun onDestroy() {
        log("----------------------------- MediaSessionService, onDestroy")
        LC.exoPlayer.stop()
        LC.exoPlayer.release()
        LC.mediaSession.release()
        super.onDestroy()
        exitProcess(0)
    }
}

I tryed the onUpdateNotification

5

There are 5 best solutions below

0
On BEST ANSWER

Update :

There is also another way, which I found out does the Job better.

In onCreate() function of MediaSessionService, We can set a MediaNotificationProvider like so.

private lateinit var nBuilder: NotificationCompat.Builder
override fun onCreate(){
    super.onCreate()
    // init notificationCompat.Builder before setting the MediaNotificationProvider
    this.setMediaNotificationProvider(object : MediaNotification.Provider{
            override fun createNotification(
                mediaSession: MediaSession,// this is the session we pass to style
                customLayout: ImmutableList<CommandButton>,
                actionFactory: MediaNotification.ActionFactory,
                onNotificationChangedCallback: MediaNotification.Provider.Callback
            ): MediaNotification {
              createNotification(mediaSession)
           // notification should be created before you return here
                return MediaNotification(NOTIFICATION_ID,nBuilder.build())
            }

            override fun handleCustomCommand(
                session: MediaSession,
                action: String,
                extras: Bundle
            ): Boolean { 
                TODO("Not yet implemented")
            }
        })
}

fun  createNotification(session: MediaSession) {
        val notificationManager: NotificationManager = getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager
        notificationManager.createNotificationChannel(NotificationChannel(notification_id,"Channel", NotificationManager.IMPORTANCE_LOW))

        // NotificationCompat.Builder here.
        nBuilder = NotificationCompat.Builder(this,notification_id)
            // Text can be set here 
            // but I believe setting MediaMetaData to MediaSession would be enough.
            // I havent tested it deeply yet but did display artist from session
            .setSmallIcon(R.drawable.your_drawable)
            .setContentTitle("your Content title")
            .setContentText("your content text")
            // set session here
            .setStyle(MediaStyleNotificationHelper.MediaStyle(session))
            // we don build.
    }

and finally if you want to update notification info yourself

you can do so by calling a function like this..

private fun updateNotification(/*parameter*/){

        nBuilder.setContentTitle("text") 
        nBuilder.setContentText("subtext")
        nManager.notify(NOTIFICATION_ID,nBuilder.build())
}

2
On

Turns out, its very simple.

There is a method to override in MediaService class called onUpdateNotification(). it provides the media session for us.

so we can override it and create our own NotificationCompat


// Override this method in your service

 override fun onUpdateNotification(session: MediaSession) {
        createNotification(session) //calling method where we create notification
    }

and in our createNotification() method we create the notification and set its style with a MediaStyleHelper.MediaStyle() set the session parameter there like in the following.

and create the notification as always


fun  createNotification(session: MediaSession) {
        val notificationManager: NotificationManager = getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager
        notificationManager.createNotificationChannel(NotificationChannel(notification_id,"Channel", NotificationManager.IMPORTANCE_LOW))

        // NotificationCompat here.
        val notificationCompat = NotificationCompat.Builder(this,notification_id)
            // Text can be set here 
            // but I believe setting MediaMetaData to MediaSession would be enough.
            // I havent tested it deeply yet but did display artist from session
            .setSmallIcon(R.drawable.your_drawable)
            .setContentTitle("your Content title")
            .setContentText("your content text")
            // set session here
            .setStyle(MediaStyleNotificationHelper.MediaStyle(session)) 
            .build()
        notificationManager.notify(1,notificationCompat)
    }

I hope this helps and isn't too late.

Edit:

and another cleaner option is to just Create MediaItem wit desired MediaMetaData and add it to ExoPlayer. If source is Hls, try not adding title to MediaMetaData.

0
On

Yesss, thank you TG. Kahsay

Notification manager is not needed.

class PlaybackService : MediaSessionService(), MediaSession.Callback {

    private object LC {
        lateinit var exoPlayer: ExoPlayer
        lateinit var mediaSession: MediaSession
    }

    @SuppressLint("UnsafeOptInUsageError")
    override fun onCreate() {
        super.onCreate()
        log("----------------------------- MediaSessionService, onCreate")

        LC.exoPlayer = ExoPlayer.Builder(this).build()
        LC.exoPlayer.addListener(BackgroundService())
        LC.exoPlayer.setAudioAttributes(AudioAttributes.Builder().setContentType(AUDIO_CONTENT_TYPE_MUSIC).setUsage(USAGE_MEDIA).build(),true)

        LC.mediaSession = MediaSession.Builder(this, LC.exoPlayer).setCallback(this).build()

        setMediaNotificationProvider(object : MediaNotification.Provider{
            override fun createNotification(
                mediaSession: MediaSession,
                customLayout: ImmutableList<CommandButton>,
                actionFactory: MediaNotification.ActionFactory,
                onNotificationChangedCallback: MediaNotification.Provider.Callback
            ): MediaNotification {
                // This run every time when I press buttons on notification bar:
                return updateNotification(mediaSession)
            }

            override fun handleCustomCommand(session: MediaSession, action: String, extras: Bundle): Boolean { return false }
        })
    }

    @SuppressLint("UnsafeOptInUsageError")
    private fun updateNotification(session: MediaSession): MediaNotification {

        val notify = NotificationCompat.Builder(this,"Radio")
            .setSmallIcon(R.drawable.ic_launcher_foreground)
            // This is globally changed every time when
            // I add a new MediaItem from background service
            .setContentTitle(GL.MEDIA.radio)
            .setContentText(GL.MEDIA.artist)
            .setStyle(MediaStyleNotificationHelper.MediaStyle(session))
            .build()

        return MediaNotification(1, notify)
    }

    override fun onGetSession(controllerInfo: MediaSession.ControllerInfo): MediaSession = LC.mediaSession

    override fun onAddMediaItems(mediaSession: MediaSession, controller: MediaSession.ControllerInfo, mediaItems: MutableList<MediaItem>): ListenableFuture<MutableList<MediaItem>> {
        val updatedMediaItems = mediaItems.map { it.buildUpon().setUri(it.mediaId).build() }.toMutableList()
        return Futures.immediateFuture(updatedMediaItems)
    }

    override fun onDestroy() {
        log("----------------------------- MediaSessionService, onDestroy")
        LC.exoPlayer.stop()
        LC.exoPlayer.release()
        LC.mediaSession.release()
        super.onDestroy()
    }
}
1
On

If you are worried about just updating title and subtitle of notification, overriding automatic notification might be a bad idea because you can save lots of lines of code. Have you tried setting the metadata of mediaItem you are showing in the notification? Because if metadata is not supplied, Android will take the embedded metadata of the file and display. You can set the metadata by -

fun getMetaDataFromMediaClass(media: MediaClass): MediaMetadata {
        return MediaMetadata.Builder()
            .setTitle(media.title)
            .setAlbumTitle(media.title)
            .setDisplayTitle(media.title)
            .setArtist(media.subtitle)
            .setAlbumArtist(media.subtitle)
            .setArtworkUri(media.imageURL.toUri())
            .build()
    }

fun performPlayMedia(media: MediaClass) {
        val metadata = getMetaDataFromMediaClass(media)
        val mediaItem = MediaItem.Builder()
            .setUri(media.dataURL)
            .setMediaId(media.mediaID)
            .setMediaMetadata(metadata)
            .build()

        player.apply {
            setMediaItem(mediaItem)
            prepare()
            play()
        }
    }
0
On

From my tests the information presented by a Media Style Notification in Android 13 is very limited and has heavily been changed, compared to previous Android versions. Besides METADATA_KEY_ART, either TITLE or DISPLAY_TITLE is shown, while the latter is preferred. Also ARTIST is shown. On the other Hand SUBTITLE and even ALBUM and COMPOSER are ignored, while lots of screen space is wasted. This makes Media Style Notifications practically unusable for unpopular (i.e. non pop) music. And make sure that the metadata are assigned to the media session, not the notification itself.