diff options
| author | 2018-12-27 11:18:03 +0000 | |
|---|---|---|
| committer | 2018-12-27 11:18:03 +0000 | |
| commit | ab1ad60ec8615b6d1d5a2950a25c738848635495 (patch) | |
| tree | d31527a646ecab4643e4c84a46cb9d031f6748d4 | |
| parent | ba281ccf76984b090a71d482c8b2762d514cd4b9 (diff) | |
| parent | bf37bd47fdddb1f9920a090735e1b09d7b36abe8 (diff) | |
Merge "Add Session2Command and Session2CommandGroup"
| -rw-r--r-- | media/java/android/media/Session2Command.java | 596 | ||||
| -rw-r--r-- | media/java/android/media/Session2CommandGroup.java | 321 |
2 files changed, 917 insertions, 0 deletions
diff --git a/media/java/android/media/Session2Command.java b/media/java/android/media/Session2Command.java new file mode 100644 index 000000000000..a5e2ae4488a9 --- /dev/null +++ b/media/java/android/media/Session2Command.java @@ -0,0 +1,596 @@ +/* + * Copyright 2018 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.media; + +import android.annotation.IntDef; +import android.annotation.NonNull; +import android.annotation.Nullable; +import android.os.Bundle; +import android.os.Parcel; +import android.os.Parcelable; +import android.text.TextUtils; +import android.util.ArrayMap; + +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.util.List; +import java.util.Objects; + +/** + * Define a command that a {@link MediaController2} can send to a {@link MediaSession2}. + * <p> + * If {@link #getCommandCode()} isn't {@link #COMMAND_CODE_CUSTOM}), it's predefined command. + * If {@link #getCommandCode()} is {@link #COMMAND_CODE_CUSTOM}), it's custom command and + * {@link #getCustomCommand()} shouldn't be {@code null}. + * <p> + * This API is not generally intended for third party application developers. + * Use the <a href="{@docRoot}jetpack/androidx.html">AndroidX</a> + * <a href="{@docRoot}reference/androidx/media2/package-summary.html">Media2 Library</a> + * for consistent behavior across all devices. + * </p> + * @hide + */ +public final class Session2Command implements Parcelable { + /** + * The first version of session commands. This version is for commands introduced in API 29. + * <p> + * This would be used to specify which commands should be added by + * {@link Session2CommandGroup.Builder#addAllPredefinedCommands(int)} + * + * @see Session2CommandGroup.Builder#addAllPredefinedCommands(int) + */ + public static final int COMMAND_VERSION_1 = 1; + + /** + * @hide + */ + public static final int COMMAND_VERSION_CURRENT = COMMAND_VERSION_1; + + /** + * @hide + */ + @IntDef({COMMAND_VERSION_1}) + @Retention(RetentionPolicy.SOURCE) + public @interface CommandVersion {} + + /** + * @hide + */ + @IntDef({COMMAND_CODE_CUSTOM, + COMMAND_CODE_PLAYER_PLAY, + COMMAND_CODE_PLAYER_PAUSE, + COMMAND_CODE_PLAYER_PREPARE, + COMMAND_CODE_PLAYER_SEEK_TO, + COMMAND_CODE_PLAYER_SET_SPEED, + COMMAND_CODE_PLAYER_GET_PLAYLIST, + COMMAND_CODE_PLAYER_SET_PLAYLIST, + COMMAND_CODE_PLAYER_SKIP_TO_PLAYLIST_ITEM, + COMMAND_CODE_PLAYER_SKIP_TO_PREVIOUS_PLAYLIST_ITEM, + COMMAND_CODE_PLAYER_SKIP_TO_NEXT_PLAYLIST_ITEM, + COMMAND_CODE_PLAYER_SET_SHUFFLE_MODE, + COMMAND_CODE_PLAYER_SET_REPEAT_MODE, + COMMAND_CODE_PLAYER_GET_PLAYLIST_METADATA, + COMMAND_CODE_PLAYER_ADD_PLAYLIST_ITEM, + COMMAND_CODE_PLAYER_REMOVE_PLAYLIST_ITEM, + COMMAND_CODE_PLAYER_REPLACE_PLAYLIST_ITEM, + COMMAND_CODE_PLAYER_GET_CURRENT_MEDIA_ITEM, + COMMAND_CODE_PLAYER_UPDATE_LIST_METADATA, + COMMAND_CODE_PLAYER_SET_MEDIA_ITEM, + COMMAND_CODE_VOLUME_SET_VOLUME, + COMMAND_CODE_VOLUME_ADJUST_VOLUME, + COMMAND_CODE_SESSION_FAST_FORWARD, + COMMAND_CODE_SESSION_REWIND, + COMMAND_CODE_SESSION_SKIP_FORWARD, + COMMAND_CODE_SESSION_SKIP_BACKWARD, + COMMAND_CODE_SESSION_SET_RATING, + COMMAND_CODE_LIBRARY_GET_LIBRARY_ROOT, + COMMAND_CODE_LIBRARY_SUBSCRIBE, + COMMAND_CODE_LIBRARY_UNSUBSCRIBE, + COMMAND_CODE_LIBRARY_GET_CHILDREN, + COMMAND_CODE_LIBRARY_GET_ITEM, + COMMAND_CODE_LIBRARY_SEARCH, + COMMAND_CODE_LIBRARY_GET_SEARCH_RESULT + }) + @Retention(RetentionPolicy.SOURCE) + public @interface CommandCode {} + + /** + * Command code for the custom command which can be defined by string action in the + * {@link Session2Command}. + */ + public static final int COMMAND_CODE_CUSTOM = 0; + + //////////////////////////////////////////////////////////////////////////////////////////////// + // Player commands (i.e. commands to {@link Session2Player}) + //////////////////////////////////////////////////////////////////////////////////////////////// + static final ArrayMap<Integer, Range> VERSION_PLAYER_COMMANDS_MAP = new ArrayMap<>(); + static final ArrayMap<Integer, Range> VERSION_PLAYER_PLAYLIST_COMMANDS_MAP = new ArrayMap<>(); + + // TODO: check the link tag, and reassign int values properly. + /** + * Command code for {@link MediaController2#play()}. + * <p> + * Command would be sent directly to the player if the session doesn't reject the request + * through the {@link Session2Callback#onCommandRequest(MediaSession2, Controller2Info, + * Session2Command)}. + * <p> + * Code version is {@link #COMMAND_VERSION_1}. + */ + public static final int COMMAND_CODE_PLAYER_PLAY = 10000; + + /** + * Command code for {@link MediaController2#pause()}. + * <p> + * Command would be sent directly to the player if the session doesn't reject the request + * through the {@link Session2Callback#onCommandRequest(MediaSession2, Controller2Info, + * Session2Command)}. + * <p> + * Code version is {@link #COMMAND_VERSION_1}. + */ + public static final int COMMAND_CODE_PLAYER_PAUSE = 10001; + + /** + * Command code for {@link MediaController2#prepare()}. + * <p> + * Command would be sent directly to the player if the session doesn't reject the request + * through the {@link Session2Callback#onCommandRequest(MediaSession2, Controller2Info, + * Session2Command)}. + * <p> + * Code version is {@link #COMMAND_VERSION_1}. + */ + public static final int COMMAND_CODE_PLAYER_PREPARE = 10002; + + /** + * Command code for {@link MediaController2#seekTo(long)}. + * <p> + * Command would be sent directly to the player if the session doesn't reject the request + * through the {@link Session2Callback#onCommandRequest(MediaSession2, Controller2Info, + * Session2Command)}. + * <p> + * Code version is {@link #COMMAND_VERSION_1}. + */ + public static final int COMMAND_CODE_PLAYER_SEEK_TO = 10003; + + /** + * Command code for {@link MediaController2#setPlaybackSpeed(float)}}. + * <p> + * Command would be sent directly to the player if the session doesn't reject the request + * through the {@link Session22Callback#onCommandRequest(MediaSession2, Controller2Info, + * Session2Command)}. + * <p> + * Code version is {@link #COMMAND_VERSION_1}. + */ + public static final int COMMAND_CODE_PLAYER_SET_SPEED = 10004; + + /** + * Command code for {@link MediaController2#getPlaylist()}. This will expose metadata + * information to the controller. + * <p> + * Code version is {@link #COMMAND_VERSION_1}. + */ + public static final int COMMAND_CODE_PLAYER_GET_PLAYLIST = 10005; + + /** + * Command code for {@link MediaController2#setPlaylist(List, MediaMetadata)}. + * <p> + * Command would be sent directly to the player if the session doesn't reject the request + * through the + * {@link Session2Callback#onCommandRequest(MediaSession2, Controller2Info, Session2Command)}. + * <p> + * Code version is {@link #COMMAND_VERSION_1}. + */ + public static final int COMMAND_CODE_PLAYER_SET_PLAYLIST = 10006; + + /** + * Command code for {@link MediaController2#skipToPlaylistItem(int)}. + * <p> + * Command would be sent directly to the player if the session doesn't reject the request + * through the + * {@link Session2Callback#onCommandRequest(MediaSession2, Controller2Info, Session2Command)}. + * <p> + * Code version is {@link #COMMAND_VERSION_1}. + */ + public static final int COMMAND_CODE_PLAYER_SKIP_TO_PLAYLIST_ITEM = 10007; + + /** + * Command code for {@link MediaController2#skipToPreviousPlaylistItem()}. + * <p> + * Command would be sent directly to the player if the session doesn't reject the request + * through the {@link Session2Callback#onCommandRequest( + * MediaSession2, Controller2Info, Session2Command)}. + * <p> + * Code version is {@link #COMMAND_VERSION_1}. + */ + public static final int COMMAND_CODE_PLAYER_SKIP_TO_PREVIOUS_PLAYLIST_ITEM = 10008; + + /** + * Command code for {@link MediaController2#skipToNextPlaylistItem()}. + * <p> + * Command would be sent directly to the player if the session doesn't reject the request + * through the {@link Session2Callback#onCommandRequest( + * MediaSession2, Controller2Info, Session2Command)}. + * <p> + * Code version is {@link #COMMAND_VERSION_1}. + */ + + public static final int COMMAND_CODE_PLAYER_SKIP_TO_NEXT_PLAYLIST_ITEM = 10009; + + /** + * Command code for {@link MediaController2#setShuffleMode(int)}. + * <p> + * Command would be sent directly to the player if the session doesn't reject the request + * through the + * {@link Session2Callback#onCommandRequest(MediaSession2, Controller2Info, Session2Command)}. + * <p> + * Code version is {@link #COMMAND_VERSION_1}. + */ + public static final int COMMAND_CODE_PLAYER_SET_SHUFFLE_MODE = 10010; + + /** + * Command code for {@link MediaController2#setRepeatMode(int)}. + * <p> + * Command would be sent directly to the player if the session doesn't reject the request + * through the + * {@link Session2Callback#onCommandRequest(MediaSession2, Controller2Info, Session2Command)}. + * <p> + * Code version is {@link #COMMAND_VERSION_1}. + */ + public static final int COMMAND_CODE_PLAYER_SET_REPEAT_MODE = 10011; + + /** + * Command code for {@link MediaController2#getPlaylistMetadata()}. This will expose metadata + * information to the controller. + * <p> + * Code version is {@link #COMMAND_VERSION_1}. + */ + public static final int COMMAND_CODE_PLAYER_GET_PLAYLIST_METADATA = 10012; + + /** + * Command code for {@link MediaController2#addPlaylistItem(int, String)}. + * <p> + * Command would be sent directly to the player if the session doesn't reject the request + * through the + * {@link Session2Callback#onCommandRequest(MediaSession2, Controller2Info, Session2Command)}. + * <p> + * Code version is {@link #COMMAND_VERSION_1}. + */ + public static final int COMMAND_CODE_PLAYER_ADD_PLAYLIST_ITEM = 10013; + + /** + * Command code for {@link MediaController2#removePlaylistItem(int, String)}. + * <p> + * Command would be sent directly to the player if the session doesn't reject the request + * through the + * {@link Session2Callback#onCommandRequest(MediaSession2, Controller2Info, Session2Command)}. + * <p> + * Code version is {@link #COMMAND_VERSION_1}. + */ + public static final int COMMAND_CODE_PLAYER_REMOVE_PLAYLIST_ITEM = 10014; + + /** + * Command code for {@link MediaController2#replacePlaylistItem(int, String)}. + * <p> + * Command would be sent directly to the player if the session doesn't reject the request + * through the + * {@link Session2Callback#onCommandRequest(MediaSession2, Controller2Info, Session2Command)}. + * <p> + * Code version is {@link #COMMAND_VERSION_1}. + */ + public static final int COMMAND_CODE_PLAYER_REPLACE_PLAYLIST_ITEM = 10015; + + /** + * Command code for {@link MediaController2#getCurrentMediaItem()}. This will expose metadata + * information to the controller. + * <p> + * Code version is {@link #COMMAND_VERSION_1}. + */ + public static final int COMMAND_CODE_PLAYER_GET_CURRENT_MEDIA_ITEM = 10016; + + /** + * Command code for {@link MediaController2#updatePlaylistMetadata(MediaMetadata)}. + * <p> + * Command would be sent directly to the player if the session doesn't reject the request + * through the + * {@link Session2Callback#onCommandRequest(MediaSession2, Controller2Info, Session2Command)}. + * <p> + * Code version is {@link #COMMAND_VERSION_1}. + */ + public static final int COMMAND_CODE_PLAYER_UPDATE_LIST_METADATA = 10017; + + /** + * Command code for {@link MediaController2#setMediaItem(String)}. + * <p> + * Command would be sent directly to the player if the session doesn't reject the request + * through the + * {@link Session2Callback#onCommandRequest(MediaSession2, Controller2Info, Session2Command)}. + * <p> + * Code version is {@link #COMMAND_VERSION_1}. + */ + public static final int COMMAND_CODE_PLAYER_SET_MEDIA_ITEM = 10018; + + static { + VERSION_PLAYER_COMMANDS_MAP.put(COMMAND_VERSION_1, + new Range(COMMAND_CODE_PLAYER_PLAY, COMMAND_CODE_PLAYER_SET_MEDIA_ITEM)); + } + + static { + VERSION_PLAYER_PLAYLIST_COMMANDS_MAP.put(COMMAND_VERSION_1, + new Range(COMMAND_CODE_PLAYER_GET_PLAYLIST, + COMMAND_CODE_PLAYER_SET_MEDIA_ITEM)); + } + + //////////////////////////////////////////////////////////////////////////////////////////////// + // Volume commands (i.e. commands to {@link AudioManager} or {@link RouteMediaPlayer}) + //////////////////////////////////////////////////////////////////////////////////////////////// + static final ArrayMap<Integer, Range> VERSION_VOLUME_COMMANDS_MAP = new ArrayMap<>(); + + /** + * Command code for {@link MediaController2#setVolumeTo(int, int)}. + * <p> + * <p> + * If the session doesn't reject the request through the + * {@link Session2Callback#onCommandRequest(MediaSession2, Controller2Info, Session2Command)}, + * command would adjust the device volume. It would send to the player directly only if it's + * remote player. See RouteMediaPlayer for a remote player. + * <p> + * Code version is {@link #COMMAND_VERSION_1}. + */ + public static final int COMMAND_CODE_VOLUME_SET_VOLUME = 30000; + + /** + * Command code for {@link MediaController2#adjustVolume(int, int)}. + * <p> + * If the session doesn't reject the request through the + * {@link Session2Callback#onCommandRequest(MediaSession2, Controller2Info, Session2Command)}, + * command would adjust the device volume. It would send to the player directly only if it's + * remote player. See RouteMediaPlayer for a remote player. + * <p> + * Code version is {@link #COMMAND_VERSION_1}. + */ + public static final int COMMAND_CODE_VOLUME_ADJUST_VOLUME = 30001; + + static { + VERSION_VOLUME_COMMANDS_MAP.put(COMMAND_VERSION_1, + new Range(COMMAND_CODE_VOLUME_SET_VOLUME, + COMMAND_CODE_VOLUME_ADJUST_VOLUME)); + } + + //////////////////////////////////////////////////////////////////////////////////////////////// + // Session commands (i.e. commands to {@link MediaSession2#Session2Callback}) + //////////////////////////////////////////////////////////////////////////////////////////////// + static final ArrayMap<Integer, Range> VERSION_SESSION_COMMANDS_MAP = new ArrayMap<>(); + + /** + * Command code for {@link MediaController2#fastForward()}. + * <p> + * Code version is {@link #COMMAND_VERSION_1}. + */ + public static final int COMMAND_CODE_SESSION_FAST_FORWARD = 40000; + + /** + * Command code for {@link MediaController2#rewind()}. + * <p> + * Code version is {@link #COMMAND_VERSION_1}. + */ + public static final int COMMAND_CODE_SESSION_REWIND = 40001; + + /** + * Command code for {@link MediaController2#skipForward()}. + * <p> + * Code version is {@link #COMMAND_VERSION_1}. + */ + public static final int COMMAND_CODE_SESSION_SKIP_FORWARD = 40002; + + /** + * Command code for {@link MediaController2#skipBackward()}. + * <p> + * Code version is {@link #COMMAND_VERSION_1}. + */ + public static final int COMMAND_CODE_SESSION_SKIP_BACKWARD = 40003; + + /** + * Command code for {@link MediaController2#setRating(String, Rating)}. + * <p> + * Code version is {@link #COMMAND_VERSION_1}. + */ + public static final int COMMAND_CODE_SESSION_SET_RATING = 40010; + + public static final Parcelable.Creator<Session2Command> CREATOR = + new Parcelable.Creator<Session2Command>() { + @Override + public Session2Command createFromParcel(Parcel in) { + return new Session2Command(in); + } + + @Override + public Session2Command[] newArray(int size) { + return new Session2Command[size]; + } + }; + + static { + VERSION_SESSION_COMMANDS_MAP.put(COMMAND_VERSION_1, + new Range(COMMAND_CODE_SESSION_FAST_FORWARD, COMMAND_CODE_SESSION_SET_RATING)); + } + + //////////////////////////////////////////////////////////////////////////////////////////////// + // Session commands (i.e. commands to {@link MediaLibrarySession#MediaLibrarySessionCallback}) + //////////////////////////////////////////////////////////////////////////////////////////////// + static final ArrayMap<Integer, Range> VERSION_LIBRARY_COMMANDS_MAP = new ArrayMap<>(); + + /** + * Command code for {@link MediaBrowser2#getLibraryRoot(Library2Params)}. + * <p> + * Code version is {@link #COMMAND_VERSION_1}. + */ + public static final int COMMAND_CODE_LIBRARY_GET_LIBRARY_ROOT = 50000; + + /** + * Command code for {@link MediaBrowser2#subscribe(String, Library2Params)}. + * <p> + * Code version is {@link #COMMAND_VERSION_1}. + */ + public static final int COMMAND_CODE_LIBRARY_SUBSCRIBE = 50001; + + /** + * Command code for {@link MediaBrowser2#unsubscribe(String)}. + * <p> + * Code version is {@link #COMMAND_VERSION_1}. + */ + public static final int COMMAND_CODE_LIBRARY_UNSUBSCRIBE = 50002; + + /** + * Command code for {@link MediaBrowser2#getChildren(String, int, int, Library2Params)}. + * <p> + * Code version is {@link #COMMAND_VERSION_1}. + */ + public static final int COMMAND_CODE_LIBRARY_GET_CHILDREN = 50003; + + /** + * Command code for {@link MediaBrowser2#getItem(String)}. + * <p> + * Code version is {@link #COMMAND_VERSION_1}. + */ + public static final int COMMAND_CODE_LIBRARY_GET_ITEM = 50004; + + /** + * Command code for {@link MediaBrowser2#search(String, LibraryParams)}. + * <p> + * Code version is {@link #COMMAND_VERSION_1}. + */ + public static final int COMMAND_CODE_LIBRARY_SEARCH = 50005; + + /** + * Command code for {@link MediaBrowser2#getSearchResult(String, int, int, Library2Params)}. + * <p> + * Code version is {@link #COMMAND_VERSION_1}. + */ + public static final int COMMAND_CODE_LIBRARY_GET_SEARCH_RESULT = 50006; + + static { + VERSION_LIBRARY_COMMANDS_MAP.put(COMMAND_VERSION_1, + new Range(COMMAND_CODE_LIBRARY_GET_LIBRARY_ROOT, + COMMAND_CODE_LIBRARY_GET_SEARCH_RESULT)); + } + + @CommandCode private final int mCommandCode; + // Nonnull if it's custom command + private final String mCustomCommand; + private final Bundle mExtras; + + /** + * Constructor for creating a predefined command. + * + * @param commandCode A command code for predefined command. + */ + public Session2Command(@CommandCode int commandCode) { + if (commandCode == COMMAND_CODE_CUSTOM) { + throw new IllegalArgumentException("commandCode shouldn't be COMMAND_CODE_CUSTOM"); + } + mCommandCode = commandCode; + mCustomCommand = null; + mExtras = null; + } + + /** + * Constructor for creating a custom command. + * + * @param action The action of this custom command. + * @param extras An extra bundle for this custom command. + */ + public Session2Command(@NonNull String action, @Nullable Bundle extras) { + if (action == null) { + throw new IllegalArgumentException("action shouldn't be null"); + } + mCommandCode = COMMAND_CODE_CUSTOM; + mCustomCommand = action; + mExtras = extras; + } + + /** + * Used by parcelable creator. + */ + @SuppressWarnings("WeakerAccess") /* synthetic access */ + Session2Command(Parcel in) { + mCommandCode = in.readInt(); + mCustomCommand = in.readString(); + mExtras = in.readBundle(); + } + + /** + * Gets the command code of a predefined command. + * This will return {@link #COMMAND_CODE_CUSTOM} for a custom command. + */ + public @CommandCode int getCommandCode() { + return mCommandCode; + } + + /** + * Gets the action of a custom command. + * This will return {@code null} for a predefined command. + */ + public @Nullable String getCustomCommand() { + return mCustomCommand; + } + + /** + * Gets the extra bundle of a custom command. + * This will return {@code null} for a predefined command. + */ + public @Nullable Bundle getExtras() { + return mExtras; + } + + @Override + public int describeContents() { + return 0; + } + + @Override + public void writeToParcel(Parcel dest, int flags) { + dest.writeInt(mCommandCode); + dest.writeString(mCustomCommand); + dest.writeBundle(mExtras); + } + + @Override + public boolean equals(Object obj) { + if (!(obj instanceof Session2Command)) { + return false; + } + Session2Command other = (Session2Command) obj; + return mCommandCode == other.mCommandCode + && TextUtils.equals(mCustomCommand, other.mCustomCommand); + } + + @Override + public int hashCode() { + return Objects.hash(mCustomCommand, mCommandCode); + } + + @SuppressWarnings("WeakerAccess") /* synthetic access */ + static final class Range { + public final int lower; + public final int upper; + + Range(int lower, int upper) { + this.lower = lower; + this.upper = upper; + } + } +} + diff --git a/media/java/android/media/Session2CommandGroup.java b/media/java/android/media/Session2CommandGroup.java new file mode 100644 index 000000000000..de34fe5c8a2a --- /dev/null +++ b/media/java/android/media/Session2CommandGroup.java @@ -0,0 +1,321 @@ +/* + * Copyright 2018 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.media; + +import static android.media.Session2Command.COMMAND_CODE_CUSTOM; +import static android.media.Session2Command.COMMAND_VERSION_1; + +import android.annotation.NonNull; +import android.annotation.Nullable; +import android.media.Session2Command.Range; +import android.os.Parcel; +import android.os.Parcelable; +import android.util.ArrayMap; + +import java.util.Collection; +import java.util.HashSet; +import java.util.Set; + +/** + * A set of {@link Session2Command} which represents a command group. + * <p> + * This API is not generally intended for third party application developers. + * Use the <a href="{@docRoot}tools/extras/support-library.html">Support Library</a> + * {@link androidx.media2.SessionCommandGroup} for consistent behavior across all devices. + * </p> + * @hide + */ +public final class Session2CommandGroup implements Parcelable { + private static final String TAG = "Session2CommandGroup"; + + public static final Parcelable.Creator<Session2CommandGroup> CREATOR = + new Parcelable.Creator<Session2CommandGroup>() { + @Override + public Session2CommandGroup createFromParcel(Parcel in) { + return new Session2CommandGroup(in); + } + + @Override + public Session2CommandGroup[] newArray(int size) { + return new Session2CommandGroup[size]; + } + }; + + Set<Session2Command> mCommands = new HashSet<>(); + + /** + * Default Constructor. + */ + public Session2CommandGroup() { } + + /** + * Creates a new Session2CommandGroup with commands copied from another object. + * + * @param commands The collection of commands to copy. + */ + public Session2CommandGroup(@Nullable Collection<Session2Command> commands) { + if (commands != null) { + mCommands.addAll(commands); + } + } + + /** + * Used by parcelable creator. + */ + @SuppressWarnings("WeakerAccess") /* synthetic access */ + Session2CommandGroup(Parcel in) { + Session2Command[] commands = in.readParcelableArray( + Session2Command.class.getClassLoader(), Session2Command.class); + if (commands != null) { + for (Session2Command command : commands) { + mCommands.add(command); + } + } + } + + /** + * Adds a command to this command group. + * + * @param command A command to add. Shouldn't be {@code null}. + * @hide TODO remove this method + */ + public void addCommand(@NonNull Session2Command command) { + if (command == null) { + throw new IllegalArgumentException("command shouldn't be null"); + } + if (!hasCommand(command)) { + mCommands.add(command); + } + } + + /** + * Adds a predefined command with given {@code commandCode} to this command group. + * + * @param commandCode A command code to add. + * Shouldn't be {@link Session2Command#COMMAND_CODE_CUSTOM}. + * @hide TODO remove this method + */ + public void addCommand(@Session2Command.CommandCode int commandCode) { + if (commandCode == COMMAND_CODE_CUSTOM) { + throw new IllegalArgumentException( + "Use addCommand(Session2Command) for COMMAND_CODE_CUSTOM."); + } + if (!hasCommand(commandCode)) { + mCommands.add(new Session2Command(commandCode)); + } + } + + /** + * Checks whether this command group has a command that matches given {@code command}. + * + * @param command A command to find. Shouldn't be {@code null}. + */ + public boolean hasCommand(@NonNull Session2Command command) { + if (command == null) { + throw new IllegalArgumentException("command shouldn't be null"); + } + return mCommands.contains(command); + } + + /** + * Checks whether this command group has a command that matches given {@code commandCode}. + * + * @param commandCode A command code to find. + * Shouldn't be {@link Session2Command#COMMAND_CODE_CUSTOM}. + */ + public boolean hasCommand(@Session2Command.CommandCode int commandCode) { + if (commandCode == COMMAND_CODE_CUSTOM) { + throw new IllegalArgumentException("Use hasCommand(Command) for custom command"); + } + for (Session2Command command : mCommands) { + if (command.getCommandCode() == commandCode) { + return true; + } + } + return false; + } + + /** + * Gets all commands of this command group. + */ + public @NonNull Set<Session2Command> getCommands() { + return new HashSet<>(mCommands); + } + + @Override + public int describeContents() { + return 0; + } + + @Override + public void writeToParcel(Parcel dest, int flags) { + dest.writeParcelableArray((Session2Command[]) mCommands.toArray(), 0); + } + + /** + * Builds a {@link Session2CommandGroup} object. + */ + public static final class Builder { + private Set<Session2Command> mCommands; + + public Builder() { + mCommands = new HashSet<>(); + } + + /** + * Creates a new builder for {@link Session2CommandGroup} with commands copied from another + * {@link Session2CommandGroup} object. + * @param commandGroup + */ + public Builder(@NonNull Session2CommandGroup commandGroup) { + mCommands = commandGroup.getCommands(); + } + + /** + * Adds a command to this command group. + * + * @param command A command to add. Shouldn't be {@code null}. + */ + public @NonNull Builder addCommand(@NonNull Session2Command command) { + if (command == null) { + throw new IllegalArgumentException("command shouldn't be null"); + } + mCommands.add(command); + return this; + } + + /** + * Adds a predefined command with given {@code commandCode} to this command group. + * + * @param commandCode A command code to add. + * Shouldn't be {@link Session2Command#COMMAND_CODE_CUSTOM}. + */ + public @NonNull Builder addCommand(@Session2Command.CommandCode int commandCode) { + if (commandCode == COMMAND_CODE_CUSTOM) { + throw new IllegalArgumentException( + "Use addCommand(Session2Command) for COMMAND_CODE_CUSTOM."); + } + mCommands.add(new Session2Command(commandCode)); + return this; + } + + /** + * Adds all predefined session commands except for the commands added after the specified + * version without default implementation. This provides convenient way to add commands + * with implementation. + * + * @param version command version + * @see Session2Command#COMMAND_VERSION_1 + * @see + * MediaSession2.Session2Callback#onConnect + */ + public @NonNull Builder addAllPredefinedCommands( + @Session2Command.CommandVersion int version) { + if (version != COMMAND_VERSION_1) { + throw new IllegalArgumentException("Unknown command version " + version); + } + addAllPlayerCommands(version); + addAllVolumeCommands(version); + addAllSessionCommands(version); + addAllLibraryCommands(version); + return this; + } + + /** + * Removes a command from this group which matches given {@code command}. + * + * @param command A command to find. Shouldn't be {@code null}. + */ + public @NonNull Builder removeCommand(@NonNull Session2Command command) { + if (command == null) { + throw new IllegalArgumentException("command shouldn't be null"); + } + mCommands.remove(command); + return this; + } + + /** + * Removes a command from this group which matches given {@code commandCode}. + * + * @param commandCode A command code to find. + * Shouldn't be {@link Session2Command#COMMAND_CODE_CUSTOM}. + */ + public @NonNull Builder removeCommand(@Session2Command.CommandCode int commandCode) { + if (commandCode == COMMAND_CODE_CUSTOM) { + throw new IllegalArgumentException("commandCode shouldn't be COMMAND_CODE_CUSTOM"); + } + mCommands.remove(new Session2Command(commandCode)); + return this; + } + + @NonNull Builder addAllPlayerCommands(@Session2Command.CommandVersion int version) { + addCommands(version, Session2Command.VERSION_PLAYER_COMMANDS_MAP); + return this; + } + + @NonNull Builder addAllPlayerCommands(@Session2Command.CommandVersion int version, + boolean includePlaylistCommands) { + if (includePlaylistCommands) { + return addAllPlayerCommands(version); + } + for (int i = COMMAND_VERSION_1; i <= version; i++) { + Range include = Session2Command.VERSION_PLAYER_COMMANDS_MAP.get(i); + Range exclude = Session2Command.VERSION_PLAYER_PLAYLIST_COMMANDS_MAP.get(i); + for (int code = include.lower; code <= include.upper; code++) { + if (code < exclude.lower && code > exclude.upper) { + addCommand(code); + } + } + } + return this; + } + + @NonNull Builder addAllVolumeCommands(@Session2Command.CommandVersion int version) { + addCommands(version, Session2Command.VERSION_VOLUME_COMMANDS_MAP); + return this; + } + + @NonNull Builder addAllSessionCommands(@Session2Command.CommandVersion int version) { + addCommands(version, Session2Command.VERSION_SESSION_COMMANDS_MAP); + return this; + } + + @NonNull Builder addAllLibraryCommands(@Session2Command.CommandVersion int version) { + addCommands(version, Session2Command.VERSION_LIBRARY_COMMANDS_MAP); + return this; + } + + private void addCommands( + @Session2Command.CommandVersion int version, ArrayMap<Integer, Range> map) { + for (int i = COMMAND_VERSION_1; i <= version; i++) { + Range range = map.get(i); + for (int code = range.lower; code <= range.upper; code++) { + addCommand(code); + } + } + } + + /** + * Builds {@link Session2CommandGroup}. + * + * @return a new {@link Session2CommandGroup}. + */ + public @NonNull Session2CommandGroup build() { + return new Session2CommandGroup(mCommands); + } + } +} |