Add icon constants to CommandButton

These allow to set the icon in a standardized way without needing
custom bitmaps or resources. For now, this is just additional
information without backwards-compatible icons or implications.

The same value gets written to the platform session via a new
extras key that can be read and set from sessions not using Media3
yet.

PiperOrigin-RevId: 605670988
This commit is contained in:
tonihei 2024-02-09 10:34:07 -08:00 committed by Copybara-Service
parent 338aef4830
commit adc23e8e8b
8 changed files with 511 additions and 14 deletions

View File

@ -18,11 +18,13 @@ package androidx.media3.session;
import static androidx.media3.common.util.Assertions.checkArgument;
import static androidx.media3.common.util.Assertions.checkNotNull;
import static androidx.media3.common.util.Assertions.checkState;
import static java.lang.annotation.ElementType.TYPE_USE;
import android.net.Uri;
import android.os.Bundle;
import android.text.TextUtils;
import androidx.annotation.DrawableRes;
import androidx.annotation.IntDef;
import androidx.annotation.Nullable;
import androidx.media3.common.Bundleable;
import androidx.media3.common.Player;
@ -32,6 +34,10 @@ import com.google.common.base.Objects;
import com.google.common.collect.ImmutableList;
import com.google.errorprone.annotations.CanIgnoreReturnValue;
import com.google.errorprone.annotations.CheckReturnValue;
import java.lang.annotation.Documented;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
import java.util.List;
/**
@ -43,11 +49,343 @@ import java.util.List;
*/
public final class CommandButton implements Bundleable {
/** An icon constant for a button. Must be one of the {@code CommandButton.ICON_} constants. */
@UnstableApi
@Documented
@Retention(RetentionPolicy.SOURCE)
@Target(TYPE_USE)
@IntDef({
ICON_UNDEFINED,
ICON_PLAY,
ICON_PAUSE,
ICON_STOP,
ICON_NEXT,
ICON_PREVIOUS,
ICON_SKIP_FORWARD,
ICON_SKIP_FORWARD_5,
ICON_SKIP_FORWARD_10,
ICON_SKIP_FORWARD_15,
ICON_SKIP_FORWARD_30,
ICON_SKIP_BACK,
ICON_SKIP_BACK_5,
ICON_SKIP_BACK_10,
ICON_SKIP_BACK_15,
ICON_SKIP_BACK_30,
ICON_FAST_FORWARD,
ICON_REWIND,
ICON_REPEAT_ALL,
ICON_REPEAT_ONE,
ICON_SHUFFLE,
ICON_SHUFFLE_STAR,
ICON_HEART_FILLED,
ICON_HEART_UNFILLED,
ICON_STAR_FILLED,
ICON_STAR_UNFILLED,
ICON_BOOKMARK_FILLED,
ICON_BOOKMARK_UNFILLED,
ICON_THUMB_UP_FILLED,
ICON_THUMB_UP_UNFILLED,
ICON_THUMB_DOWN_FILLED,
ICON_THUMB_DOWN_UNFILLED,
ICON_FLAG_FILLED,
ICON_FLAG_UNFILLED,
ICON_PLUS,
ICON_MINUS,
ICON_PLAYLIST_ADD,
ICON_PLAYLIST_REMOVE,
ICON_BLOCK,
ICON_PLUS_CIRCLE_FILLED,
ICON_PLUS_CIRCLE_UNFILLED,
ICON_MINUS_CIRCLE_FILLED,
ICON_MINUS_CIRCLE_UNFILLED,
ICON_CHECK_CIRCLE_FILLED,
ICON_CHECK_CIRCLE_UNFILLED,
ICON_PLAYBACK_SPEED,
ICON_PLAYBACK_SPEED_0_5,
ICON_PLAYBACK_SPEED_0_8,
ICON_PLAYBACK_SPEED_1_0,
ICON_PLAYBACK_SPEED_1_2,
ICON_PLAYBACK_SPEED_1_5,
ICON_PLAYBACK_SPEED_1_8,
ICON_PLAYBACK_SPEED_2_0,
ICON_SETTINGS,
ICON_QUALITY,
ICON_SUBTITLES,
ICON_SUBTITLES_OFF,
ICON_CLOSED_CAPTIONS,
ICON_CLOSED_CAPTIONS_OFF,
ICON_SYNC,
ICON_SHARE,
ICON_VOLUME_UP,
ICON_VOLUME_DOWN,
ICON_VOLUME_OFF,
ICON_ARTIST,
ICON_ALBUM,
ICON_RADIO,
ICON_SIGNAL,
ICON_FEED
})
public @interface Icon {}
// Note: The constant values of these icons matches the Material Design code points.
/**
* An icon constant representing an undefined icon, for example a custom icon not covered by the
* existing constants.
*/
@UnstableApi public static final int ICON_UNDEFINED = 0;
/** An icon showing a play symbol (a right facing triangle). */
@UnstableApi public static final int ICON_PLAY = 0xe037;
/** An icon showing a pause symbol (two vertical bars). */
@UnstableApi public static final int ICON_PAUSE = 0xe034;
/** An icon showing a stop symbol (a square). */
@UnstableApi public static final int ICON_STOP = 0xe047;
/** An icon showing a next symbol (a right facing triangle with a vertical bar). */
@UnstableApi public static final int ICON_NEXT = 0xe044;
/** An icon showing a previous symbol (a left facing triangle with a vertical bar). */
@UnstableApi public static final int ICON_PREVIOUS = 0xe045;
/** An icon showing a skip forward symbol (an open clock-wise arrow). */
@UnstableApi public static final int ICON_SKIP_FORWARD = 0xf6f4;
/**
* An icon showing a skip forward 5 seconds symbol (an open clockwise arrow with the number 5).
*/
@UnstableApi public static final int ICON_SKIP_FORWARD_5 = 0xe058;
/**
* An icon showing a skip forward 10 seconds symbol (an open clockwise arrow with the number 10).
*/
@UnstableApi public static final int ICON_SKIP_FORWARD_10 = 0xe056;
/**
* An icon showing a skip forward 15 seconds symbol (an open clockwise arrow with the number 15).
*/
@UnstableApi public static final int ICON_SKIP_FORWARD_15 = 0xfe056;
/**
* An icon showing a skip forward 30 seconds symbol (an open clockwise arrow with the number 30).
*/
@UnstableApi public static final int ICON_SKIP_FORWARD_30 = 0xe057;
/** An icon showing a skip back symbol (an open anti-clockwise arrow). */
@UnstableApi public static final int ICON_SKIP_BACK = 0xe042;
/**
* An icon showing a skip back 5 seconds symbol (an open anti-clockwise arrow with the number 5).
*/
@UnstableApi public static final int ICON_SKIP_BACK_5 = 0xe05b;
/**
* An icon showing a skip back 10 seconds symbol (an open anti-clockwise arrow with the number
* 10).
*/
@UnstableApi public static final int ICON_SKIP_BACK_10 = 0xe059;
/**
* An icon showing a skip back 15 seconds symbol (an open anti-clockwise arrow with the number
* 15).
*/
@UnstableApi public static final int ICON_SKIP_BACK_15 = 0xfe059;
/**
* An icon showing a skip back 30 seconds symbol (an open anti-clockwise arrow with the number
* 30).
*/
@UnstableApi public static final int ICON_SKIP_BACK_30 = 0xe05a;
/** An icon showing a fast forward symbol (two right facing triangles). */
@UnstableApi public static final int ICON_FAST_FORWARD = 0xe01f;
/** An icon showing a rewind symbol (two left facing triangles). */
@UnstableApi public static final int ICON_REWIND = 0xe020;
/** An icon showing a repeat all symbol (two open clockwise arrows). */
@UnstableApi public static final int ICON_REPEAT_ALL = 0xe040;
/** An icon showing a repeat one symbol (two open clockwise arrows with an overlaid number 1). */
@UnstableApi public static final int ICON_REPEAT_ONE = 0xe041;
/** An icon showing a shuffle symbol (two diagonal upward and downward facing arrows). */
@UnstableApi public static final int ICON_SHUFFLE = 0xe043;
/**
* An icon showing a shuffle symbol with a start (two diagonal upward and downward facing arrows
* with an overlaid star).
*/
@UnstableApi public static final int ICON_SHUFFLE_STAR = 0xfe043;
/** An icon showing a filled heart symbol. */
@UnstableApi public static final int ICON_HEART_FILLED = 0xfe87d;
/** An icon showing an unfilled heart symbol. */
@UnstableApi public static final int ICON_HEART_UNFILLED = 0xe87d;
/** An icon showing a filled star symbol. */
@UnstableApi public static final int ICON_STAR_FILLED = 0xfe838;
/** An icon showing an unfilled star symbol. */
@UnstableApi public static final int ICON_STAR_UNFILLED = 0xe838;
/** An icon showing a filled bookmark symbol. */
@UnstableApi public static final int ICON_BOOKMARK_FILLED = 0xfe866;
/** An icon showing an unfilled bookmark symbol. */
@UnstableApi public static final int ICON_BOOKMARK_UNFILLED = 0xe866;
/** An icon showing a filled thumb-up symbol. */
@UnstableApi public static final int ICON_THUMB_UP_FILLED = 0xfe8dc;
/** An icon showing an unfilled thumb-up symbol. */
@UnstableApi public static final int ICON_THUMB_UP_UNFILLED = 0xe8dc;
/** An icon showing a filled thumb-down symbol. */
@UnstableApi public static final int ICON_THUMB_DOWN_FILLED = 0xfe8db;
/** An icon showing an unfilled thumb-down symbol. */
@UnstableApi public static final int ICON_THUMB_DOWN_UNFILLED = 0xe8db;
/** An icon showing a filled flag symbol. */
@UnstableApi public static final int ICON_FLAG_FILLED = 0xfe153;
/** An icon showing an unfilled flag symbol. */
@UnstableApi public static final int ICON_FLAG_UNFILLED = 0xe153;
/** An icon showing a plus symbol. */
@UnstableApi public static final int ICON_PLUS = 0xe145;
/** An icon showing a minus symbol. */
@UnstableApi public static final int ICON_MINUS = 0xe15b;
/** An icon showing an add to playlist symbol (multiple horizontal bars with a small plus). */
@UnstableApi public static final int ICON_PLAYLIST_ADD = 0xe03b;
/**
* An icon showing an remove from playlist symbol (multiple horizontal bars with a small minus).
*/
@UnstableApi public static final int ICON_PLAYLIST_REMOVE = 0xeb80;
/** An icon showing a block symbol (a circle with a diagonal line). */
@UnstableApi public static final int ICON_BLOCK = 0xe14b;
/** An icon showing a filled circle with a plus. */
@UnstableApi public static final int ICON_PLUS_CIRCLE_FILLED = 0xfe147;
/** An icon showing an unfilled circle with a plus. */
@UnstableApi public static final int ICON_PLUS_CIRCLE_UNFILLED = 0xe147;
/** An icon showing a filled circle with a minus. */
@UnstableApi public static final int ICON_MINUS_CIRCLE_FILLED = 0xfe148;
/** An icon showing an unfilled circle with a minus. */
@UnstableApi public static final int ICON_MINUS_CIRCLE_UNFILLED = 0xfe149;
/** An icon showing a filled circle with a check mark. */
@UnstableApi public static final int ICON_CHECK_CIRCLE_FILLED = 0xfe86c;
/** An icon showing a unfilled circle with a check mark. */
@UnstableApi public static final int ICON_CHECK_CIRCLE_UNFILLED = 0xe86c;
/**
* An icon showing a playback speed symbol (a right facing triangle in a circle with half-dashed,
* half-solid contour).
*/
@UnstableApi public static final int ICON_PLAYBACK_SPEED = 0xe068;
/** An icon showing a 0.5x speed symbol. */
@UnstableApi public static final int ICON_PLAYBACK_SPEED_0_5 = 0xf4e2;
/** An icon showing a 0.8x speed symbol. */
@UnstableApi public static final int ICON_PLAYBACK_SPEED_0_8 = 0xff4e2;
/** An icon showing a 1.0x speed symbol. */
@UnstableApi public static final int ICON_PLAYBACK_SPEED_1_0 = 0xefcd;
/** An icon showing a 1.2x speed symbol. */
@UnstableApi public static final int ICON_PLAYBACK_SPEED_1_2 = 0xf4e1;
/** An icon showing a 1.5x speed symbol. */
@UnstableApi public static final int ICON_PLAYBACK_SPEED_1_5 = 0xf4e0;
/** An icon showing a 1.8x speed symbol. */
@UnstableApi public static final int ICON_PLAYBACK_SPEED_1_8 = 0xff4e0;
/** An icon showing a 2.0x speed symbol. */
@UnstableApi public static final int ICON_PLAYBACK_SPEED_2_0 = 0xf4eb;
/** An icon showing a settings symbol (a stylized cog). */
@UnstableApi public static final int ICON_SETTINGS = 0xe8b8;
/** An icon showing a quality selection symbol (multiple horizontal bars with sliders). */
@UnstableApi public static final int ICON_QUALITY = 0xe429;
/** An icon showing a subtitles symbol (a rectangle filled with dots and horizontal lines). */
@UnstableApi public static final int ICON_SUBTITLES = 0xe048;
/**
* An icon showing a subtitles off symbol (a rectangle filled with dots and horizontal lines, with
* a large diagonal line across).
*/
@UnstableApi public static final int ICON_SUBTITLES_OFF = 0xef72;
/** An icon showing a closed caption symbol (a rectangle with the letters CC). */
@UnstableApi public static final int ICON_CLOSED_CAPTIONS = 0xe01c;
/**
* An icon showing a closed caption off symbol (a rectangle with the letters CC, with a large
* diagonal line across).
*/
@UnstableApi public static final int ICON_CLOSED_CAPTIONS_OFF = 0xf1dc;
/** An icon showing a sync symbol (two open anti-clockwise arrows). */
@UnstableApi public static final int ICON_SYNC = 0xe627;
/**
* An icon showing a share symbol (three dots connected by two diagonal lines, open on the right).
*/
@UnstableApi public static final int ICON_SHARE = 0xe80d;
/** An icon showing a volume up symbol (a stylized speaker with multiple sound waves). */
@UnstableApi public static final int ICON_VOLUME_UP = 0xe050;
/** An icon showing a volume down symbol (a stylized speaker with a single small sound wave). */
@UnstableApi public static final int ICON_VOLUME_DOWN = 0xe04d;
/**
* An icon showing a volume off symbol (a stylized speaker with multiple sound waves, with a large
* diagonal line across).
*/
@UnstableApi public static final int ICON_VOLUME_OFF = 0xe04f;
/** An icon showing an artist symbol (a stylized person with a musical note). */
@UnstableApi public static final int ICON_ARTIST = 0xe01a;
/** An icon showing an album symbol (a stylized LP record). */
@UnstableApi public static final int ICON_ALBUM = 0xe019;
/** An icon showing a radio symbol (left and right facing sound waves). */
@UnstableApi public static final int ICON_RADIO = 0xe51e;
/** An icon showing an signal symbol (a vertical mast with circular sounds waves). */
@UnstableApi public static final int ICON_SIGNAL = 0xf048;
/**
* An icon showing an feed symbol (a dot in the bottom-left with multiple concentric quarter
* circles).
*/
@UnstableApi public static final int ICON_FEED = 0xe0e5;
/** A builder for {@link CommandButton}. */
public static final class Builder {
@Nullable private SessionCommand sessionCommand;
private @Player.Command int playerCommand;
private @Icon int icon;
@DrawableRes private int iconResId;
@Nullable private Uri iconUri;
private CharSequence displayName;
@ -59,6 +397,7 @@ public final class CommandButton implements Bundleable {
displayName = "";
extras = Bundle.EMPTY;
playerCommand = Player.COMMAND_INVALID;
icon = ICON_UNDEFINED;
}
/**
@ -97,6 +436,19 @@ public final class CommandButton implements Bundleable {
return this;
}
/**
* Sets the icon of this button.
*
* @param icon The {@link Icon} that should be shown for this button.
* @return This builder for chaining.
*/
@UnstableApi
@CanIgnoreReturnValue
public Builder setIcon(@Icon int icon) {
this.icon = icon;
return this;
}
/**
* Sets the resource id of a bitmap (e.g. PNG) icon of this button.
*
@ -168,7 +520,7 @@ public final class CommandButton implements Bundleable {
(sessionCommand == null) != (playerCommand == Player.COMMAND_INVALID),
"Exactly one of sessionCommand and playerCommand should be set");
return new CommandButton(
sessionCommand, playerCommand, iconResId, iconUri, displayName, extras, enabled);
sessionCommand, playerCommand, icon, iconResId, iconUri, displayName, extras, enabled);
}
}
@ -181,6 +533,9 @@ public final class CommandButton implements Bundleable {
*/
public final @Player.Command int playerCommand;
/** The {@link Icon} of the button. */
@UnstableApi public final @Icon int icon;
/**
* The icon resource id of the button. Can be {@code 0} if the command is predefined and a custom
* icon isn't needed.
@ -208,6 +563,7 @@ public final class CommandButton implements Bundleable {
private CommandButton(
@Nullable SessionCommand sessionCommand,
@Player.Command int playerCommand,
@Icon int icon,
@DrawableRes int iconResId,
@Nullable Uri iconUri,
CharSequence displayName,
@ -215,6 +571,7 @@ public final class CommandButton implements Bundleable {
boolean enabled) {
this.sessionCommand = sessionCommand;
this.playerCommand = playerCommand;
this.icon = icon;
this.iconResId = iconResId;
this.iconUri = iconUri;
this.displayName = displayName;
@ -234,6 +591,7 @@ public final class CommandButton implements Bundleable {
return new CommandButton(
sessionCommand,
playerCommand,
icon,
iconResId,
iconUri,
displayName,
@ -253,6 +611,7 @@ public final class CommandButton implements Bundleable {
CommandButton button = (CommandButton) obj;
return Objects.equal(sessionCommand, button.sessionCommand)
&& playerCommand == button.playerCommand
&& icon == button.icon
&& iconResId == button.iconResId
&& Objects.equal(iconUri, button.iconUri)
&& TextUtils.equals(displayName, button.displayName)
@ -262,7 +621,7 @@ public final class CommandButton implements Bundleable {
@Override
public int hashCode() {
return Objects.hashCode(
sessionCommand, playerCommand, iconResId, displayName, isEnabled, iconUri);
sessionCommand, playerCommand, icon, iconResId, displayName, isEnabled, iconUri);
}
/**
@ -307,6 +666,7 @@ public final class CommandButton implements Bundleable {
private static final String FIELD_EXTRAS = Util.intToStringMaxRadix(4);
private static final String FIELD_ENABLED = Util.intToStringMaxRadix(5);
private static final String FIELD_ICON_URI = Util.intToStringMaxRadix(6);
private static final String FIELD_ICON = Util.intToStringMaxRadix(7);
@UnstableApi
@Override
@ -315,12 +675,27 @@ public final class CommandButton implements Bundleable {
if (sessionCommand != null) {
bundle.putBundle(FIELD_SESSION_COMMAND, sessionCommand.toBundle());
}
bundle.putInt(FIELD_PLAYER_COMMAND, playerCommand);
bundle.putInt(FIELD_ICON_RES_ID, iconResId);
bundle.putCharSequence(FIELD_DISPLAY_NAME, displayName);
bundle.putBundle(FIELD_EXTRAS, extras);
bundle.putParcelable(FIELD_ICON_URI, iconUri);
bundle.putBoolean(FIELD_ENABLED, isEnabled);
if (playerCommand != Player.COMMAND_INVALID) {
bundle.putInt(FIELD_PLAYER_COMMAND, playerCommand);
}
if (icon != ICON_UNDEFINED) {
bundle.putInt(FIELD_ICON, icon);
}
if (iconResId != 0) {
bundle.putInt(FIELD_ICON_RES_ID, iconResId);
}
if (displayName != "") {
bundle.putCharSequence(FIELD_DISPLAY_NAME, displayName);
}
if (!extras.isEmpty()) {
bundle.putBundle(FIELD_EXTRAS, extras);
}
if (iconUri != null) {
bundle.putParcelable(FIELD_ICON_URI, iconUri);
}
if (isEnabled) {
bundle.putBoolean(FIELD_ENABLED, isEnabled);
}
return bundle;
}
@ -349,6 +724,7 @@ public final class CommandButton implements Bundleable {
@Nullable Bundle extras = bundle.getBundle(FIELD_EXTRAS);
boolean enabled = bundle.getBoolean(FIELD_ENABLED, /* defaultValue= */ false);
@Nullable Uri iconUri = bundle.getParcelable(FIELD_ICON_URI);
@Icon int icon = bundle.getInt(FIELD_ICON, /* defaultValue= */ ICON_UNDEFINED);
Builder builder = new Builder();
if (sessionCommand != null) {
builder.setSessionCommand(sessionCommand);
@ -360,6 +736,7 @@ public final class CommandButton implements Bundleable {
builder.setIconUri(iconUri);
}
return builder
.setIcon(icon)
.setIconResId(iconResId)
.setDisplayName(displayName)
.setExtras(extras == null ? Bundle.EMPTY : extras)

View File

@ -1258,11 +1258,19 @@ import java.util.concurrent.TimeoutException;
for (CustomAction customAction : state.getCustomActions()) {
String action = customAction.getAction();
@Nullable Bundle extras = customAction.getExtras();
@CommandButton.Icon
int icon =
extras != null
? extras.getInt(
MediaConstants.EXTRAS_KEY_COMMAND_BUTTON_ICON_COMPAT,
/* defaultValue= */ CommandButton.ICON_UNDEFINED)
: CommandButton.ICON_UNDEFINED;
CommandButton button =
new CommandButton.Builder()
.setSessionCommand(new SessionCommand(action, extras == null ? Bundle.EMPTY : extras))
.setDisplayName(customAction.getName())
.setEnabled(true)
.setIcon(icon)
.setIconResId(customAction.getIcon())
.build();
layout.add(button);

View File

@ -465,6 +465,15 @@ public final class MediaConstants {
public static final String EXTRAS_KEY_MEDIA_TYPE_COMPAT =
"androidx.media3.session.EXTRAS_KEY_MEDIA_TYPE_COMPAT";
/**
* {@link Bundle} key used to indicate the {@link CommandButton.Icon} in the extras of the legacy
* {@link PlaybackStateCompat.CustomAction}. The corresponding value should be one of the {@code
* CommandButton.ICON_} integer constants.
*/
@UnstableApi
public static final String EXTRAS_KEY_COMMAND_BUTTON_ICON_COMPAT =
"androidx.media3.session.EXTRAS_KEY_COMMAND_BUTTON_ICON_COMPAT";
/* package */ static final String SESSION_COMMAND_ON_CAPTIONING_ENABLED_CHANGED =
"androidx.media3.session.SESSION_COMMAND_ON_CAPTIONING_ENABLED_CHANGED";
/* package */ static final String SESSION_COMMAND_REQUEST_SESSION3_TOKEN =

View File

@ -1048,12 +1048,18 @@ import java.util.List;
if (sessionCommand.commandCode == SessionCommand.COMMAND_CODE_CUSTOM
&& CommandButton.isEnabled(
commandButton, availableSessionCommands, availablePlayerCommands)) {
Bundle actionExtras = sessionCommand.customExtras;
if (commandButton.icon != CommandButton.ICON_UNDEFINED) {
actionExtras = new Bundle(sessionCommand.customExtras);
actionExtras.putInt(
MediaConstants.EXTRAS_KEY_COMMAND_BUTTON_ICON_COMPAT, commandButton.icon);
}
builder.addCustomAction(
new PlaybackStateCompat.CustomAction.Builder(
sessionCommand.customAction,
commandButton.displayName,
commandButton.iconResId)
.setExtras(sessionCommand.customExtras)
.setExtras(actionExtras)
.build());
}
}

View File

@ -242,6 +242,14 @@ public class CommandButtonTest {
.setIconUri(Uri.parse("content://test"))
.setPlayerCommand(Player.COMMAND_SEEK_TO_NEXT)
.build());
assertThat(button)
.isNotEqualTo(
new CommandButton.Builder()
.setIcon(CommandButton.ICON_NEXT)
.setDisplayName("button")
.setIconResId(R.drawable.media3_notification_small_icon)
.setPlayerCommand(Player.COMMAND_SEEK_TO_NEXT)
.build());
}
@Test
@ -360,4 +368,45 @@ public class CommandButtonTest {
.setIconResId(R.drawable.media3_notification_small_icon);
assertThrows(IllegalStateException.class, builder::build);
}
@Test
public void fromBundle_afterToBundle_returnsEqualInstance() {
Bundle extras = new Bundle();
extras.putString("key", "value");
CommandButton buttonWithSessionCommand =
new CommandButton.Builder()
.setDisplayName("name")
.setEnabled(true)
.setIcon(CommandButton.ICON_CLOSED_CAPTIONS)
.setIconResId(R.drawable.media3_notification_small_icon)
.setIconUri(Uri.parse("http://test.test"))
.setExtras(extras)
.setSessionCommand(new SessionCommand(SessionCommand.COMMAND_CODE_SESSION_SET_RATING))
.build();
CommandButton buttonWithPlayerCommand =
new CommandButton.Builder()
.setDisplayName("name")
.setEnabled(true)
.setIcon(CommandButton.ICON_CLOSED_CAPTIONS)
.setIconResId(R.drawable.media3_notification_small_icon)
.setIconUri(Uri.parse("http://test.test"))
.setExtras(extras)
.setPlayerCommand(Player.COMMAND_GET_METADATA)
.build();
CommandButton buttonWithDefaultValues =
new CommandButton.Builder().setPlayerCommand(Player.COMMAND_PLAY_PAUSE).build();
CommandButton restoredButtonWithSessionCommand =
CommandButton.fromBundle(buttonWithSessionCommand.toBundle());
CommandButton restoredButtonWithPlayerCommand =
CommandButton.fromBundle(buttonWithPlayerCommand.toBundle());
CommandButton restoredButtonWithDefaultValues =
CommandButton.fromBundle(buttonWithDefaultValues.toBundle());
assertThat(restoredButtonWithSessionCommand).isEqualTo(buttonWithSessionCommand);
assertThat(restoredButtonWithSessionCommand.extras.get("key")).isEqualTo("value");
assertThat(restoredButtonWithPlayerCommand).isEqualTo(buttonWithPlayerCommand);
assertThat(restoredButtonWithPlayerCommand.extras.get("key")).isEqualTo("value");
assertThat(restoredButtonWithDefaultValues).isEqualTo(buttonWithDefaultValues);
}
}

View File

@ -58,6 +58,7 @@ import androidx.test.core.app.ApplicationProvider;
import androidx.test.ext.junit.runners.AndroidJUnit4;
import androidx.test.filters.SdkSuppress;
import androidx.test.filters.SmallTest;
import com.google.common.collect.ImmutableList;
import com.google.common.util.concurrent.ListenableFuture;
import java.util.List;
import org.junit.Before;
@ -895,23 +896,23 @@ public final class LegacyConversionsTest {
}
@Test
public void convertToCustomLayout() {
public void convertToCustomLayout_withNull_returnsEmptyList() {
assertThat(LegacyConversions.convertToCustomLayout(null)).isEmpty();
}
@Test
public void convertToCustomLayout_withoutIconConstantInExtras() {
String extraKey = "key";
String extraValue = "value";
String actionStr = "action";
String displayName = "display_name";
int iconRes = 21;
Bundle extras = new Bundle();
extras.putString(extraKey, extraValue);
PlaybackStateCompat.CustomAction action =
new PlaybackStateCompat.CustomAction.Builder(actionStr, displayName, iconRes)
.setExtras(extras)
.build();
PlaybackStateCompat state =
new PlaybackStateCompat.Builder()
.setState(
@ -922,7 +923,8 @@ public final class LegacyConversionsTest {
.addCustomAction(action)
.build();
List<CommandButton> buttons = LegacyConversions.convertToCustomLayout(state);
ImmutableList<CommandButton> buttons = LegacyConversions.convertToCustomLayout(state);
assertThat(buttons).hasSize(1);
CommandButton button = buttons.get(0);
assertThat(button.displayName.toString()).isEqualTo(displayName);
@ -930,6 +932,41 @@ public final class LegacyConversionsTest {
assertThat(button.iconResId).isEqualTo(iconRes);
assertThat(button.sessionCommand.customAction).isEqualTo(actionStr);
assertThat(button.sessionCommand.customExtras.getString(extraKey)).isEqualTo(extraValue);
assertThat(button.icon).isEqualTo(CommandButton.ICON_UNDEFINED);
}
@Test
public void convertToCustomLayout_withIconConstantInExtras() {
String actionStr = "action";
String displayName = "display_name";
int iconRes = 21;
Bundle extras = new Bundle();
extras.putInt(
androidx.media3.session.MediaConstants.EXTRAS_KEY_COMMAND_BUTTON_ICON_COMPAT,
CommandButton.ICON_FAST_FORWARD);
PlaybackStateCompat.CustomAction action =
new PlaybackStateCompat.CustomAction.Builder(actionStr, displayName, iconRes)
.setExtras(extras)
.build();
PlaybackStateCompat state =
new PlaybackStateCompat.Builder()
.setState(
PlaybackStateCompat.STATE_NONE,
/* position= */ 0,
/* playbackSpeed= */ 1,
/* updateTime= */ 100)
.addCustomAction(action)
.build();
ImmutableList<CommandButton> buttons = LegacyConversions.convertToCustomLayout(state);
assertThat(buttons).hasSize(1);
CommandButton button = buttons.get(0);
assertThat(button.displayName.toString()).isEqualTo(displayName);
assertThat(button.isEnabled).isTrue();
assertThat(button.iconResId).isEqualTo(iconRes);
assertThat(button.sessionCommand.customAction).isEqualTo(actionStr);
assertThat(button.icon).isEqualTo(CommandButton.ICON_FAST_FORWARD);
}
@Test

View File

@ -1563,6 +1563,7 @@ public class MediaControllerCompatPlaybackStateCompatActionsWithMediaSessionTest
.build(),
new CommandButton.Builder()
.setDisplayName("button2")
.setIcon(CommandButton.ICON_FAST_FORWARD)
.setIconResId(R.drawable.media3_notification_pause)
.setSessionCommand(command2)
.build());

View File

@ -138,6 +138,8 @@ public class MediaControllerListenerWithMediaSessionCompatTest {
.build();
Bundle extras2 = new Bundle();
extras2.putString("key", "value-2");
extras2.putInt(
MediaConstants.EXTRAS_KEY_COMMAND_BUTTON_ICON_COMPAT, CommandButton.ICON_FAST_FORWARD);
PlaybackStateCompat.CustomAction customAction2 =
new PlaybackStateCompat.CustomAction.Builder("action2", "actionName2", /* icon= */ 2)
.setExtras(extras2)
@ -151,6 +153,7 @@ public class MediaControllerListenerWithMediaSessionCompatTest {
List<String> receivedBundleValues = new ArrayList<>();
List<Integer> receivedIconResIds = new ArrayList<>();
List<Integer> receivedCommandCodes = new ArrayList<>();
List<Integer> receivedIcons = new ArrayList<>();
CountDownLatch countDownLatch = new CountDownLatch(1);
controllerTestRule.createController(
session.getSessionToken(),
@ -164,6 +167,7 @@ public class MediaControllerListenerWithMediaSessionCompatTest {
receivedBundleValues.add(button.sessionCommand.customExtras.getString("key"));
receivedCommandCodes.add(button.sessionCommand.commandCode);
receivedIconResIds.add(button.iconResId);
receivedIcons.add(button.icon);
}
countDownLatch.countDown();
return Futures.immediateFuture(new SessionResult(SessionResult.RESULT_SUCCESS));
@ -180,6 +184,9 @@ public class MediaControllerListenerWithMediaSessionCompatTest {
assertThat(receivedDisplayNames).containsExactly("actionName1", "actionName2").inOrder();
assertThat(receivedIconResIds).containsExactly(1, 2).inOrder();
assertThat(receivedBundleValues).containsExactly("value-1", "value-2").inOrder();
assertThat(receivedIcons)
.containsExactly(CommandButton.ICON_UNDEFINED, CommandButton.ICON_FAST_FORWARD)
.inOrder();
}
@Test
@ -395,6 +402,7 @@ public class MediaControllerListenerWithMediaSessionCompatTest {
CommandButton button2 =
new CommandButton.Builder()
.setDisplayName("button2")
.setIcon(CommandButton.ICON_FAST_FORWARD)
.setIconResId(R.drawable.media3_notification_small_icon)
.setSessionCommand(new SessionCommand("command2", Bundle.EMPTY))
.build();
@ -431,6 +439,8 @@ public class MediaControllerListenerWithMediaSessionCompatTest {
.build();
Bundle extras2 = new Bundle();
extras2.putString("key", "value-2");
extras2.putInt(
MediaConstants.EXTRAS_KEY_COMMAND_BUTTON_ICON_COMPAT, CommandButton.ICON_FAST_FORWARD);
PlaybackStateCompat.CustomAction customAction2 =
new PlaybackStateCompat.CustomAction.Builder(
"command2", "button2", /* icon= */ R.drawable.media3_notification_small_icon)