diff --git a/libraries/session/src/main/java/androidx/media3/session/CommandButton.java b/libraries/session/src/main/java/androidx/media3/session/CommandButton.java index cecf79a8bc..e4c98ecba9 100644 --- a/libraries/session/src/main/java/androidx/media3/session/CommandButton.java +++ b/libraries/session/src/main/java/androidx/media3/session/CommandButton.java @@ -28,6 +28,7 @@ import androidx.media3.common.Player; import androidx.media3.common.util.UnstableApi; import androidx.media3.common.util.Util; 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.util.List; @@ -235,6 +236,39 @@ public final class CommandButton implements Bundleable { return Objects.hashCode(sessionCommand, playerCommand, iconResId, displayName, isEnabled); } + /** + * Returns a list of command buttons with the {@link CommandButton#isEnabled} flag set according + * to the available commands passed in. + */ + /* package */ static ImmutableList getEnabledCommandButtons( + List commandButtons, + SessionCommands sessionCommands, + Player.Commands playerCommands) { + ImmutableList.Builder enabledButtons = new ImmutableList.Builder<>(); + for (int i = 0; i < commandButtons.size(); i++) { + CommandButton button = commandButtons.get(i); + enabledButtons.add( + button.copyWithIsEnabled(isEnabled(button, sessionCommands, playerCommands))); + } + return enabledButtons.build(); + } + + /** + * Returns whether the {@link CommandButton} is enabled given the available commands passed in. + * + * @param button The command button. + * @param sessionCommands The available session commands. + * @param playerCommands The available player commands. + * @return Whether the button is enabled given the available commands. + */ + /* package */ static boolean isEnabled( + CommandButton button, SessionCommands sessionCommands, Player.Commands playerCommands) { + return playerCommands.contains(button.playerCommand) + || (button.sessionCommand != null && sessionCommands.contains(button.sessionCommand)) + || (button.playerCommand != Player.COMMAND_INVALID + && sessionCommands.contains(button.playerCommand)); + } + // Bundleable implementation. private static final String FIELD_SESSION_COMMAND = Util.intToStringMaxRadix(0); 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 b56af18ee9..64026ab579 100644 --- a/libraries/session/src/main/java/androidx/media3/session/MediaControllerImplBase.java +++ b/libraries/session/src/main/java/androidx/media3/session/MediaControllerImplBase.java @@ -2481,7 +2481,8 @@ import org.checkerframework.checker.nullness.qual.NonNull; intersectedPlayerCommands = createIntersectedCommands(playerCommandsFromSession, playerCommandsFromPlayer); customLayout = - getEnabledCustomLayout(result.customLayout, intersectedPlayerCommands, sessionCommands); + CommandButton.getEnabledCommandButtons( + result.customLayout, sessionCommands, intersectedPlayerCommands); playerInfo = result.playerInfo; try { // Implementation for the local binder is no-op, @@ -2646,7 +2647,8 @@ import org.checkerframework.checker.nullness.qual.NonNull; this.sessionCommands = sessionCommands; ImmutableList oldCustomLayout = customLayout; customLayout = - getEnabledCustomLayout(customLayout, intersectedPlayerCommands, sessionCommands); + CommandButton.getEnabledCommandButtons( + customLayout, sessionCommands, intersectedPlayerCommands); customLayoutChanged = !customLayout.equals(oldCustomLayout); } if (intersectedPlayerCommandsChanged) { @@ -2694,7 +2696,8 @@ import org.checkerframework.checker.nullness.qual.NonNull; return; } ImmutableList oldCustomLayout = customLayout; - customLayout = getEnabledCustomLayout(layout, intersectedPlayerCommands, sessionCommands); + customLayout = + CommandButton.getEnabledCommandButtons(layout, sessionCommands, intersectedPlayerCommands); boolean hasCustomLayoutChanged = !Objects.equals(customLayout, oldCustomLayout); getInstance() .notifyControllerListener( @@ -2747,23 +2750,6 @@ import org.checkerframework.checker.nullness.qual.NonNull; } } - private static ImmutableList getEnabledCustomLayout( - List customLayout, - Player.Commands playerCommands, - SessionCommands sessionCommands) { - ImmutableList.Builder availableCustomLayout = new ImmutableList.Builder<>(); - for (int i = 0; i < customLayout.size(); i++) { - CommandButton button = customLayout.get(i); - boolean isEnabled = - playerCommands.contains(button.playerCommand) - || (button.sessionCommand != null && sessionCommands.contains(button.sessionCommand)) - || (button.playerCommand != Player.COMMAND_INVALID - && sessionCommands.contains(button.playerCommand)); - availableCustomLayout.add(button.copyWithIsEnabled(isEnabled)); - } - return availableCustomLayout.build(); - } - @Player.RepeatMode private static int convertRepeatModeForNavigation(@Player.RepeatMode int repeatMode) { return repeatMode == Player.REPEAT_MODE_ONE ? Player.REPEAT_MODE_OFF : repeatMode; diff --git a/libraries/session/src/test/java/androidx/media3/session/CommandButtonTest.java b/libraries/session/src/test/java/androidx/media3/session/CommandButtonTest.java index b1e7c1f0d5..cd13fb1051 100644 --- a/libraries/session/src/test/java/androidx/media3/session/CommandButtonTest.java +++ b/libraries/session/src/test/java/androidx/media3/session/CommandButtonTest.java @@ -22,6 +22,7 @@ import static org.junit.Assert.assertThrows; import android.os.Bundle; import androidx.media3.common.Player; import androidx.test.ext.junit.runners.AndroidJUnit4; +import com.google.common.collect.ImmutableList; import org.junit.Test; import org.junit.runner.RunWith; @@ -29,6 +30,90 @@ import org.junit.runner.RunWith; @RunWith(AndroidJUnit4.class) public class CommandButtonTest { + @Test + public void isEnabled_playerCommandAvailableOrUnavailableInPlayerCommands_isEnabledCorrectly() { + CommandButton button = + new CommandButton.Builder() + .setDisplayName("button") + .setIconResId(R.drawable.media3_notification_small_icon) + .setPlayerCommand(Player.COMMAND_SEEK_TO_NEXT) + .build(); + Player.Commands availablePlayerCommands = + Player.Commands.EMPTY.buildUpon().add(Player.COMMAND_SEEK_TO_NEXT).build(); + + assertThat(CommandButton.isEnabled(button, SessionCommands.EMPTY, Player.Commands.EMPTY)) + .isFalse(); + assertThat(CommandButton.isEnabled(button, SessionCommands.EMPTY, availablePlayerCommands)) + .isTrue(); + } + + @Test + public void isEnabled_playerCommandAvailableOrUnavailableInSessionCommands_isEnabledCorrectly() { + CommandButton button = + new CommandButton.Builder() + .setDisplayName("button") + .setIconResId(R.drawable.media3_notification_small_icon) + .setPlayerCommand(Player.COMMAND_SEEK_TO_NEXT) + .build(); + SessionCommands availableSessionCommands = + SessionCommands.EMPTY.buildUpon().add(Player.COMMAND_SEEK_TO_NEXT).build(); + + assertThat(CommandButton.isEnabled(button, SessionCommands.EMPTY, Player.Commands.EMPTY)) + .isFalse(); + assertThat(CommandButton.isEnabled(button, availableSessionCommands, Player.Commands.EMPTY)) + .isTrue(); + } + + @Test + public void isEnabled_sessionCommandAvailableOrUnavailable_isEnabledCorrectly() { + SessionCommand command1 = new SessionCommand("command1", Bundle.EMPTY); + CommandButton button = + new CommandButton.Builder() + .setDisplayName("button") + .setIconResId(R.drawable.media3_notification_small_icon) + .setSessionCommand(command1) + .build(); + SessionCommands availableSessionCommands = + SessionCommands.EMPTY.buildUpon().add(command1).build(); + + assertThat(CommandButton.isEnabled(button, SessionCommands.EMPTY, Player.Commands.EMPTY)) + .isFalse(); + assertThat(CommandButton.isEnabled(button, availableSessionCommands, Player.Commands.EMPTY)) + .isTrue(); + } + + @Test + public void getEnabledCommandButtons() { + CommandButton button1 = + new CommandButton.Builder() + .setDisplayName("button1") + .setIconResId(R.drawable.media3_notification_small_icon) + .setPlayerCommand(Player.COMMAND_SEEK_TO_PREVIOUS) + .build(); + SessionCommand command2 = new SessionCommand("command2", Bundle.EMPTY); + CommandButton button2 = + new CommandButton.Builder() + .setDisplayName("button2") + .setIconResId(R.drawable.media3_notification_small_icon) + .setSessionCommand(command2) + .build(); + SessionCommands availableSessionCommands = + SessionCommands.EMPTY.buildUpon().add(command2).build(); + Player.Commands availablePlayerCommands = + Player.Commands.EMPTY.buildUpon().add(Player.COMMAND_SEEK_TO_PREVIOUS).build(); + + assertThat( + CommandButton.getEnabledCommandButtons( + ImmutableList.of(button1, button2), SessionCommands.EMPTY, Player.Commands.EMPTY)) + .containsExactly(button1, button2); + assertThat( + CommandButton.getEnabledCommandButtons( + ImmutableList.of(button1, button2), + availableSessionCommands, + availablePlayerCommands)) + .containsExactly(button1.copyWithIsEnabled(true), button2.copyWithIsEnabled(true)); + } + @Test public void equals() { assertThat(