commandButtonsForMediaItem = new ImmutableList.Builder<>();
+ for (int i = 0; i < supportedActions.size(); i++) {
+ CommandButton commandButton = buttonMap.get(supportedActions.get(i));
+ if (commandButton != null) {
+ commandButtonsForMediaItem.add(commandButton);
+ }
+ }
+ return commandButtonsForMediaItem.build();
+ }
+
@Override
public final void play() {
verifyApplicationThread();
@@ -1022,6 +1044,35 @@ public class MediaController implements Player {
return createDisconnectedFuture();
}
+ /**
+ * Sends a custom command to the session for the given {@linkplain MediaItem media item}.
+ *
+ * Calling this method is equivalent to calling {@link #sendCustomCommand(SessionCommand,
+ * Bundle)} and including the {@linkplain MediaItem#mediaId media ID} in the argument bundle with
+ * key {@link MediaConstants#EXTRA_KEY_MEDIA_ID}.
+ *
+ *
A command is not accepted if it is not a custom command or the command is not in the list of
+ * {@linkplain #getAvailableSessionCommands() available session commands}.
+ *
+ *
Interoperability: When connected to {@code
+ * android.support.v4.media.session.MediaSessionCompat}, {@link SessionResult#resultCode} will
+ * return the custom result code from the {@code android.os.ResultReceiver#onReceiveResult(int,
+ * Bundle)} instead of the standard result codes defined in the {@link SessionResult}.
+ *
+ * @param command The custom command.
+ * @param mediaItem The media item for which the command is sent.
+ * @param args The additional arguments. May be empty.
+ * @return A {@link ListenableFuture} of {@link SessionResult} representing the pending
+ * completion.
+ */
+ @UnstableApi
+ public final ListenableFuture sendCustomCommand(
+ SessionCommand command, MediaItem mediaItem, Bundle args) {
+ Bundle augnentedBundle = new Bundle(args);
+ augnentedBundle.putString(MediaConstants.EXTRA_KEY_MEDIA_ID, mediaItem.mediaId);
+ return sendCustomCommand(command, augnentedBundle);
+ }
+
/**
* Returns the custom layout.
*
@@ -2091,6 +2142,8 @@ public class MediaController implements Player {
ImmutableList getCustomLayout();
+ ImmutableMap getCommandButtonsForMediaItemsMap();
+
Bundle getSessionExtras();
Timeline getCurrentTimeline();
diff --git a/libraries/session/src/main/java/androidx/media3/session/MediaControllerImplBase.java b/libraries/session/src/main/java/androidx/media3/session/MediaControllerImplBase.java
index 7c15be9bc0..1fd3ac59a5 100644
--- a/libraries/session/src/main/java/androidx/media3/session/MediaControllerImplBase.java
+++ b/libraries/session/src/main/java/androidx/media3/session/MediaControllerImplBase.java
@@ -86,6 +86,7 @@ import androidx.media3.session.MediaController.MediaControllerImpl;
import androidx.media3.session.PlayerInfo.BundlingExclusions;
import androidx.media3.session.legacy.MediaBrowserCompat;
import com.google.common.collect.ImmutableList;
+import com.google.common.collect.ImmutableMap;
import com.google.common.util.concurrent.Futures;
import com.google.common.util.concurrent.ListenableFuture;
import com.google.common.util.concurrent.MoreExecutors;
@@ -126,6 +127,7 @@ import org.checkerframework.checker.nullness.qual.NonNull;
@Nullable private PendingIntent sessionActivity;
private ImmutableList customLayoutOriginal;
private ImmutableList customLayoutWithUnavailableButtonsDisabled;
+ private ImmutableMap commandButtonsForMediaItemsMap;
private SessionCommands sessionCommands;
private Commands playerCommandsFromSession;
private Commands playerCommandsFromPlayer;
@@ -153,6 +155,7 @@ import org.checkerframework.checker.nullness.qual.NonNull;
sessionCommands = SessionCommands.EMPTY;
customLayoutOriginal = ImmutableList.of();
customLayoutWithUnavailableButtonsDisabled = ImmutableList.of();
+ commandButtonsForMediaItemsMap = ImmutableMap.of();
playerCommandsFromSession = Commands.EMPTY;
playerCommandsFromPlayer = Commands.EMPTY;
intersectedPlayerCommands =
@@ -733,6 +736,11 @@ import org.checkerframework.checker.nullness.qual.NonNull;
return customLayoutWithUnavailableButtonsDisabled;
}
+ @Override
+ public ImmutableMap getCommandButtonsForMediaItemsMap() {
+ return commandButtonsForMediaItemsMap;
+ }
+
@Override
public Bundle getSessionExtras() {
return sessionExtras;
@@ -2619,6 +2627,16 @@ import org.checkerframework.checker.nullness.qual.NonNull;
customLayoutWithUnavailableButtonsDisabled =
CommandButton.copyWithUnavailableButtonsDisabled(
result.customLayout, sessionCommands, intersectedPlayerCommands);
+ ImmutableMap.Builder commandButtonsForMediaItems =
+ new ImmutableMap.Builder<>();
+ for (int i = 0; i < result.commandButtonsForMediaItems.size(); i++) {
+ CommandButton commandButton = result.commandButtonsForMediaItems.get(i);
+ if (commandButton.sessionCommand != null
+ && commandButton.sessionCommand.commandCode == SessionCommand.COMMAND_CODE_CUSTOM) {
+ commandButtonsForMediaItems.put(commandButton.sessionCommand.customAction, commandButton);
+ }
+ }
+ commandButtonsForMediaItemsMap = commandButtonsForMediaItems.buildOrThrow();
playerInfo = result.playerInfo;
MediaSession.Token platformToken =
result.platformToken == null ? token.getPlatformToken() : result.platformToken;
diff --git a/libraries/session/src/main/java/androidx/media3/session/MediaControllerImplLegacy.java b/libraries/session/src/main/java/androidx/media3/session/MediaControllerImplLegacy.java
index 66cde26b68..24cdc6811a 100644
--- a/libraries/session/src/main/java/androidx/media3/session/MediaControllerImplLegacy.java
+++ b/libraries/session/src/main/java/androidx/media3/session/MediaControllerImplLegacy.java
@@ -76,6 +76,7 @@ import androidx.media3.session.legacy.PlaybackStateCompat;
import androidx.media3.session.legacy.RatingCompat;
import androidx.media3.session.legacy.VolumeProviderCompat;
import com.google.common.collect.ImmutableList;
+import com.google.common.collect.ImmutableMap;
import com.google.common.util.concurrent.Futures;
import com.google.common.util.concurrent.ListenableFuture;
import com.google.common.util.concurrent.SettableFuture;
@@ -420,6 +421,11 @@ import org.checkerframework.checker.initialization.qual.UnderInitialization;
return controllerInfo.customLayout;
}
+ @Override
+ public ImmutableMap getCommandButtonsForMediaItemsMap() {
+ return ImmutableMap.of();
+ }
+
@Override
public Bundle getSessionExtras() {
return controllerInfo.sessionExtras;
diff --git a/libraries/session/src/main/java/androidx/media3/session/MediaLibraryService.java b/libraries/session/src/main/java/androidx/media3/session/MediaLibraryService.java
index 26a19bdfda..554ed2f859 100644
--- a/libraries/session/src/main/java/androidx/media3/session/MediaLibraryService.java
+++ b/libraries/session/src/main/java/androidx/media3/session/MediaLibraryService.java
@@ -630,6 +630,18 @@ public abstract class MediaLibraryService extends MediaSessionService {
return this;
}
+ /**
+ * Sets {@link CommandButton command buttons} that can be added as {@link
+ * MediaMetadata.Builder#setSupportedCommands(List) supported media item commands}.
+ *
+ * @param commandButtons The command buttons.
+ */
+ @UnstableApi
+ @Override
+ public Builder setCommandButtonsForMediaItems(List commandButtons) {
+ return super.setCommandButtonsForMediaItems(commandButtons);
+ }
+
/**
* Builds a {@link MediaLibrarySession}.
*
@@ -648,6 +660,7 @@ public abstract class MediaLibraryService extends MediaSessionService {
player,
sessionActivity,
customLayout,
+ commandButtonsForMediaItems,
callback,
tokenExtras,
sessionExtras,
@@ -664,6 +677,7 @@ public abstract class MediaLibraryService extends MediaSessionService {
Player player,
@Nullable PendingIntent sessionActivity,
ImmutableList customLayout,
+ ImmutableList commandButtonsForMediaItems,
MediaSession.Callback callback,
Bundle tokenExtras,
Bundle sessionExtras,
@@ -677,6 +691,7 @@ public abstract class MediaLibraryService extends MediaSessionService {
player,
sessionActivity,
customLayout,
+ commandButtonsForMediaItems,
callback,
tokenExtras,
sessionExtras,
@@ -693,6 +708,7 @@ public abstract class MediaLibraryService extends MediaSessionService {
Player player,
@Nullable PendingIntent sessionActivity,
ImmutableList customLayout,
+ ImmutableList commandButtonsForMediaItems,
MediaSession.Callback callback,
Bundle tokenExtras,
Bundle sessionExtras,
@@ -707,6 +723,7 @@ public abstract class MediaLibraryService extends MediaSessionService {
player,
sessionActivity,
customLayout,
+ commandButtonsForMediaItems,
(Callback) callback,
tokenExtras,
sessionExtras,
diff --git a/libraries/session/src/main/java/androidx/media3/session/MediaLibrarySessionImpl.java b/libraries/session/src/main/java/androidx/media3/session/MediaLibrarySessionImpl.java
index 6d288636f5..db2b779995 100644
--- a/libraries/session/src/main/java/androidx/media3/session/MediaLibrarySessionImpl.java
+++ b/libraries/session/src/main/java/androidx/media3/session/MediaLibrarySessionImpl.java
@@ -75,6 +75,7 @@ import java.util.concurrent.Future;
Player player,
@Nullable PendingIntent sessionActivity,
ImmutableList customLayout,
+ ImmutableList commandButtonsForMediaItems,
MediaLibrarySession.Callback callback,
Bundle tokenExtras,
Bundle sessionExtras,
@@ -89,6 +90,7 @@ import java.util.concurrent.Future;
player,
sessionActivity,
customLayout,
+ commandButtonsForMediaItems,
callback,
tokenExtras,
sessionExtras,
diff --git a/libraries/session/src/main/java/androidx/media3/session/MediaSession.java b/libraries/session/src/main/java/androidx/media3/session/MediaSession.java
index a56a304754..c7d1325739 100644
--- a/libraries/session/src/main/java/androidx/media3/session/MediaSession.java
+++ b/libraries/session/src/main/java/androidx/media3/session/MediaSession.java
@@ -425,6 +425,18 @@ public class MediaSession {
return super.setShowPlayButtonIfPlaybackIsSuppressed(showPlayButtonIfPlaybackIsSuppressed);
}
+ /**
+ * Sets {@link CommandButton command buttons} that can be added as {@linkplain
+ * MediaMetadata.Builder#setSupportedCommands(List) supported media item commands}.
+ *
+ * @param commandButtons The command buttons.
+ */
+ @UnstableApi
+ @Override
+ public Builder setCommandButtonsForMediaItems(List commandButtons) {
+ return super.setCommandButtonsForMediaItems(commandButtons);
+ }
+
/**
* Builds a {@link MediaSession}.
*
@@ -443,6 +455,7 @@ public class MediaSession {
player,
sessionActivity,
customLayout,
+ commandButtonsForMediaItems,
callback,
tokenExtras,
sessionExtras,
@@ -654,6 +667,7 @@ public class MediaSession {
Player player,
@Nullable PendingIntent sessionActivity,
ImmutableList customLayout,
+ ImmutableList commandButtonsForMediaItems,
Callback callback,
Bundle tokenExtras,
Bundle sessionExtras,
@@ -674,6 +688,7 @@ public class MediaSession {
player,
sessionActivity,
customLayout,
+ commandButtonsForMediaItems,
callback,
tokenExtras,
sessionExtras,
@@ -689,6 +704,7 @@ public class MediaSession {
Player player,
@Nullable PendingIntent sessionActivity,
ImmutableList customLayout,
+ ImmutableList commandButtonsForMediaItems,
Callback callback,
Bundle tokenExtras,
Bundle sessionExtras,
@@ -703,6 +719,7 @@ public class MediaSession {
player,
sessionActivity,
customLayout,
+ commandButtonsForMediaItems,
callback,
tokenExtras,
sessionExtras,
@@ -2073,6 +2090,7 @@ public class MediaSession {
/* package */ @MonotonicNonNull BitmapLoader bitmapLoader;
/* package */ boolean playIfSuppressed;
/* package */ ImmutableList customLayout;
+ /* package */ ImmutableList commandButtonsForMediaItems;
/* package */ boolean isPeriodicPositionUpdateEnabled;
public BuilderBase(Context context, Player player, CallbackT callback) {
@@ -2086,6 +2104,7 @@ public class MediaSession {
customLayout = ImmutableList.of();
playIfSuppressed = true;
isPeriodicPositionUpdateEnabled = true;
+ commandButtonsForMediaItems = ImmutableList.of();
}
@SuppressWarnings("unchecked")
@@ -2140,6 +2159,12 @@ public class MediaSession {
return (BuilderT) this;
}
+ @SuppressWarnings("unchecked")
+ public BuilderT setCommandButtonsForMediaItems(List commandButtons) {
+ this.commandButtonsForMediaItems = ImmutableList.copyOf(commandButtons);
+ return (BuilderT) this;
+ }
+
@SuppressWarnings("unchecked")
public BuilderT setPeriodicPositionUpdateEnabled(boolean isPeriodicPositionUpdateEnabled) {
this.isPeriodicPositionUpdateEnabled = isPeriodicPositionUpdateEnabled;
diff --git a/libraries/session/src/main/java/androidx/media3/session/MediaSessionImpl.java b/libraries/session/src/main/java/androidx/media3/session/MediaSessionImpl.java
index 52e0f918c2..9f9c0e045b 100644
--- a/libraries/session/src/main/java/androidx/media3/session/MediaSessionImpl.java
+++ b/libraries/session/src/main/java/androidx/media3/session/MediaSessionImpl.java
@@ -132,6 +132,7 @@ import org.checkerframework.checker.nullness.qual.MonotonicNonNull;
private final Handler mainHandler;
private final boolean playIfSuppressed;
private final boolean isPeriodicPositionUpdateEnabled;
+ private final ImmutableList commandButtonsForMediaItems;
private PlayerInfo playerInfo;
private PlayerWrapper playerWrapper;
@@ -161,6 +162,7 @@ import org.checkerframework.checker.nullness.qual.MonotonicNonNull;
Player player,
@Nullable PendingIntent sessionActivity,
ImmutableList customLayout,
+ ImmutableList commandButtonsForMediaItems,
MediaSession.Callback callback,
Bundle tokenExtras,
Bundle sessionExtras,
@@ -181,6 +183,7 @@ import org.checkerframework.checker.nullness.qual.MonotonicNonNull;
sessionId = id;
this.sessionActivity = sessionActivity;
this.customLayout = customLayout;
+ this.commandButtonsForMediaItems = commandButtonsForMediaItems;
this.callback = callback;
this.sessionExtras = sessionExtras;
this.bitmapLoader = bitmapLoader;
@@ -513,6 +516,11 @@ import org.checkerframework.checker.nullness.qual.MonotonicNonNull;
return customLayout;
}
+ /** Returns the command buttons for media items. */
+ public ImmutableList getCommandButtonsForMediaItems() {
+ return commandButtonsForMediaItems;
+ }
+
public void setSessionExtras(Bundle sessionExtras) {
this.sessionExtras = sessionExtras;
dispatchRemoteControllerTaskWithoutReturn(
diff --git a/libraries/session/src/main/java/androidx/media3/session/MediaSessionStub.java b/libraries/session/src/main/java/androidx/media3/session/MediaSessionStub.java
index df9f731e90..8529282436 100644
--- a/libraries/session/src/main/java/androidx/media3/session/MediaSessionStub.java
+++ b/libraries/session/src/main/java/androidx/media3/session/MediaSessionStub.java
@@ -535,6 +535,7 @@ import java.util.concurrent.ExecutionException;
connectionResult.customLayout != null
? connectionResult.customLayout
: sessionImpl.getCustomLayout(),
+ sessionImpl.getCommandButtonsForMediaItems(),
connectionResult.availableSessionCommands,
connectionResult.availablePlayerCommands,
playerWrapper.getAvailableCommands(),
diff --git a/libraries/test_session_common/src/main/java/androidx/media3/test/session/common/MediaSessionConstants.java b/libraries/test_session_common/src/main/java/androidx/media3/test/session/common/MediaSessionConstants.java
index 5ccbc12d44..5b75b7ea06 100644
--- a/libraries/test_session_common/src/main/java/androidx/media3/test/session/common/MediaSessionConstants.java
+++ b/libraries/test_session_common/src/main/java/androidx/media3/test/session/common/MediaSessionConstants.java
@@ -21,6 +21,8 @@ public class MediaSessionConstants {
// Test method names
public static final String TEST_GET_SESSION_ACTIVITY = "testGetSessionActivity";
public static final String TEST_GET_CUSTOM_LAYOUT = "testGetCustomLayout";
+ public static final String TEST_GET_COMMAND_BUTTONS_FOR_MEDIA_ITEMS =
+ "testGetCommandButtonsForMediaItems";
public static final String TEST_WITH_CUSTOM_COMMANDS = "testWithCustomCommands";
public static final String TEST_CONTROLLER_LISTENER_SESSION_REJECTS = "connection_sessionRejects";
public static final String TEST_IS_SESSION_COMMAND_AVAILABLE = "testIsSessionCommandAvailable";
diff --git a/libraries/test_session_current/src/androidTest/java/androidx/media3/session/MediaControllerTest.java b/libraries/test_session_current/src/androidTest/java/androidx/media3/session/MediaControllerTest.java
index f33402c1db..dec5175b8d 100644
--- a/libraries/test_session_current/src/androidTest/java/androidx/media3/session/MediaControllerTest.java
+++ b/libraries/test_session_current/src/androidTest/java/androidx/media3/session/MediaControllerTest.java
@@ -21,6 +21,7 @@ import static androidx.media3.session.MediaUtils.createPlayerCommandsWithout;
import static androidx.media3.test.session.common.CommonConstants.DEFAULT_TEST_NAME;
import static androidx.media3.test.session.common.CommonConstants.SUPPORT_APP_PACKAGE_NAME;
import static androidx.media3.test.session.common.MediaSessionConstants.KEY_AVAILABLE_SESSION_COMMANDS;
+import static androidx.media3.test.session.common.MediaSessionConstants.TEST_GET_COMMAND_BUTTONS_FOR_MEDIA_ITEMS;
import static androidx.media3.test.session.common.MediaSessionConstants.TEST_GET_CUSTOM_LAYOUT;
import static androidx.media3.test.session.common.MediaSessionConstants.TEST_GET_SESSION_ACTIVITY;
import static androidx.media3.test.session.common.MediaSessionConstants.TEST_IS_SESSION_COMMAND_AVAILABLE;
@@ -66,8 +67,10 @@ import androidx.test.core.app.ApplicationProvider;
import androidx.test.ext.junit.runners.AndroidJUnit4;
import androidx.test.filters.LargeTest;
import com.google.common.collect.ImmutableList;
+import com.google.common.util.concurrent.FutureCallback;
import com.google.common.util.concurrent.Futures;
import com.google.common.util.concurrent.ListenableFuture;
+import com.google.common.util.concurrent.MoreExecutors;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.CountDownLatch;
@@ -547,6 +550,90 @@ public class MediaControllerTest {
session.cleanUp();
}
+ @Test
+ public void getCommandButtonsForMediaItem() throws Exception {
+ RemoteMediaSession session =
+ createRemoteMediaSession(TEST_GET_COMMAND_BUTTONS_FOR_MEDIA_ITEMS, /* tokenExtras= */ null);
+ CommandButton playlistAddButton =
+ new CommandButton.Builder(CommandButton.ICON_PLAYLIST_ADD)
+ .setSessionCommand(
+ new SessionCommand("androidx.media3.actions.playlist_add", Bundle.EMPTY))
+ .build();
+ CommandButton radioButton =
+ new CommandButton.Builder(CommandButton.ICON_RADIO)
+ .setSessionCommand(new SessionCommand("androidx.media3.actions.radio", Bundle.EMPTY))
+ .build();
+ MediaItem mediaItem =
+ new MediaItem.Builder()
+ .setMediaId("mediaId")
+ .setMediaMetadata(
+ new MediaMetadata.Builder()
+ .setSupportedCommands(
+ ImmutableList.of(
+ "androidx.media3.actions.playlist_add",
+ "androidx.media3.actions.radio",
+ "invalid"))
+ .build())
+ .build();
+ MediaController controller = controllerTestRule.createController(session.getToken());
+
+ ImmutableList commandButtons =
+ threadTestRule
+ .getHandler()
+ .postAndSync(() -> controller.getCommandButtonsForMediaItem(mediaItem));
+
+ assertThat(commandButtons).containsExactly(playlistAddButton, radioButton).inOrder();
+ session.cleanUp();
+ }
+
+ @Test
+ public void sendCustomCommandForMediaItem() throws Exception {
+ RemoteMediaSession session =
+ createRemoteMediaSession(TEST_GET_COMMAND_BUTTONS_FOR_MEDIA_ITEMS, /* tokenExtras= */ null);
+ MediaItem mediaItem =
+ new MediaItem.Builder()
+ .setMediaId("mediaId-1")
+ .setMediaMetadata(
+ new MediaMetadata.Builder()
+ .setSupportedCommands(ImmutableList.of("androidx.media3.actions.playlist_add"))
+ .build())
+ .build();
+ CountDownLatch latch = new CountDownLatch(/* count= */ 1);
+ AtomicReference sessionResultRef = new AtomicReference<>();
+ MediaController controller = controllerTestRule.createController(session.getToken());
+
+ Futures.addCallback(
+ threadTestRule
+ .getHandler()
+ .postAndSync(
+ () -> {
+ CommandButton commandButton =
+ controller.getCommandButtonsForMediaItem(mediaItem).get(0);
+ return controller.sendCustomCommand(
+ commandButton.sessionCommand, mediaItem, Bundle.EMPTY);
+ }),
+ new FutureCallback() {
+ @Override
+ public void onSuccess(SessionResult result) {
+ sessionResultRef.set(result);
+ latch.countDown();
+ }
+
+ @Override
+ public void onFailure(Throwable t) {
+ latch.countDown();
+ }
+ },
+ MoreExecutors.directExecutor());
+
+ assertThat(latch.await(TIMEOUT_MS, MILLISECONDS)).isTrue();
+ assertThat(sessionResultRef.get()).isNotNull();
+ assertThat(sessionResultRef.get().resultCode).isEqualTo(SessionResult.RESULT_SUCCESS);
+ assertThat(sessionResultRef.get().extras.getString(MediaConstants.EXTRA_KEY_MEDIA_ID))
+ .isEqualTo("mediaId-1");
+ session.cleanUp();
+ }
+
@Test
public void getSessionExtras_includedInConnectionStateWhenConnecting() throws Exception {
RemoteMediaSession session =
diff --git a/libraries/test_session_current/src/main/java/androidx/media3/session/MediaSessionProviderService.java b/libraries/test_session_current/src/main/java/androidx/media3/session/MediaSessionProviderService.java
index 8dcf4f6a2f..22dd0f2ac2 100644
--- a/libraries/test_session_current/src/main/java/androidx/media3/session/MediaSessionProviderService.java
+++ b/libraries/test_session_current/src/main/java/androidx/media3/session/MediaSessionProviderService.java
@@ -63,6 +63,7 @@ import static androidx.media3.test.session.common.MediaSessionConstants.KEY_CONT
import static androidx.media3.test.session.common.MediaSessionConstants.NOTIFICATION_CONTROLLER_KEY;
import static androidx.media3.test.session.common.MediaSessionConstants.TEST_COMMAND_GET_TRACKS;
import static androidx.media3.test.session.common.MediaSessionConstants.TEST_CONTROLLER_LISTENER_SESSION_REJECTS;
+import static androidx.media3.test.session.common.MediaSessionConstants.TEST_GET_COMMAND_BUTTONS_FOR_MEDIA_ITEMS;
import static androidx.media3.test.session.common.MediaSessionConstants.TEST_GET_CUSTOM_LAYOUT;
import static androidx.media3.test.session.common.MediaSessionConstants.TEST_GET_SESSION_ACTIVITY;
import static androidx.media3.test.session.common.MediaSessionConstants.TEST_IS_SESSION_COMMAND_AVAILABLE;
@@ -71,6 +72,7 @@ import static androidx.media3.test.session.common.MediaSessionConstants.TEST_ON_
import static androidx.media3.test.session.common.MediaSessionConstants.TEST_ON_VIDEO_SIZE_CHANGED;
import static androidx.media3.test.session.common.MediaSessionConstants.TEST_SET_SHOW_PLAY_BUTTON_IF_SUPPRESSED_TO_FALSE;
import static androidx.media3.test.session.common.MediaSessionConstants.TEST_WITH_CUSTOM_COMMANDS;
+import static com.google.common.util.concurrent.Futures.immediateFuture;
import android.app.PendingIntent;
import android.app.Service;
@@ -233,6 +235,58 @@ public class MediaSessionProviderService extends Service {
});
break;
}
+ case TEST_GET_COMMAND_BUTTONS_FOR_MEDIA_ITEMS:
+ {
+ CommandButton playlistAddButton =
+ new CommandButton.Builder(CommandButton.ICON_PLAYLIST_ADD)
+ .setSessionCommand(
+ new SessionCommand("androidx.media3.actions.playlist_add", Bundle.EMPTY))
+ .build();
+ CommandButton radioButton =
+ new CommandButton.Builder(CommandButton.ICON_RADIO)
+ .setSessionCommand(
+ new SessionCommand("androidx.media3.actions.radio", Bundle.EMPTY))
+ .build();
+ builder.setCommandButtonsForMediaItems(
+ ImmutableList.of(playlistAddButton, radioButton));
+ builder.setCallback(
+ new MediaSession.Callback() {
+ @Override
+ public MediaSession.ConnectionResult onConnect(
+ MediaSession session, ControllerInfo controller) {
+ return new MediaSession.ConnectionResult.AcceptedResultBuilder(session)
+ .setAvailableSessionCommands(
+ new SessionCommands.Builder()
+ .add(checkNotNull(playlistAddButton.sessionCommand))
+ .add(checkNotNull(radioButton.sessionCommand))
+ .build())
+ .build();
+ }
+
+ @Override
+ public ListenableFuture onCustomCommand(
+ MediaSession session,
+ ControllerInfo controller,
+ SessionCommand customCommand,
+ Bundle args) {
+ SessionResult sessionResult =
+ new SessionResult(SessionResult.RESULT_ERROR_NOT_SUPPORTED);
+ if (customCommand.equals(playlistAddButton.sessionCommand)
+ || customCommand.equals(radioButton.sessionCommand)) {
+ Bundle extras = new Bundle();
+ String receivedMediaId = args.getString(MediaConstants.EXTRA_KEY_MEDIA_ID);
+ @SessionResult.Code int resultCode = SessionResult.RESULT_ERROR_BAD_VALUE;
+ if (receivedMediaId != null) {
+ extras.putString(MediaConstants.EXTRA_KEY_MEDIA_ID, receivedMediaId);
+ resultCode = SessionResult.RESULT_SUCCESS;
+ }
+ sessionResult = new SessionResult(resultCode, extras);
+ }
+ return immediateFuture(sessionResult);
+ }
+ });
+ break;
+ }
case TEST_CONTROLLER_LISTENER_SESSION_REJECTS:
{
builder.setCallback(