From 7c0b787bdb81647b4907fb348b72b024114cf9fc Mon Sep 17 00:00:00 2001 From: bachinger Date: Thu, 9 Jun 2022 15:36:13 +0000 Subject: [PATCH] Add session URI to Intent used with the notification The service handles three different types of `Intents`. Custom command and media command Intents created by the library and media button event Intents from other sources. Media commands from the library as well as from external sources have the action set to `android.intent.action.MEDIA_BUTTON`. If the data URI is set and can be used to identify a session then it is a library Intent. If the Intent is coming from an external KeyEvent, the service implementation is asked which session to use by calling `onGetSession(controllerInfo)` with the controller info being an anonymous legacy controller info. Intents representing a custom command are always coming from the library and hence always have a data URI. Issue: androidx/media#82 PiperOrigin-RevId: 453932972 (cherry picked from commit 8b592fc77aeead345adac999eda27da55df0ae01) --- .../media3/session/DefaultActionFactory.java | 26 ++++--- .../DefaultMediaNotificationProvider.java | 13 +++- .../media3/session/MediaNotification.java | 20 ++++-- .../media3/session/MediaSessionService.java | 10 +-- .../session/DefaultActionFactoryTest.java | 27 ++++++-- .../DefaultMediaNotificationProviderTest.java | 69 +++++++++++++++---- 6 files changed, 123 insertions(+), 42 deletions(-) diff --git a/libraries/session/src/main/java/androidx/media3/session/DefaultActionFactory.java b/libraries/session/src/main/java/androidx/media3/session/DefaultActionFactory.java index cf63e6cc4b..699f9cb991 100644 --- a/libraries/session/src/main/java/androidx/media3/session/DefaultActionFactory.java +++ b/libraries/session/src/main/java/androidx/media3/session/DefaultActionFactory.java @@ -66,20 +66,25 @@ import androidx.media3.common.util.Util; @Override public NotificationCompat.Action createMediaAction( - IconCompat icon, CharSequence title, @Player.Command int command) { - return new NotificationCompat.Action(icon, title, createMediaActionPendingIntent(command)); + MediaSession mediaSession, IconCompat icon, CharSequence title, @Player.Command int command) { + return new NotificationCompat.Action( + icon, title, createMediaActionPendingIntent(mediaSession, command)); } @Override public NotificationCompat.Action createCustomAction( - IconCompat icon, CharSequence title, String customAction, Bundle extras) { + MediaSession mediaSession, + IconCompat icon, + CharSequence title, + String customAction, + Bundle extras) { return new NotificationCompat.Action( - icon, title, createCustomActionPendingIntent(customAction, extras)); + icon, title, createCustomActionPendingIntent(mediaSession, customAction, extras)); } @Override public NotificationCompat.Action createCustomActionFromCustomCommandButton( - CommandButton customCommandButton) { + MediaSession mediaSession, CommandButton customCommandButton) { checkArgument( customCommandButton.sessionCommand != null && customCommandButton.sessionCommand.commandCode @@ -88,13 +93,16 @@ import androidx.media3.common.util.Util; return new NotificationCompat.Action( IconCompat.createWithResource(service, customCommandButton.iconResId), customCommandButton.displayName, - createCustomActionPendingIntent(customCommand.customAction, customCommand.customExtras)); + createCustomActionPendingIntent( + mediaSession, customCommand.customAction, customCommand.customExtras)); } @Override - public PendingIntent createMediaActionPendingIntent(@Player.Command long command) { + public PendingIntent createMediaActionPendingIntent( + MediaSession mediaSession, @Player.Command long command) { int keyCode = toKeyCode(command); Intent intent = new Intent(Intent.ACTION_MEDIA_BUTTON); + intent.setData(mediaSession.getImpl().getUri()); intent.setComponent(new ComponentName(service, service.getClass())); intent.putExtra(Intent.EXTRA_KEY_EVENT, new KeyEvent(KeyEvent.ACTION_DOWN, keyCode)); if (Util.SDK_INT >= 26 && command == COMMAND_PLAY_PAUSE) { @@ -126,8 +134,10 @@ import androidx.media3.common.util.Util; return KEYCODE_UNKNOWN; } - private PendingIntent createCustomActionPendingIntent(String action, Bundle extras) { + private PendingIntent createCustomActionPendingIntent( + MediaSession mediaSession, String action, Bundle extras) { Intent intent = new Intent(ACTION_CUSTOM); + intent.setData(mediaSession.getImpl().getUri()); intent.setComponent(new ComponentName(service, service.getClass())); intent.putExtra(EXTRAS_KEY_ACTION_CUSTOM, action); intent.putExtra(EXTRAS_KEY_ACTION_CUSTOM_EXTRAS, extras); 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 dc88c45510..992a6fdad0 100644 --- a/libraries/session/src/main/java/androidx/media3/session/DefaultMediaNotificationProvider.java +++ b/libraries/session/src/main/java/androidx/media3/session/DefaultMediaNotificationProvider.java @@ -140,6 +140,7 @@ public class DefaultMediaNotificationProvider implements MediaNotification.Provi MediaStyle mediaStyle = new MediaStyle(); int[] compactViewIndices = addNotificationActions( + mediaSession, getMediaButtons(player.getAvailableCommands(), customLayout, player.getPlayWhenReady()), builder, actionFactory); @@ -173,7 +174,8 @@ public class DefaultMediaNotificationProvider implements MediaNotification.Provi if (player.isCommandAvailable(COMMAND_STOP) || Util.SDK_INT < 21) { // We must include a cancel intent for pre-L devices. - mediaStyle.setCancelButtonIntent(actionFactory.createMediaActionPendingIntent(COMMAND_STOP)); + mediaStyle.setCancelButtonIntent( + actionFactory.createMediaActionPendingIntent(mediaSession, COMMAND_STOP)); } long playbackStartTimeMs = getPlaybackStartTimeEpochMs(player); @@ -186,7 +188,8 @@ public class DefaultMediaNotificationProvider implements MediaNotification.Provi Notification notification = builder .setContentIntent(mediaSession.getSessionActivity()) - .setDeleteIntent(actionFactory.createMediaActionPendingIntent(COMMAND_STOP)) + .setDeleteIntent( + actionFactory.createMediaActionPendingIntent(mediaSession, COMMAND_STOP)) .setOnlyAlertOnce(true) .setSmallIcon(R.drawable.media3_notification_small_icon) .setStyle(mediaStyle) @@ -292,6 +295,7 @@ public class DefaultMediaNotificationProvider implements MediaNotification.Provi * buttons are marked with {@link DefaultMediaNotificationProvider#COMMAND_KEY_COMPACT_VIEW_INDEX} * to declare the index in compact view of the given command button in the button extras. * + * @param mediaSession The media session to which the actions will be sent. * @param mediaButtons The command buttons to be included in the notification. * @param builder The builder to add the actions to. * @param actionFactory The actions factory to be used to build notifications. @@ -300,6 +304,7 @@ public class DefaultMediaNotificationProvider implements MediaNotification.Provi * notification}. */ protected int[] addNotificationActions( + MediaSession mediaSession, List mediaButtons, NotificationCompat.Builder builder, MediaNotification.ActionFactory actionFactory) { @@ -309,11 +314,13 @@ public class DefaultMediaNotificationProvider implements MediaNotification.Provi for (int i = 0; i < mediaButtons.size(); i++) { CommandButton commandButton = mediaButtons.get(i); if (commandButton.sessionCommand != null) { - builder.addAction(actionFactory.createCustomActionFromCustomCommandButton(commandButton)); + builder.addAction( + actionFactory.createCustomActionFromCustomCommandButton(mediaSession, commandButton)); } else { checkState(commandButton.playerCommand != COMMAND_INVALID); builder.addAction( actionFactory.createMediaAction( + mediaSession, IconCompat.createWithResource(context, commandButton.iconResId), commandButton.displayName, commandButton.playerCommand)); diff --git a/libraries/session/src/main/java/androidx/media3/session/MediaNotification.java b/libraries/session/src/main/java/androidx/media3/session/MediaNotification.java index c9c39af9bc..14064cfc55 100644 --- a/libraries/session/src/main/java/androidx/media3/session/MediaNotification.java +++ b/libraries/session/src/main/java/androidx/media3/session/MediaNotification.java @@ -43,12 +43,16 @@ public final class MediaNotification { * Creates a {@link NotificationCompat.Action} for a notification. These actions will be handled * by the library. * + * @param mediaSession The media session to which the action will be sent. * @param icon The icon to show for this action. * @param title The title of the action. * @param command A command to send when users trigger this action. */ NotificationCompat.Action createMediaAction( - IconCompat icon, CharSequence title, @Player.Command int command); + MediaSession mediaSession, + IconCompat icon, + CharSequence title, + @Player.Command int command); /** * Creates a {@link NotificationCompat.Action} for a notification with a custom action. Actions @@ -56,6 +60,7 @@ public final class MediaNotification { * to the {@linkplain MediaNotification.Provider#handleCustomCommand notification provider} that * provided them. * + * @param mediaSession The media session to which the action will be sent. * @param icon The icon to show for this action. * @param title The title of the action. * @param customAction The custom action set. @@ -63,7 +68,11 @@ public final class MediaNotification { * @see MediaNotification.Provider#handleCustomCommand */ NotificationCompat.Action createCustomAction( - IconCompat icon, CharSequence title, String customAction, Bundle extras); + MediaSession mediaSession, + IconCompat icon, + CharSequence title, + String customAction, + Bundle extras); /** * Creates a {@link NotificationCompat.Action} for a notification from a custom command button. @@ -76,18 +85,21 @@ public final class MediaNotification { * SessionCommand#customExtras command's extras} will be passed to {@link * Provider#handleCustomCommand(MediaSession, String, Bundle)} when the action is executed. * + * @param mediaSession The media session to which the action will be sent. * @param customCommandButton A {@linkplain CommandButton custom command button}. * @see MediaNotification.Provider#handleCustomCommand */ NotificationCompat.Action createCustomActionFromCustomCommandButton( - CommandButton customCommandButton); + MediaSession mediaSession, CommandButton customCommandButton); /** * Creates a {@link PendingIntent} for a media action that will be handled by the library. * + * @param mediaSession The media session to which the action will be sent. * @param command The intent's command. */ - PendingIntent createMediaActionPendingIntent(@Player.Command long command); + PendingIntent createMediaActionPendingIntent( + MediaSession mediaSession, @Player.Command long command); } /** diff --git a/libraries/session/src/main/java/androidx/media3/session/MediaSessionService.java b/libraries/session/src/main/java/androidx/media3/session/MediaSessionService.java index d44c3d0da1..680c8834b9 100644 --- a/libraries/session/src/main/java/androidx/media3/session/MediaSessionService.java +++ b/libraries/session/src/main/java/androidx/media3/session/MediaSessionService.java @@ -354,19 +354,11 @@ public abstract class MediaSessionService extends Service { if (keyEvent != null) { session.getSessionCompat().getController().dispatchMediaButtonEvent(keyEvent); } - } else if (actionFactory.isCustomAction(intent)) { + } else if (session != null && actionFactory.isCustomAction(intent)) { @Nullable String customAction = actionFactory.getCustomAction(intent); if (customAction == null) { return START_STICKY; } - if (session == null) { - ControllerInfo controllerInfo = ControllerInfo.createLegacyControllerInfo(); - session = onGetSession(controllerInfo); - if (session == null) { - return START_STICKY; - } - addSession(session); - } Bundle customExtras = actionFactory.getCustomActionExtras(intent); getMediaNotificationManager().onCustomAction(session, customAction, customExtras); } diff --git a/libraries/session/src/test/java/androidx/media3/session/DefaultActionFactoryTest.java b/libraries/session/src/test/java/androidx/media3/session/DefaultActionFactoryTest.java index fc7f5b1402..73c68ce21f 100644 --- a/libraries/session/src/test/java/androidx/media3/session/DefaultActionFactoryTest.java +++ b/libraries/session/src/test/java/androidx/media3/session/DefaultActionFactoryTest.java @@ -16,10 +16,13 @@ package androidx.media3.session; import static com.google.common.truth.Truth.assertThat; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; import static org.robolectric.Shadows.shadowOf; import android.app.PendingIntent; import android.content.Intent; +import android.net.Uri; import android.os.Bundle; import androidx.annotation.Nullable; import androidx.core.app.NotificationCompat; @@ -39,12 +42,18 @@ public class DefaultActionFactoryTest { public void createMediaPendingIntent_intentIsMediaAction() { DefaultActionFactory actionFactory = new DefaultActionFactory(Robolectric.setupService(TestService.class)); + MediaSession mockMediaSession = mock(MediaSession.class); + MediaSessionImpl mockMediaSessionImpl = mock(MediaSessionImpl.class); + when(mockMediaSession.getImpl()).thenReturn(mockMediaSessionImpl); + Uri dataUri = Uri.parse("http://example.com"); + when(mockMediaSessionImpl.getUri()).thenReturn(dataUri); PendingIntent pendingIntent = - actionFactory.createMediaActionPendingIntent(Player.COMMAND_PLAY_PAUSE); + actionFactory.createMediaActionPendingIntent(mockMediaSession, Player.COMMAND_PLAY_PAUSE); ShadowPendingIntent shadowPendingIntent = shadowOf(pendingIntent); assertThat(actionFactory.isMediaAction(shadowPendingIntent.getSavedIntent())).isTrue(); + assertThat(shadowPendingIntent.getSavedIntent().getData()).isEqualTo(dataUri); } @Test @@ -71,7 +80,11 @@ public class DefaultActionFactoryTest { public void createCustomActionFromCustomCommandButton() { DefaultActionFactory actionFactory = new DefaultActionFactory(Robolectric.setupService(TestService.class)); - + MediaSession mockMediaSession = mock(MediaSession.class); + MediaSessionImpl mockMediaSessionImpl = mock(MediaSessionImpl.class); + when(mockMediaSession.getImpl()).thenReturn(mockMediaSessionImpl); + Uri dataUri = Uri.parse("http://example.com"); + when(mockMediaSessionImpl.getUri()).thenReturn(dataUri); Bundle commandBundle = new Bundle(); commandBundle.putString("command-key", "command-value"); Bundle buttonBundle = new Bundle(); @@ -85,8 +98,11 @@ public class DefaultActionFactoryTest { .build(); NotificationCompat.Action notificationAction = - actionFactory.createCustomActionFromCustomCommandButton(customSessionCommand); + actionFactory.createCustomActionFromCustomCommandButton( + mockMediaSession, customSessionCommand); + ShadowPendingIntent shadowPendingIntent = shadowOf(notificationAction.actionIntent); + assertThat(shadowPendingIntent.getSavedIntent().getData()).isEqualTo(dataUri); assertThat(String.valueOf(notificationAction.title)).isEqualTo("name"); assertThat(notificationAction.getIconCompat().getResId()) .isEqualTo(R.drawable.media3_notification_pause); @@ -99,7 +115,6 @@ public class DefaultActionFactoryTest { createCustomActionFromCustomCommandButton_notACustomAction_throwsIllegalArgumentException() { DefaultActionFactory actionFactory = new DefaultActionFactory(Robolectric.setupService(TestService.class)); - CommandButton customSessionCommand = new CommandButton.Builder() .setPlayerCommand(Player.COMMAND_PLAY_PAUSE) @@ -109,7 +124,9 @@ public class DefaultActionFactoryTest { Assert.assertThrows( IllegalArgumentException.class, - () -> actionFactory.createCustomActionFromCustomCommandButton(customSessionCommand)); + () -> + actionFactory.createCustomActionFromCustomCommandButton( + mock(MediaSession.class), customSessionCommand)); } /** A test service for unit tests. */ 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 de56133863..4cca95092b 100644 --- a/libraries/session/src/test/java/androidx/media3/session/DefaultMediaNotificationProviderTest.java +++ b/libraries/session/src/test/java/androidx/media3/session/DefaultMediaNotificationProviderTest.java @@ -22,7 +22,9 @@ import static org.mockito.Mockito.mock; import static org.mockito.Mockito.times; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.verifyNoMoreInteractions; +import static org.mockito.Mockito.when; +import android.net.Uri; import android.os.Bundle; import androidx.annotation.Nullable; import androidx.core.app.NotificationCompat; @@ -119,6 +121,7 @@ public class DefaultMediaNotificationProviderTest { new DefaultMediaNotificationProvider(ApplicationProvider.getApplicationContext()); NotificationCompat.Builder mockNotificationBuilder = mock(NotificationCompat.Builder.class); MediaNotification.ActionFactory mockActionFactory = mock(MediaNotification.ActionFactory.class); + MediaSession mockMediaSession = mock(MediaSession.class); CommandButton commandButton1 = new CommandButton.Builder() .setPlayerCommand(Player.COMMAND_PLAY_PAUSE) @@ -155,6 +158,7 @@ public class DefaultMediaNotificationProviderTest { int[] compactViewIndices = defaultMediaNotificationProvider.addNotificationActions( + mockMediaSession, ImmutableList.of(commandButton1, commandButton2, commandButton3, commandButton4), mockNotificationBuilder, mockActionFactory); @@ -163,10 +167,17 @@ public class DefaultMediaNotificationProviderTest { InOrder inOrder = Mockito.inOrder(mockActionFactory); inOrder .verify(mockActionFactory) - .createMediaAction(any(), eq("displayName"), eq(commandButton1.playerCommand)); - inOrder.verify(mockActionFactory).createCustomActionFromCustomCommandButton(commandButton2); - inOrder.verify(mockActionFactory).createCustomActionFromCustomCommandButton(commandButton3); - inOrder.verify(mockActionFactory).createCustomActionFromCustomCommandButton(commandButton4); + .createMediaAction( + eq(mockMediaSession), any(), eq("displayName"), eq(commandButton1.playerCommand)); + inOrder + .verify(mockActionFactory) + .createCustomActionFromCustomCommandButton(mockMediaSession, commandButton2); + inOrder + .verify(mockActionFactory) + .createCustomActionFromCustomCommandButton(mockMediaSession, commandButton3); + inOrder + .verify(mockActionFactory) + .createCustomActionFromCustomCommandButton(mockMediaSession, commandButton4); verifyNoMoreInteractions(mockActionFactory); assertThat(compactViewIndices).asList().containsExactly(1, 3, 2).inOrder(); } @@ -177,6 +188,7 @@ public class DefaultMediaNotificationProviderTest { new DefaultMediaNotificationProvider(ApplicationProvider.getApplicationContext()); NotificationCompat.Builder mockNotificationBuilder = mock(NotificationCompat.Builder.class); MediaNotification.ActionFactory mockActionFactory = mock(MediaNotification.ActionFactory.class); + MediaSession mockMediaSession = mock(MediaSession.class); CommandButton commandButton1 = new CommandButton.Builder() .setDisplayName("displayName") @@ -192,6 +204,7 @@ public class DefaultMediaNotificationProviderTest { int[] compactViewIndices = defaultMediaNotificationProvider.addNotificationActions( + mockMediaSession, ImmutableList.of(commandButton1, commandButton2), mockNotificationBuilder, mockActionFactory); @@ -202,10 +215,13 @@ public class DefaultMediaNotificationProviderTest { List actions = actionCaptor.getAllValues(); assertThat(actions).hasSize(2); InOrder inOrder = Mockito.inOrder(mockActionFactory); - inOrder.verify(mockActionFactory).createCustomActionFromCustomCommandButton(commandButton1); inOrder .verify(mockActionFactory) - .createMediaAction(any(), eq("displayName"), eq(commandButton2.playerCommand)); + .createCustomActionFromCustomCommandButton(mockMediaSession, commandButton1); + inOrder + .verify(mockActionFactory) + .createMediaAction( + eq(mockMediaSession), any(), eq("displayName"), eq(commandButton2.playerCommand)); verifyNoMoreInteractions(mockActionFactory); assertThat(compactViewIndices).asList().containsExactly(1); } @@ -217,6 +233,7 @@ public class DefaultMediaNotificationProviderTest { new DefaultMediaNotificationProvider(ApplicationProvider.getApplicationContext()); NotificationCompat.Builder mockNotificationBuilder = mock(NotificationCompat.Builder.class); MediaNotification.ActionFactory mockActionFactory = mock(MediaNotification.ActionFactory.class); + MediaSession mockMediaSession = mock(MediaSession.class); CommandButton commandButton1 = new CommandButton.Builder() .setDisplayName("displayName") @@ -226,10 +243,15 @@ public class DefaultMediaNotificationProviderTest { int[] compactViewIndices = defaultMediaNotificationProvider.addNotificationActions( - ImmutableList.of(commandButton1), mockNotificationBuilder, mockActionFactory); + mockMediaSession, + ImmutableList.of(commandButton1), + mockNotificationBuilder, + mockActionFactory); InOrder inOrder = Mockito.inOrder(mockActionFactory); - inOrder.verify(mockActionFactory).createCustomActionFromCustomCommandButton(commandButton1); + inOrder + .verify(mockActionFactory) + .createCustomActionFromCustomCommandButton(mockMediaSession, commandButton1); verifyNoMoreInteractions(mockActionFactory); assertThat(compactViewIndices).asList().isEmpty(); } @@ -240,6 +262,7 @@ public class DefaultMediaNotificationProviderTest { new DefaultMediaNotificationProvider(ApplicationProvider.getApplicationContext()); NotificationCompat.Builder mockNotificationBuilder = mock(NotificationCompat.Builder.class); MediaNotification.ActionFactory mockActionFactory = mock(MediaNotification.ActionFactory.class); + MediaSession mockMediaSession = mock(MediaSession.class); Bundle commandButtonBundle1 = new Bundle(); commandButtonBundle1.putInt(DefaultMediaNotificationProvider.COMMAND_KEY_COMPACT_VIEW_INDEX, 2); CommandButton commandButton1 = @@ -262,13 +285,18 @@ public class DefaultMediaNotificationProviderTest { int[] compactViewIndices = defaultMediaNotificationProvider.addNotificationActions( + mockMediaSession, ImmutableList.of(commandButton1, commandButton2), mockNotificationBuilder, mockActionFactory); InOrder inOrder = Mockito.inOrder(mockActionFactory); - inOrder.verify(mockActionFactory).createCustomActionFromCustomCommandButton(commandButton1); - inOrder.verify(mockActionFactory).createCustomActionFromCustomCommandButton(commandButton2); + inOrder + .verify(mockActionFactory) + .createCustomActionFromCustomCommandButton(mockMediaSession, commandButton1); + inOrder + .verify(mockActionFactory) + .createCustomActionFromCustomCommandButton(mockMediaSession, commandButton2); verifyNoMoreInteractions(mockActionFactory); assertThat(compactViewIndices).asList().isEmpty(); } @@ -279,6 +307,7 @@ public class DefaultMediaNotificationProviderTest { new DefaultMediaNotificationProvider(ApplicationProvider.getApplicationContext()); NotificationCompat.Builder mockNotificationBuilder = mock(NotificationCompat.Builder.class); MediaNotification.ActionFactory mockActionFactory = mock(MediaNotification.ActionFactory.class); + MediaSession mockMediaSession = mock(MediaSession.class); Bundle commandButtonBundle = new Bundle(); commandButtonBundle.putInt(DefaultMediaNotificationProvider.COMMAND_KEY_COMPACT_VIEW_INDEX, 1); CommandButton commandButton1 = @@ -291,10 +320,15 @@ public class DefaultMediaNotificationProviderTest { int[] compactViewIndices = defaultMediaNotificationProvider.addNotificationActions( - ImmutableList.of(commandButton1), mockNotificationBuilder, mockActionFactory); + mockMediaSession, + ImmutableList.of(commandButton1), + mockNotificationBuilder, + mockActionFactory); InOrder inOrder = Mockito.inOrder(mockActionFactory); - inOrder.verify(mockActionFactory).createCustomActionFromCustomCommandButton(commandButton1); + inOrder + .verify(mockActionFactory) + .createCustomActionFromCustomCommandButton(mockMediaSession, commandButton1); verifyNoMoreInteractions(mockActionFactory); // [INDEX_UNSET, 1, INDEX_UNSET] cropped up to the first INDEX_UNSET value assertThat(compactViewIndices).asList().isEmpty(); @@ -307,6 +341,10 @@ public class DefaultMediaNotificationProviderTest { NotificationCompat.Builder mockNotificationBuilder = mock(NotificationCompat.Builder.class); DefaultActionFactory defaultActionFactory = new DefaultActionFactory(Robolectric.setupService(TestService.class)); + MediaSession mockMediaSession = mock(MediaSession.class); + MediaSessionImpl mockMediaSessionImpl = mock(MediaSessionImpl.class); + when(mockMediaSession.getImpl()).thenReturn(mockMediaSessionImpl); + when(mockMediaSessionImpl.getUri()).thenReturn(Uri.parse("http://example.com")); Bundle commandButtonBundle = new Bundle(); commandButtonBundle.putString("testKey", "testValue"); CommandButton commandButton1 = @@ -318,12 +356,17 @@ public class DefaultMediaNotificationProviderTest { .build(); defaultMediaNotificationProvider.addNotificationActions( - ImmutableList.of(commandButton1), mockNotificationBuilder, defaultActionFactory); + mockMediaSession, + ImmutableList.of(commandButton1), + mockNotificationBuilder, + defaultActionFactory); ArgumentCaptor actionCaptor = ArgumentCaptor.forClass(NotificationCompat.Action.class); verify(mockNotificationBuilder).addAction(actionCaptor.capture()); verifyNoMoreInteractions(mockNotificationBuilder); + verify(mockMediaSessionImpl).getUri(); + verifyNoMoreInteractions(mockMediaSessionImpl); List actions = actionCaptor.getAllValues(); assertThat(actions).hasSize(1); assertThat(String.valueOf(actions.get(0).title)).isEqualTo("displayName1");