Android Notification MediaStyle Ignores Ongoing Flag

2.3k views Asked by At

I am attempting to employ a MediaStyle notification in an audio Android application as discussed in the audio app documentation as well as in the media style documentation. I am able to successfully show a notification with functional transport controls, so I believe my implementation to be sound. However, when I set the androidx.media.app.NotificationCompat.MediaStyle via the .setStyle() of the NotificationCompat.Builder as recommended, it appears to ignore any usage of the .setOngoing() flag. setOngoing ensures "notifications cannot be dismissed" according to the source code. I experience the described behavior when no media style is set but not when I use .setStyle().

I am wondering if anyone is aware of a workaround or if there is some undocumented requirement I need to appease to use .setOngoing() with a media style notification. My compile SDK version, target SDK version, and min SDK version are all 30. Please let me know if any additional code would be useful; I believe I am providing what is relevant.

To illustrate this predicament, I've included screenshots showing how the notification is dismissible when the media style is applied and is not dismissible when the media style is not applied.

With media style: with media style

Without media style: without media style

Dependencies (all up to date):

val appCompatVersion: String = "1.4.0-alpha01"
val mediaVersion: String = "1.4.0-alpha01"
val media2Version: String = "1.0.0-alpha04"

implementation("androidx.appcompat:appcompat:$appCompatVersion")
implementation("androidx.media:media:$mediaVersion")
implementation("androidx.media2:media2:$media2Version")
private val notification: Notification?
        get() {
            val controller: MediaControllerCompat = mediaSession.controller ?: return null
            val description: MediaDescriptionCompat = controller.metadata?.description ?: return null
            val notificationManager: NotificationManager = notificationManager ?: return null

            val notificationChannel = NotificationChannel(
                CHANNEL_ID,
                CHANNEL_NAME,
                NotificationManager.IMPORTANCE_NONE
            )

            if (notificationChannel !in notificationManager.notificationChannels) {
                notificationManager.createNotificationChannel(notificationChannel)
            }

            ...

            val style = androidx.media.app.NotificationCompat.MediaStyle()
                .setMediaSession(controller.sessionToken)
                .setShowActionsInCompactView(0, 1, 2)
                .setShowCancelButton(false)

            return NotificationCompat.Builder(
                this,
                CHANNEL_ID
            ).apply {
                actions.forEach { addAction(it) }
                color = backgroundColor
            }
                .setContentTitle(description.title)
                .setContentText(description.subtitle)

                .setSmallIcon(smallIcon)
                .setLargeIcon(largeIcon)

                .setOngoing(true)
                .setColorized(true)
                .setAutoCancel(false)
                .setAllowSystemGeneratedContextualActions(true)

                .setContentIntent(controller.sessionActivity)
                .setVisibility(NotificationCompat.VISIBILITY_PUBLIC)

                .setChannelId(CHANNEL_ID)
                .setStyle(style)
                .build()
        }
2

There are 2 answers

0
buggily On

Android notifications sure can be mysterious, and I am not entirely sure why my application behaves differently than others (Music Player GO, UAMP) with the same approach. I'd guess the discrepancy is due to package versions or imports because even trying to import MediaStyle is quite a mess considering the media libraries were never fully ported to androidx. I've found, while the media style notification is initially dismissible via swipe, it still persists in the extended menu as an extended notification. I'm fine with this because the notification is accessible somewhere. However, I wish the user could toggle its placement in the notification pane because swiping seems like an arbitrary action for sequestering it to the extended notification pane. I also found this behavior only occurs due to the .setMediaSession() method of androidx.media.app.NotificationCompat.MediaStyle. If I don't set the media session of the media style, I get somewhat of a hybrid of a traditional ongoing notification style and the modern media notification style. I'll provide a matrix of different notifications styles I've encountered while trying to debug this.

without media style (not dismissible): without media style (not dismissible)

with media style and without setting the media session (not dismissible): with media style and without setting the media session (not dismissible)

with media style and with setting the media session (dismissible): with media style and with setting the media session (dismissible)

with media style and with setting the media session on dismiss: with media style and with setting the media session on dismiss

0
Rene On

I was facing the same issue. After setting the setPlaybackState to the mediasession it was fixed for me.

mediaSession.setPlaybackState(
    PlaybackStateCompat.Builder()
        .setState(PlaybackStateCompat.STATE_PLAYING, 0, 1f)
        .build()
)

Check this link for the correct parameters for setState. https://developer.android.com/reference/kotlin/android/support/v4/media/session/PlaybackStateCompat.Builder

My full mediasession below. Make sure to set the correct parameter values for your audio like duration etc..

private fun createMediaSession(): MediaSessionCompat {
    val mediaSession = MediaSessionCompat(this, "tag")

    mediaSession.setMetadata(MediaMetadataCompat.Builder()
        .putLong(MediaMetadata.METADATA_KEY_DURATION, -1L)
        .build())

    mediaSession.setRepeatMode(PlaybackStateCompat.REPEAT_MODE_ONE)
    mediaSession.setFlags(0)

    mediaSession.setPlaybackState(
        PlaybackStateCompat.Builder()
            .setState(PlaybackStateCompat.STATE_PLAYING, 0, 1f)
            .build()
    )

    return mediaSession
}