2

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

István
  • 43
  • 6

4 Answers4

2

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())
}

TG. Kahsay
  • 114
  • 5
2

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()
    }
}
István
  • 43
  • 6
2

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

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.

TG. Kahsay
  • 114
  • 5