From f3e450e7833bbc62237c1f24f9a1f6c4eed21460 Mon Sep 17 00:00:00 2001 From: rohks Date: Tue, 29 Nov 2022 18:18:13 +0000 Subject: [PATCH] Add public constructors to `DefaultMediaNotificationProvider` Issue: androidx/media#213 Without a public constructor, it is not possible to extend this class and override its method. PiperOrigin-RevId: 491673111 --- .../DefaultMediaNotificationProvider.java | 38 ++++++++-- .../DefaultMediaNotificationProviderTest.java | 74 +++++++++++++++++++ 2 files changed, 107 insertions(+), 5 deletions(-) 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 45de1f5a94..5cdc263033 100644 --- a/libraries/session/src/main/java/androidx/media3/session/DefaultMediaNotificationProvider.java +++ b/libraries/session/src/main/java/androidx/media3/session/DefaultMediaNotificationProvider.java @@ -249,11 +249,31 @@ public class DefaultMediaNotificationProvider implements MediaNotification.Provi private @MonotonicNonNull OnBitmapLoadedFutureCallback pendingOnBitmapLoadedFutureCallback; @DrawableRes private int smallIconResourceId; - private DefaultMediaNotificationProvider(Builder builder) { - this.context = builder.context; - this.notificationIdProvider = builder.notificationIdProvider; - this.channelId = builder.channelId; - this.channelNameResourceId = builder.channelNameResourceId; + /** + * Creates an instance. Use this constructor only when you want to override methods of this class. + * Otherwise use {@link Builder}. + */ + public DefaultMediaNotificationProvider(Context context) { + this( + context, + session -> DEFAULT_NOTIFICATION_ID, + DEFAULT_CHANNEL_ID, + DEFAULT_CHANNEL_NAME_RESOURCE_ID); + } + + /** + * Creates an instance. Use this constructor only when you want to override methods of this class. + * Otherwise use {@link Builder}. + */ + public DefaultMediaNotificationProvider( + Context context, + NotificationIdProvider notificationIdProvider, + String channelId, + int channelNameResourceId) { + this.context = context; + this.notificationIdProvider = notificationIdProvider; + this.channelId = channelId; + this.channelNameResourceId = channelNameResourceId; notificationManager = checkStateNotNull( (NotificationManager) context.getSystemService(Context.NOTIFICATION_SERVICE)); @@ -261,6 +281,14 @@ public class DefaultMediaNotificationProvider implements MediaNotification.Provi smallIconResourceId = R.drawable.media3_notification_small_icon; } + private DefaultMediaNotificationProvider(Builder builder) { + this( + builder.context, + builder.notificationIdProvider, + builder.channelId, + builder.channelNameResourceId); + } + // MediaNotification.Provider implementation @Override 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 bf23e9c894..fe7616bce3 100644 --- a/libraries/session/src/test/java/androidx/media3/session/DefaultMediaNotificationProviderTest.java +++ b/libraries/session/src/test/java/androidx/media3/session/DefaultMediaNotificationProviderTest.java @@ -628,6 +628,80 @@ public class DefaultMediaNotificationProviderTest { assertThat(isMediaMetadataArtistEqualToNotificationContentText).isTrue(); } + /** + * {@link DefaultMediaNotificationProvider} is designed to be extendable. Public constructor + * should not be removed. + */ + @Test + public void createsProviderUsingConstructor_idsNotSpecified_usesDefaultIds() { + Context context = ApplicationProvider.getApplicationContext(); + DefaultMediaNotificationProvider defaultMediaNotificationProvider = + new DefaultMediaNotificationProvider(context); + MediaSession mockMediaSession = createMockMediaSessionForNotification(MediaMetadata.EMPTY); + BitmapLoader mockBitmapLoader = mock(BitmapLoader.class); + when(mockBitmapLoader.loadBitmapFromMetadata(any())).thenReturn(null); + when(mockMediaSession.getBitmapLoader()).thenReturn(mockBitmapLoader); + DefaultActionFactory defaultActionFactory = + new DefaultActionFactory(Robolectric.setupService(TestService.class)); + + MediaNotification notification = + defaultMediaNotificationProvider.createNotification( + mockMediaSession, + /* customLayout= */ ImmutableList.of(), + defaultActionFactory, + /* onNotificationChangedCallback= */ 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)); + } + + /** + * Extends {@link DefaultMediaNotificationProvider} and overrides all known protected methods. If + * by accident we change the signature of the class in a way that affects inheritance, this test + * would no longer compile. + */ + @Test + public void overridesProviderDefinition_compilesSuccessfully() { + Context context = ApplicationProvider.getApplicationContext(); + + DefaultMediaNotificationProvider unused = + new DefaultMediaNotificationProvider(context) { + @Override + public List getMediaButtons( + Player.Commands playerCommands, + List customLayout, + boolean showPauseButton) { + return super.getMediaButtons(playerCommands, customLayout, showPauseButton); + } + + @Override + public int[] addNotificationActions( + MediaSession mediaSession, + List mediaButtons, + NotificationCompat.Builder builder, + MediaNotification.ActionFactory actionFactory) { + return super.addNotificationActions(mediaSession, mediaButtons, builder, actionFactory); + } + + @Override + public CharSequence getNotificationContentTitle(MediaMetadata metadata) { + return super.getNotificationContentTitle(metadata); + } + + @Override + public CharSequence getNotificationContentText(MediaMetadata metadata) { + return super.getNotificationContentText(metadata); + } + }; + } + private static void assertHasNotificationChannel( List notificationChannels, String channelId, String channelName) { boolean found = false;