Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 7800fef9 authored by Jaewan Kim's avatar Jaewan Kim Committed by Android (Google) Code Review
Browse files

Merge "MediaSession2: Add MediaSession2.CommandButton"

parents f68e5b63 e542bf01
Loading
Loading
Loading
Loading
+2 −0
Original line number Diff line number Diff line
@@ -45,6 +45,8 @@ oneway interface IMediaSession2Callback {
    // TODO(jaewan): Is term 'accepted/rejected' correct? For permission, 'grant' is used.
    void onConnectionChanged(IMediaSession2 sessionBinder, in Bundle commandGroup);

    void onCustomLayoutChanged(in List<Bundle> commandButtonlist);

    //////////////////////////////////////////////////////////////////////////////////////////////
    // Browser sepcific
    //////////////////////////////////////////////////////////////////////////////////////////////
+13 −0
Original line number Diff line number Diff line
@@ -19,6 +19,7 @@ package android.media;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.content.Context;
import android.media.MediaSession2.CommandButton;
import android.media.MediaSession2.CommandGroup;
import android.media.MediaSession2.ControllerInfo;
import android.media.session.MediaSessionManager;
@@ -26,6 +27,8 @@ import android.media.session.PlaybackState;
import android.media.update.ApiLoader;
import android.media.update.MediaController2Provider;
import android.os.Handler;

import java.util.List;
import java.util.concurrent.Executor;

/**
@@ -81,6 +84,16 @@ public class MediaController2 extends MediaPlayerBase {
         * You don't need to call {@link #release()} after this.
         */
        public void onDisconnected() { }

        /**
         * Called when the session sets the custom layout through the
         * {@link MediaSession2#setCustomLayout(ControllerInfo, List)}.
         * <p>
         * Can be called before {@link #onConnected(CommandGroup)} is called.
         *
         * @param layout
         */
        public void onCustomLayoutChanged(List<CommandButton> layout) { }
    }

    private final MediaController2Provider mProvider;
+196 −3
Original line number Diff line number Diff line
@@ -95,11 +95,11 @@ public class MediaSession2 extends MediaPlayerBase {
    // TODO(jaewan): Move this into the updatable.
    public static final class Command {
        private static final String KEY_COMMAND_CODE
                = "android.media.mediasession2.command.command_command";
                = "android.media.media_session2.command.command_code";
        private static final String KEY_COMMAND_CUSTOM_COMMAND
                = "android.media.mediasession2.command.custom_command";
                = "android.media.media_session2.command.custom_command";
        private static final String KEY_COMMAND_EXTRA
                = "android.media.mediasession2.command.extra";
                = "android.media.media_session2.command.extra";

        private final int mCommandCode;
        // Nonnull if it's custom command
@@ -482,11 +482,180 @@ public class MediaSession2 extends MediaPlayerBase {

        @Override
        public String toString() {
            // TODO(jaewan): Move this to updatable.
            return "ControllerInfo {pkg=" + getPackageName() + ", uid=" + getUid() + ", trusted="
                    + isTrusted() + "}";
        }
    }

    /**
     * Button for a {@link Command} that will be shown by the controller.
     * <p>
     * It's up to the controller's decision to respect or ignore this customization request.
     */
    // TODO(jaewan): Move this to updatable.
    public static class CommandButton {
        private static final String KEY_COMMAND
                = "android.media.media_session2.command_button.command";
        private static final String KEY_ICON_RES_ID
                = "android.media.media_session2.command_button.icon_res_id";
        private static final String KEY_DISPLAY_NAME
                = "android.media.media_session2.command_button.display_name";
        private static final String KEY_EXTRA
                = "android.media.media_session2.command_button.extra";
        private static final String KEY_ENABLED
                = "android.media.media_session2.command_button.enabled";

        private Command mCommand;
        private int mIconResId;
        private String mDisplayName;
        private Bundle mExtra;
        private boolean mEnabled;

        private CommandButton(@Nullable Command command, int iconResId,
                @Nullable String displayName, Bundle extra, boolean enabled) {
            mCommand = command;
            mIconResId = iconResId;
            mDisplayName = displayName;
            mExtra = extra;
            mEnabled = enabled;
        }

        /**
         * Get command associated with this button. Can be {@code null} if the button isn't enabled
         * and only providing placeholder.
         *
         * @return command or {@code null}
         */
        public @Nullable Command getCommand() {
            return mCommand;
        }

        /**
         * Resource id of the button in this package. Can be {@code 0} if the command is predefined
         * and custom icon isn't needed.
         *
         * @return resource id of the icon. Can be {@code 0}.
         */
        public int getIconResId() {
            return mIconResId;
        }

        /**
         * Display name of the button. Can be {@code null} or empty if the command is predefined
         * and custom name isn't needed.
         *
         * @return custom display name. Can be {@code null} or empty.
         */
        public @Nullable String getDisplayName() {
            return mDisplayName;
        }

        /**
         * Extra information of the button. It's private information between session and controller.
         *
         * @return
         */
        public @Nullable Bundle getExtra() {
            return mExtra;
        }

        /**
         * Return whether it's enabled
         *
         * @return {@code true} if enabled. {@code false} otherwise.
         */
        public boolean isEnabled() {
            return mEnabled;
        }

        /**
         * @hide
         */
        // TODO(jaewan): @SystemApi
        public @NonNull Bundle toBundle() {
            Bundle bundle = new Bundle();
            bundle.putBundle(KEY_COMMAND, mCommand.toBundle());
            bundle.putInt(KEY_ICON_RES_ID, mIconResId);
            bundle.putString(KEY_DISPLAY_NAME, mDisplayName);
            bundle.putBundle(KEY_EXTRA, mExtra);
            bundle.putBoolean(KEY_ENABLED, mEnabled);
            return bundle;
        }

        /**
         * @hide
         */
        // TODO(jaewan): @SystemApi
        public static @Nullable CommandButton fromBundle(Bundle bundle) {
            Builder builder = new Builder();
            builder.setCommand(Command.fromBundle(bundle.getBundle(KEY_COMMAND)));
            builder.setIconResId(bundle.getInt(KEY_ICON_RES_ID, 0));
            builder.setDisplayName(bundle.getString(KEY_DISPLAY_NAME));
            builder.setExtra(bundle.getBundle(KEY_EXTRA));
            builder.setEnabled(bundle.getBoolean(KEY_ENABLED));
            try {
                return builder.build();
            } catch (IllegalStateException e) {
                // Malformed or version mismatch. Return null for now.
                return null;
            }
        }

        /**
         * Builder for {@link CommandButton}.
         */
        public static class Builder {
            private Command mCommand;
            private int mIconResId;
            private String mDisplayName;
            private Bundle mExtra;
            private boolean mEnabled;

            public Builder() {
                mEnabled = true;
            }

            public Builder setCommand(Command command) {
                mCommand = command;
                return this;
            }

            public Builder setIconResId(int resId) {
                mIconResId = resId;
                return this;
            }

            public Builder setDisplayName(String displayName) {
                mDisplayName = displayName;
                return this;
            }

            public Builder setEnabled(boolean enabled) {
                mEnabled = enabled;
                return this;
            }

            public Builder setExtra(Bundle extra) {
                mExtra = extra;
                return this;
            }

            public CommandButton build() {
                if (mEnabled && mCommand == null) {
                    throw new IllegalStateException("Enabled button needs Command"
                            + " for controller to invoke the command");
                }
                if (mCommand != null && mCommand.getCommandCode() == COMMAND_CODE_CUSTOM
                        && (mIconResId == 0 || TextUtils.isEmpty(mDisplayName))) {
                    throw new IllegalStateException("Custom commands needs icon and"
                            + " and name to display");
                }
                return new CommandButton(mCommand, mIconResId, mDisplayName, mExtra, mEnabled);
            }
        }
    }

    /**
     * Constructor is hidden and apps can only instantiate indirectly through {@link Builder}.
     * <p>
@@ -556,6 +725,30 @@ public class MediaSession2 extends MediaPlayerBase {
        return mProvider.getConnectedControllers_impl();
    }

    /**
     * Sets ordered list of {@link CommandButton} for controllers to build UI with it.
     * <p>
     * It's up to controller's decision how to represent the layout in its own UI.
     * Here's the same way
     * (layout[i] means a CommandButton at index i in the given list)
     * For 5 icons row
     *      layout[3] layout[1] layout[0] layout[2] layout[4]
     * For 3 icons row
     *      layout[1] layout[0] layout[2]
     * For 5 icons row with overflow icon (can show +5 extra buttons with overflow button)
     *      expanded row:   layout[5] layout[6] layout[7] layout[8] layout[9]
     *      main row:       layout[3] layout[1] layout[0] layout[2] layout[4]
     * <p>
     * This API can be called in the {@link SessionCallback#onConnect(ControllerInfo)}.
     *
     * @param controller controller to specify layout.
     * @param layout oredered list of layout.
     */
    public void setCustomLayout(@NonNull ControllerInfo controller,
            @NonNull List<CommandButton> layout) {
        mProvider.setCustomLayout_impl(controller, layout);
    }

    @Override
    public void play() {
        mProvider.play_impl();
+2 −0
Original line number Diff line number Diff line
@@ -17,6 +17,7 @@
package android.media.update;

import android.media.MediaPlayerBase;
import android.media.MediaSession2.CommandButton;
import android.media.MediaSession2.ControllerInfo;
import android.media.SessionToken;

@@ -30,6 +31,7 @@ public interface MediaSession2Provider extends MediaPlayerBaseProvider {
    MediaPlayerBase getPlayer_impl();
    SessionToken getToken_impl();
    List<ControllerInfo> getConnectedControllers_impl();
    void setCustomLayout_impl(ControllerInfo controller, List<CommandButton> layout);

    /**
     * @hide