diff --git a/libraries/session/src/main/java/androidx/media3/session/DefaultMediaNotificationProvider.java b/libraries/session/src/main/java/androidx/media3/session/DefaultMediaNotificationProvider.java index d6b15c0151..29639baff4 100644 --- a/libraries/session/src/main/java/androidx/media3/session/DefaultMediaNotificationProvider.java +++ b/libraries/session/src/main/java/androidx/media3/session/DefaultMediaNotificationProvider.java @@ -119,7 +119,7 @@ public class DefaultMediaNotificationProvider implements MediaNotification.Provi /** A builder for {@link DefaultMediaNotificationProvider} instances. */ public static final class Builder { private final Context context; - private int notificationId; + private NotificationIdProvider notificationIdProvider; private String channelId; @StringRes private int channelNameResourceId; private BitmapLoader bitmapLoader; @@ -132,7 +132,7 @@ public class DefaultMediaNotificationProvider implements MediaNotification.Provi */ public Builder(Context context) { this.context = context; - notificationId = DEFAULT_NOTIFICATION_ID; + notificationIdProvider = session -> DEFAULT_NOTIFICATION_ID; channelId = DEFAULT_CHANNEL_ID; channelNameResourceId = DEFAULT_CHANNEL_NAME_RESOURCE_ID; bitmapLoader = new SimpleBitmapLoader(); @@ -142,12 +142,30 @@ public class DefaultMediaNotificationProvider implements MediaNotification.Provi * Sets the {@link MediaNotification#notificationId} used for the created notifications. By * default, this is set to {@link #DEFAULT_NOTIFICATION_ID}. * + *

Overwrites anything set in {@link #setNotificationIdProvider(NotificationIdProvider)}. + * * @param notificationId The notification ID. * @return This builder. */ @CanIgnoreReturnValue public Builder setNotificationId(int notificationId) { - this.notificationId = notificationId; + this.notificationIdProvider = session -> notificationId; + return this; + } + + /** + * Sets the provider for the {@link MediaNotification#notificationId} used for the created + * notifications. By default, this is set to a provider that always returns {@link + * #DEFAULT_NOTIFICATION_ID}. + * + *

Overwrites anything set in {@link #setNotificationId(int)}. + * + * @param notificationIdProvider The notification ID provider. + * @return This builder. + */ + @CanIgnoreReturnValue + public Builder setNotificationIdProvider(NotificationIdProvider notificationIdProvider) { + this.notificationIdProvider = notificationIdProvider; return this; } @@ -201,6 +219,16 @@ public class DefaultMediaNotificationProvider implements MediaNotification.Provi } } + /** + * Provides notification IDs for posting media notifications for given media sessions. + * + * @see Builder#setNotificationIdProvider(NotificationIdProvider) + */ + public interface NotificationIdProvider { + /** Returns the notification ID for the media notification of the given session. */ + int getNotificationId(MediaSession mediaSession); + } + /** * An extras key that can be used to define the index of a {@link CommandButton} in {@linkplain * Notification.MediaStyle#setShowActionsInCompactView(int...) compact view}. @@ -226,7 +254,7 @@ public class DefaultMediaNotificationProvider implements MediaNotification.Provi private static final String TAG = "NotificationProvider"; private final Context context; - private final int notificationId; + private final NotificationIdProvider notificationIdProvider; private final String channelId; @StringRes private final int channelNameResourceId; private final NotificationManager notificationManager; @@ -241,7 +269,7 @@ public class DefaultMediaNotificationProvider implements MediaNotification.Provi private DefaultMediaNotificationProvider(Builder builder) { this.context = builder.context; - this.notificationId = builder.notificationId; + this.notificationIdProvider = builder.notificationIdProvider; this.channelId = builder.channelId; this.channelNameResourceId = builder.channelNameResourceId; this.bitmapLoader = builder.bitmapLoader; @@ -265,6 +293,7 @@ public class DefaultMediaNotificationProvider implements MediaNotification.Provi Player player = mediaSession.getPlayer(); NotificationCompat.Builder builder = new NotificationCompat.Builder(context, channelId); + int notificationId = notificationIdProvider.getNotificationId(mediaSession); MediaStyle mediaStyle = new MediaStyle(); int[] compactViewIndices = diff --git a/libraries/session/src/test/java/androidx/media3/session/DefaultMediaNotificationProviderTest.java b/libraries/session/src/test/java/androidx/media3/session/DefaultMediaNotificationProviderTest.java index a97777e3be..4b444e29bd 100644 --- a/libraries/session/src/test/java/androidx/media3/session/DefaultMediaNotificationProviderTest.java +++ b/libraries/session/src/test/java/androidx/media3/session/DefaultMediaNotificationProviderTest.java @@ -15,6 +15,9 @@ */ package androidx.media3.session; +import static androidx.media3.common.util.Assertions.checkNotNull; +import static androidx.media3.session.DefaultMediaNotificationProvider.DEFAULT_CHANNEL_ID; +import static androidx.media3.session.DefaultMediaNotificationProvider.DEFAULT_NOTIFICATION_ID; import static com.google.common.truth.Truth.assertThat; import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.eq; @@ -449,6 +452,33 @@ public class DefaultMediaNotificationProviderTest { verifyNoInteractions(mockOnNotificationChangedCallback1); } + @Test + public void provider_idsNotSpecified_usesDefaultIds() { + Context context = ApplicationProvider.getApplicationContext(); + DefaultMediaNotificationProvider defaultMediaNotificationProvider = + new DefaultMediaNotificationProvider.Builder(context).build(); + MediaSession mockMediaSession = createMockMediaSessionForNotification(MediaMetadata.EMPTY); + DefaultActionFactory defaultActionFactory = + new DefaultActionFactory(Robolectric.setupService(TestService.class)); + + MediaNotification notification = + defaultMediaNotificationProvider.createNotification( + mockMediaSession, + ImmutableList.of(), + defaultActionFactory, + mock(MediaNotification.Provider.Callback.class)); + + assertThat(notification.notificationId).isEqualTo(DEFAULT_NOTIFICATION_ID); + assertThat(notification.notification.getChannelId()).isEqualTo(DEFAULT_CHANNEL_ID); + ShadowNotificationManager shadowNotificationManager = + Shadows.shadowOf( + (NotificationManager) context.getSystemService(Context.NOTIFICATION_SERVICE)); + assertHasNotificationChannel( + shadowNotificationManager.getNotificationChannels(), + /* channelId= */ DEFAULT_CHANNEL_ID, + /* channelName= */ context.getString(R.string.default_notification_channel_name)); + } + @Test public void provider_withCustomIds_notificationsUseCustomIds() { Context context = ApplicationProvider.getApplicationContext(); @@ -480,6 +510,32 @@ public class DefaultMediaNotificationProviderTest { /* channelName= */ context.getString(R.string.media3_controls_play_description)); } + @Test + public void provider_withCustomNotificationIdProvider_notificationsUseCustomId() { + Context context = ApplicationProvider.getApplicationContext(); + DefaultMediaNotificationProvider defaultMediaNotificationProvider = + new DefaultMediaNotificationProvider.Builder(context) + .setNotificationIdProvider( + session -> { + checkNotNull(session); + return 3; + }) + .setChannelName(/* channelNameResourceId= */ R.string.media3_controls_play_description) + .build(); + MediaSession mockMediaSession = createMockMediaSessionForNotification(MediaMetadata.EMPTY); + DefaultActionFactory defaultActionFactory = + new DefaultActionFactory(Robolectric.setupService(TestService.class)); + + MediaNotification notification = + defaultMediaNotificationProvider.createNotification( + mockMediaSession, + ImmutableList.of(), + defaultActionFactory, + mock(MediaNotification.Provider.Callback.class)); + + assertThat(notification.notificationId).isEqualTo(3); + } + @Test public void setCustomSmallIcon_notificationUsesCustomSmallIcon() { Context context = ApplicationProvider.getApplicationContext();