MediaControllerCompat.java revision 23138c4b9be07abdab0cfdde2c62186359c9e7fa
124fa6c0dd42df057729e1a258388183f94da7f82Jeff Brown/*
224fa6c0dd42df057729e1a258388183f94da7f82Jeff Brown * Copyright (C) 2014 The Android Open Source Project
324fa6c0dd42df057729e1a258388183f94da7f82Jeff Brown *
424fa6c0dd42df057729e1a258388183f94da7f82Jeff Brown * Licensed under the Apache License, Version 2.0 (the "License");
524fa6c0dd42df057729e1a258388183f94da7f82Jeff Brown * you may not use this file except in compliance with the License.
624fa6c0dd42df057729e1a258388183f94da7f82Jeff Brown * You may obtain a copy of the License at
724fa6c0dd42df057729e1a258388183f94da7f82Jeff Brown *
824fa6c0dd42df057729e1a258388183f94da7f82Jeff Brown *      http://www.apache.org/licenses/LICENSE-2.0
924fa6c0dd42df057729e1a258388183f94da7f82Jeff Brown *
1024fa6c0dd42df057729e1a258388183f94da7f82Jeff Brown * Unless required by applicable law or agreed to in writing, software
1124fa6c0dd42df057729e1a258388183f94da7f82Jeff Brown * distributed under the License is distributed on an "AS IS" BASIS,
1224fa6c0dd42df057729e1a258388183f94da7f82Jeff Brown * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
1324fa6c0dd42df057729e1a258388183f94da7f82Jeff Brown * See the License for the specific language governing permissions and
1424fa6c0dd42df057729e1a258388183f94da7f82Jeff Brown * limitations under the License.
1524fa6c0dd42df057729e1a258388183f94da7f82Jeff Brown */
1624fa6c0dd42df057729e1a258388183f94da7f82Jeff Brown
1724fa6c0dd42df057729e1a258388183f94da7f82Jeff Brownpackage android.support.v4.media.session;
1824fa6c0dd42df057729e1a258388183f94da7f82Jeff Brown
1924fa6c0dd42df057729e1a258388183f94da7f82Jeff Brownimport android.content.Context;
2024fa6c0dd42df057729e1a258388183f94da7f82Jeff Brownimport android.os.Bundle;
2124fa6c0dd42df057729e1a258388183f94da7f82Jeff Brownimport android.os.Handler;
2224fa6c0dd42df057729e1a258388183f94da7f82Jeff Brownimport android.os.RemoteException;
2324fa6c0dd42df057729e1a258388183f94da7f82Jeff Brownimport android.os.ResultReceiver;
2424fa6c0dd42df057729e1a258388183f94da7f82Jeff Brownimport android.support.v4.media.MediaMetadataCompat;
2524fa6c0dd42df057729e1a258388183f94da7f82Jeff Brownimport android.support.v4.media.RatingCompat;
2624fa6c0dd42df057729e1a258388183f94da7f82Jeff Brownimport android.support.v4.media.VolumeProviderCompat;
2724fa6c0dd42df057729e1a258388183f94da7f82Jeff Brownimport android.text.TextUtils;
2824fa6c0dd42df057729e1a258388183f94da7f82Jeff Brownimport android.view.KeyEvent;
2924fa6c0dd42df057729e1a258388183f94da7f82Jeff Brown
3024fa6c0dd42df057729e1a258388183f94da7f82Jeff Brown/**
3124fa6c0dd42df057729e1a258388183f94da7f82Jeff Brown * Allows an app to interact with an ongoing media session. Media buttons and
3224fa6c0dd42df057729e1a258388183f94da7f82Jeff Brown * other commands can be sent to the session. A callback may be registered to
3324fa6c0dd42df057729e1a258388183f94da7f82Jeff Brown * receive updates from the session, such as metadata and play state changes.
3424fa6c0dd42df057729e1a258388183f94da7f82Jeff Brown * <p>
3524fa6c0dd42df057729e1a258388183f94da7f82Jeff Brown * A MediaController can be created if you have a {@link MediaSessionCompat.Token}
3624fa6c0dd42df057729e1a258388183f94da7f82Jeff Brown * from the session owner.
3724fa6c0dd42df057729e1a258388183f94da7f82Jeff Brown * <p>
3824fa6c0dd42df057729e1a258388183f94da7f82Jeff Brown * MediaController objects are thread-safe.
3924fa6c0dd42df057729e1a258388183f94da7f82Jeff Brown * <p>
4024fa6c0dd42df057729e1a258388183f94da7f82Jeff Brown * This is a helper for accessing features in {@link android.media.session.MediaSession}
4124fa6c0dd42df057729e1a258388183f94da7f82Jeff Brown * introduced after API level 4 in a backwards compatible fashion.
4224fa6c0dd42df057729e1a258388183f94da7f82Jeff Brown */
4324fa6c0dd42df057729e1a258388183f94da7f82Jeff Brownpublic final class MediaControllerCompat {
4424fa6c0dd42df057729e1a258388183f94da7f82Jeff Brown    private final MediaControllerImpl mImpl;
4524fa6c0dd42df057729e1a258388183f94da7f82Jeff Brown
4624fa6c0dd42df057729e1a258388183f94da7f82Jeff Brown    /**
4724fa6c0dd42df057729e1a258388183f94da7f82Jeff Brown     * Creates a media controller from a session.
4824fa6c0dd42df057729e1a258388183f94da7f82Jeff Brown     *
4924fa6c0dd42df057729e1a258388183f94da7f82Jeff Brown     * @param session The session to be controlled.
5024fa6c0dd42df057729e1a258388183f94da7f82Jeff Brown     */
5124fa6c0dd42df057729e1a258388183f94da7f82Jeff Brown    public MediaControllerCompat(Context context, MediaSessionCompat session) {
5224fa6c0dd42df057729e1a258388183f94da7f82Jeff Brown        if (session == null) {
5324fa6c0dd42df057729e1a258388183f94da7f82Jeff Brown            throw new IllegalArgumentException("session must not be null");
5424fa6c0dd42df057729e1a258388183f94da7f82Jeff Brown        }
5524fa6c0dd42df057729e1a258388183f94da7f82Jeff Brown
5624fa6c0dd42df057729e1a258388183f94da7f82Jeff Brown        if (android.os.Build.VERSION.SDK_INT >= 21) {
5724fa6c0dd42df057729e1a258388183f94da7f82Jeff Brown            mImpl = new MediaControllerImplApi21(context, session);
5824fa6c0dd42df057729e1a258388183f94da7f82Jeff Brown        } else {
5924fa6c0dd42df057729e1a258388183f94da7f82Jeff Brown            mImpl = new MediaControllerImplBase();
6024fa6c0dd42df057729e1a258388183f94da7f82Jeff Brown        }
6124fa6c0dd42df057729e1a258388183f94da7f82Jeff Brown    }
6224fa6c0dd42df057729e1a258388183f94da7f82Jeff Brown
6324fa6c0dd42df057729e1a258388183f94da7f82Jeff Brown    /**
6424fa6c0dd42df057729e1a258388183f94da7f82Jeff Brown     * Creates a media controller from a session token which may have
6524fa6c0dd42df057729e1a258388183f94da7f82Jeff Brown     * been obtained from another process.
6624fa6c0dd42df057729e1a258388183f94da7f82Jeff Brown     *
6724fa6c0dd42df057729e1a258388183f94da7f82Jeff Brown     * @param sessionToken The token of the session to be controlled.
6824fa6c0dd42df057729e1a258388183f94da7f82Jeff Brown     * @throws RemoteException if the session is not accessible.
6924fa6c0dd42df057729e1a258388183f94da7f82Jeff Brown     */
7024fa6c0dd42df057729e1a258388183f94da7f82Jeff Brown    public MediaControllerCompat(Context context, MediaSessionCompat.Token sessionToken)
7124fa6c0dd42df057729e1a258388183f94da7f82Jeff Brown            throws RemoteException {
7224fa6c0dd42df057729e1a258388183f94da7f82Jeff Brown        if (sessionToken == null) {
7324fa6c0dd42df057729e1a258388183f94da7f82Jeff Brown            throw new IllegalArgumentException("sessionToken must not be null");
7424fa6c0dd42df057729e1a258388183f94da7f82Jeff Brown        }
7524fa6c0dd42df057729e1a258388183f94da7f82Jeff Brown
7624fa6c0dd42df057729e1a258388183f94da7f82Jeff Brown        if (android.os.Build.VERSION.SDK_INT >= 21) {
7724fa6c0dd42df057729e1a258388183f94da7f82Jeff Brown            mImpl = new MediaControllerImplApi21(context, sessionToken);
7824fa6c0dd42df057729e1a258388183f94da7f82Jeff Brown        } else {
7924fa6c0dd42df057729e1a258388183f94da7f82Jeff Brown            mImpl = new MediaControllerImplBase();
8024fa6c0dd42df057729e1a258388183f94da7f82Jeff Brown        }
8124fa6c0dd42df057729e1a258388183f94da7f82Jeff Brown    }
8224fa6c0dd42df057729e1a258388183f94da7f82Jeff Brown
8324fa6c0dd42df057729e1a258388183f94da7f82Jeff Brown    /**
8424fa6c0dd42df057729e1a258388183f94da7f82Jeff Brown     * Get a {@link TransportControls} instance for this session.
8524fa6c0dd42df057729e1a258388183f94da7f82Jeff Brown     *
8624fa6c0dd42df057729e1a258388183f94da7f82Jeff Brown     * @return A controls instance
8724fa6c0dd42df057729e1a258388183f94da7f82Jeff Brown     */
8824fa6c0dd42df057729e1a258388183f94da7f82Jeff Brown    public TransportControls getTransportControls() {
8924fa6c0dd42df057729e1a258388183f94da7f82Jeff Brown        return mImpl.getTransportControls();
9024fa6c0dd42df057729e1a258388183f94da7f82Jeff Brown    }
9124fa6c0dd42df057729e1a258388183f94da7f82Jeff Brown
9224fa6c0dd42df057729e1a258388183f94da7f82Jeff Brown    /**
9324fa6c0dd42df057729e1a258388183f94da7f82Jeff Brown     * Send the specified media button event to the session. Only media keys can
9424fa6c0dd42df057729e1a258388183f94da7f82Jeff Brown     * be sent by this method, other keys will be ignored.
9524fa6c0dd42df057729e1a258388183f94da7f82Jeff Brown     *
9624fa6c0dd42df057729e1a258388183f94da7f82Jeff Brown     * @param keyEvent The media button event to dispatch.
9724fa6c0dd42df057729e1a258388183f94da7f82Jeff Brown     * @return true if the event was sent to the session, false otherwise.
9824fa6c0dd42df057729e1a258388183f94da7f82Jeff Brown     */
9924fa6c0dd42df057729e1a258388183f94da7f82Jeff Brown    public boolean dispatchMediaButtonEvent(KeyEvent keyEvent) {
10024fa6c0dd42df057729e1a258388183f94da7f82Jeff Brown        if (keyEvent == null) {
10124fa6c0dd42df057729e1a258388183f94da7f82Jeff Brown            throw new IllegalArgumentException("KeyEvent may not be null");
10224fa6c0dd42df057729e1a258388183f94da7f82Jeff Brown        }
10324fa6c0dd42df057729e1a258388183f94da7f82Jeff Brown        return mImpl.dispatchMediaButtonEvent(keyEvent);
10424fa6c0dd42df057729e1a258388183f94da7f82Jeff Brown    }
10524fa6c0dd42df057729e1a258388183f94da7f82Jeff Brown
10624fa6c0dd42df057729e1a258388183f94da7f82Jeff Brown    /**
10724fa6c0dd42df057729e1a258388183f94da7f82Jeff Brown     * Get the current playback state for this session.
10824fa6c0dd42df057729e1a258388183f94da7f82Jeff Brown     *
10924fa6c0dd42df057729e1a258388183f94da7f82Jeff Brown     * @return The current PlaybackState or null
11024fa6c0dd42df057729e1a258388183f94da7f82Jeff Brown     */
11124fa6c0dd42df057729e1a258388183f94da7f82Jeff Brown    public PlaybackStateCompat getPlaybackState() {
11224fa6c0dd42df057729e1a258388183f94da7f82Jeff Brown        return mImpl.getPlaybackState();
11324fa6c0dd42df057729e1a258388183f94da7f82Jeff Brown    }
11424fa6c0dd42df057729e1a258388183f94da7f82Jeff Brown
11524fa6c0dd42df057729e1a258388183f94da7f82Jeff Brown    /**
11624fa6c0dd42df057729e1a258388183f94da7f82Jeff Brown     * Get the current metadata for this session.
11724fa6c0dd42df057729e1a258388183f94da7f82Jeff Brown     *
11824fa6c0dd42df057729e1a258388183f94da7f82Jeff Brown     * @return The current MediaMetadata or null.
11924fa6c0dd42df057729e1a258388183f94da7f82Jeff Brown     */
12024fa6c0dd42df057729e1a258388183f94da7f82Jeff Brown    public MediaMetadataCompat getMetadata() {
12124fa6c0dd42df057729e1a258388183f94da7f82Jeff Brown        return mImpl.getMetadata();
12224fa6c0dd42df057729e1a258388183f94da7f82Jeff Brown    }
12324fa6c0dd42df057729e1a258388183f94da7f82Jeff Brown
12424fa6c0dd42df057729e1a258388183f94da7f82Jeff Brown    /**
12524fa6c0dd42df057729e1a258388183f94da7f82Jeff Brown     * Get the rating type supported by the session. One of:
12624fa6c0dd42df057729e1a258388183f94da7f82Jeff Brown     * <ul>
12724fa6c0dd42df057729e1a258388183f94da7f82Jeff Brown     * <li>{@link RatingCompat#RATING_NONE}</li>
12824fa6c0dd42df057729e1a258388183f94da7f82Jeff Brown     * <li>{@link RatingCompat#RATING_HEART}</li>
12924fa6c0dd42df057729e1a258388183f94da7f82Jeff Brown     * <li>{@link RatingCompat#RATING_THUMB_UP_DOWN}</li>
13024fa6c0dd42df057729e1a258388183f94da7f82Jeff Brown     * <li>{@link RatingCompat#RATING_3_STARS}</li>
13124fa6c0dd42df057729e1a258388183f94da7f82Jeff Brown     * <li>{@link RatingCompat#RATING_4_STARS}</li>
13224fa6c0dd42df057729e1a258388183f94da7f82Jeff Brown     * <li>{@link RatingCompat#RATING_5_STARS}</li>
13324fa6c0dd42df057729e1a258388183f94da7f82Jeff Brown     * <li>{@link RatingCompat#RATING_PERCENTAGE}</li>
13424fa6c0dd42df057729e1a258388183f94da7f82Jeff Brown     * </ul>
13524fa6c0dd42df057729e1a258388183f94da7f82Jeff Brown     *
13624fa6c0dd42df057729e1a258388183f94da7f82Jeff Brown     * @return The supported rating type
13724fa6c0dd42df057729e1a258388183f94da7f82Jeff Brown     */
13824fa6c0dd42df057729e1a258388183f94da7f82Jeff Brown    public int getRatingType() {
13924fa6c0dd42df057729e1a258388183f94da7f82Jeff Brown        return mImpl.getRatingType();
14024fa6c0dd42df057729e1a258388183f94da7f82Jeff Brown    }
14124fa6c0dd42df057729e1a258388183f94da7f82Jeff Brown
14224fa6c0dd42df057729e1a258388183f94da7f82Jeff Brown    /**
14324fa6c0dd42df057729e1a258388183f94da7f82Jeff Brown     * Get the current volume info for this session.
14424fa6c0dd42df057729e1a258388183f94da7f82Jeff Brown     *
14524fa6c0dd42df057729e1a258388183f94da7f82Jeff Brown     * @return The current volume info or null.
14624fa6c0dd42df057729e1a258388183f94da7f82Jeff Brown     */
14724fa6c0dd42df057729e1a258388183f94da7f82Jeff Brown    public VolumeInfo getVolumeInfo() {
14824fa6c0dd42df057729e1a258388183f94da7f82Jeff Brown        return mImpl.getVolumeInfo();
14924fa6c0dd42df057729e1a258388183f94da7f82Jeff Brown    }
15024fa6c0dd42df057729e1a258388183f94da7f82Jeff Brown
15124fa6c0dd42df057729e1a258388183f94da7f82Jeff Brown    /**
15224fa6c0dd42df057729e1a258388183f94da7f82Jeff Brown     * Adds a callback to receive updates from the Session. Updates will be
15324fa6c0dd42df057729e1a258388183f94da7f82Jeff Brown     * posted on the caller's thread.
15424fa6c0dd42df057729e1a258388183f94da7f82Jeff Brown     *
15524fa6c0dd42df057729e1a258388183f94da7f82Jeff Brown     * @param callback The callback object, must not be null.
15624fa6c0dd42df057729e1a258388183f94da7f82Jeff Brown     */
15724fa6c0dd42df057729e1a258388183f94da7f82Jeff Brown    public void addCallback(Callback callback) {
15824fa6c0dd42df057729e1a258388183f94da7f82Jeff Brown        addCallback(callback, null);
15924fa6c0dd42df057729e1a258388183f94da7f82Jeff Brown    }
16024fa6c0dd42df057729e1a258388183f94da7f82Jeff Brown
16124fa6c0dd42df057729e1a258388183f94da7f82Jeff Brown    /**
16224fa6c0dd42df057729e1a258388183f94da7f82Jeff Brown     * Adds a callback to receive updates from the session. Updates will be
16324fa6c0dd42df057729e1a258388183f94da7f82Jeff Brown     * posted on the specified handler's thread.
16424fa6c0dd42df057729e1a258388183f94da7f82Jeff Brown     *
16524fa6c0dd42df057729e1a258388183f94da7f82Jeff Brown     * @param callback The callback object, must not be null.
16624fa6c0dd42df057729e1a258388183f94da7f82Jeff Brown     * @param handler The handler to post updates on. If null the callers thread
16724fa6c0dd42df057729e1a258388183f94da7f82Jeff Brown     *            will be used.
16824fa6c0dd42df057729e1a258388183f94da7f82Jeff Brown     */
16924fa6c0dd42df057729e1a258388183f94da7f82Jeff Brown    public void addCallback(Callback callback, Handler handler) {
17024fa6c0dd42df057729e1a258388183f94da7f82Jeff Brown        if (callback == null) {
17124fa6c0dd42df057729e1a258388183f94da7f82Jeff Brown            throw new IllegalArgumentException("callback cannot be null");
17224fa6c0dd42df057729e1a258388183f94da7f82Jeff Brown        }
17324fa6c0dd42df057729e1a258388183f94da7f82Jeff Brown        if (handler == null) {
17424fa6c0dd42df057729e1a258388183f94da7f82Jeff Brown            handler = new Handler();
17524fa6c0dd42df057729e1a258388183f94da7f82Jeff Brown        }
17624fa6c0dd42df057729e1a258388183f94da7f82Jeff Brown        mImpl.addCallback(callback, handler);
17724fa6c0dd42df057729e1a258388183f94da7f82Jeff Brown    }
17824fa6c0dd42df057729e1a258388183f94da7f82Jeff Brown
17924fa6c0dd42df057729e1a258388183f94da7f82Jeff Brown    /**
18024fa6c0dd42df057729e1a258388183f94da7f82Jeff Brown     * Stop receiving updates on the specified callback. If an update has
18124fa6c0dd42df057729e1a258388183f94da7f82Jeff Brown     * already been posted you may still receive it after calling this method.
18224fa6c0dd42df057729e1a258388183f94da7f82Jeff Brown     *
18324fa6c0dd42df057729e1a258388183f94da7f82Jeff Brown     * @param callback The callback to remove
18424fa6c0dd42df057729e1a258388183f94da7f82Jeff Brown     */
18524fa6c0dd42df057729e1a258388183f94da7f82Jeff Brown    public void removeCallback(Callback callback) {
18624fa6c0dd42df057729e1a258388183f94da7f82Jeff Brown        if (callback == null) {
18724fa6c0dd42df057729e1a258388183f94da7f82Jeff Brown            throw new IllegalArgumentException("callback cannot be null");
18824fa6c0dd42df057729e1a258388183f94da7f82Jeff Brown        }
18924fa6c0dd42df057729e1a258388183f94da7f82Jeff Brown        mImpl.removeCallback(callback);
19024fa6c0dd42df057729e1a258388183f94da7f82Jeff Brown    }
19124fa6c0dd42df057729e1a258388183f94da7f82Jeff Brown
19224fa6c0dd42df057729e1a258388183f94da7f82Jeff Brown    /**
19324fa6c0dd42df057729e1a258388183f94da7f82Jeff Brown     * Sends a generic command to the session. It is up to the session creator
19424fa6c0dd42df057729e1a258388183f94da7f82Jeff Brown     * to decide what commands and parameters they will support. As such,
19524fa6c0dd42df057729e1a258388183f94da7f82Jeff Brown     * commands should only be sent to sessions that the controller owns.
19624fa6c0dd42df057729e1a258388183f94da7f82Jeff Brown     *
19724fa6c0dd42df057729e1a258388183f94da7f82Jeff Brown     * @param command The command to send
19824fa6c0dd42df057729e1a258388183f94da7f82Jeff Brown     * @param params Any parameters to include with the command
19924fa6c0dd42df057729e1a258388183f94da7f82Jeff Brown     * @param cb The callback to receive the result on
20024fa6c0dd42df057729e1a258388183f94da7f82Jeff Brown     */
201b530c89bba371d2d575f10480b2e90914b0d3f3fGabriel Peal    public void sendCommand(String command, Bundle params, ResultReceiver cb) {
20224fa6c0dd42df057729e1a258388183f94da7f82Jeff Brown        if (TextUtils.isEmpty(command)) {
20324fa6c0dd42df057729e1a258388183f94da7f82Jeff Brown            throw new IllegalArgumentException("command cannot be null or empty");
20424fa6c0dd42df057729e1a258388183f94da7f82Jeff Brown        }
205b530c89bba371d2d575f10480b2e90914b0d3f3fGabriel Peal        mImpl.sendCommand(command, params, cb);
20624fa6c0dd42df057729e1a258388183f94da7f82Jeff Brown    }
20724fa6c0dd42df057729e1a258388183f94da7f82Jeff Brown
20824fa6c0dd42df057729e1a258388183f94da7f82Jeff Brown    /**
20924fa6c0dd42df057729e1a258388183f94da7f82Jeff Brown     * Gets the underlying framework {@link android.media.session.MediaController} object.
21024fa6c0dd42df057729e1a258388183f94da7f82Jeff Brown     * <p>
21124fa6c0dd42df057729e1a258388183f94da7f82Jeff Brown     * This method is only supported on API 21+.
21224fa6c0dd42df057729e1a258388183f94da7f82Jeff Brown     * </p>
21324fa6c0dd42df057729e1a258388183f94da7f82Jeff Brown     *
21424fa6c0dd42df057729e1a258388183f94da7f82Jeff Brown     * @return The underlying {@link android.media.session.MediaController} object,
21524fa6c0dd42df057729e1a258388183f94da7f82Jeff Brown     * or null if none.
21624fa6c0dd42df057729e1a258388183f94da7f82Jeff Brown     */
21724fa6c0dd42df057729e1a258388183f94da7f82Jeff Brown    public Object getMediaController() {
21824fa6c0dd42df057729e1a258388183f94da7f82Jeff Brown        return mImpl.getMediaController();
21924fa6c0dd42df057729e1a258388183f94da7f82Jeff Brown    }
22024fa6c0dd42df057729e1a258388183f94da7f82Jeff Brown
22124fa6c0dd42df057729e1a258388183f94da7f82Jeff Brown    /**
22224fa6c0dd42df057729e1a258388183f94da7f82Jeff Brown     * Callback for receiving updates on from the session. A Callback can be
22324fa6c0dd42df057729e1a258388183f94da7f82Jeff Brown     * registered using {@link #addCallback}
22424fa6c0dd42df057729e1a258388183f94da7f82Jeff Brown     */
22524fa6c0dd42df057729e1a258388183f94da7f82Jeff Brown    public static abstract class Callback {
22624fa6c0dd42df057729e1a258388183f94da7f82Jeff Brown        final Object mCallbackObj;
22724fa6c0dd42df057729e1a258388183f94da7f82Jeff Brown
22824fa6c0dd42df057729e1a258388183f94da7f82Jeff Brown        public Callback() {
22924fa6c0dd42df057729e1a258388183f94da7f82Jeff Brown            if (android.os.Build.VERSION.SDK_INT >= 21) {
23024fa6c0dd42df057729e1a258388183f94da7f82Jeff Brown                mCallbackObj = MediaControllerCompatApi21.createCallback(new StubApi21());
23124fa6c0dd42df057729e1a258388183f94da7f82Jeff Brown            } else {
23224fa6c0dd42df057729e1a258388183f94da7f82Jeff Brown                mCallbackObj = null;
23324fa6c0dd42df057729e1a258388183f94da7f82Jeff Brown            }
23424fa6c0dd42df057729e1a258388183f94da7f82Jeff Brown        }
23524fa6c0dd42df057729e1a258388183f94da7f82Jeff Brown
23624fa6c0dd42df057729e1a258388183f94da7f82Jeff Brown        /**
23723138c4b9be07abdab0cfdde2c62186359c9e7faRoboErik         * Override to handle the session being destroyed. The session is no
23823138c4b9be07abdab0cfdde2c62186359c9e7faRoboErik         * longer valid after this call and calls to it will be ignored.
23923138c4b9be07abdab0cfdde2c62186359c9e7faRoboErik         */
24023138c4b9be07abdab0cfdde2c62186359c9e7faRoboErik        public void onSessionDestroyed() {
24123138c4b9be07abdab0cfdde2c62186359c9e7faRoboErik        }
24223138c4b9be07abdab0cfdde2c62186359c9e7faRoboErik
24323138c4b9be07abdab0cfdde2c62186359c9e7faRoboErik        /**
24424fa6c0dd42df057729e1a258388183f94da7f82Jeff Brown         * Override to handle custom events sent by the session owner without a
24524fa6c0dd42df057729e1a258388183f94da7f82Jeff Brown         * specified interface. Controllers should only handle these for
24624fa6c0dd42df057729e1a258388183f94da7f82Jeff Brown         * sessions they own.
24724fa6c0dd42df057729e1a258388183f94da7f82Jeff Brown         *
24824fa6c0dd42df057729e1a258388183f94da7f82Jeff Brown         * @param event The event from the session.
24924fa6c0dd42df057729e1a258388183f94da7f82Jeff Brown         * @param extras Optional parameters for the event.
25024fa6c0dd42df057729e1a258388183f94da7f82Jeff Brown         */
25124fa6c0dd42df057729e1a258388183f94da7f82Jeff Brown        public void onSessionEvent(String event, Bundle extras) {
25224fa6c0dd42df057729e1a258388183f94da7f82Jeff Brown        }
25324fa6c0dd42df057729e1a258388183f94da7f82Jeff Brown
25424fa6c0dd42df057729e1a258388183f94da7f82Jeff Brown        /**
25524fa6c0dd42df057729e1a258388183f94da7f82Jeff Brown         * Override to handle changes in playback state.
25624fa6c0dd42df057729e1a258388183f94da7f82Jeff Brown         *
25724fa6c0dd42df057729e1a258388183f94da7f82Jeff Brown         * @param state The new playback state of the session
25824fa6c0dd42df057729e1a258388183f94da7f82Jeff Brown         */
25924fa6c0dd42df057729e1a258388183f94da7f82Jeff Brown        public void onPlaybackStateChanged(PlaybackStateCompat state) {
26024fa6c0dd42df057729e1a258388183f94da7f82Jeff Brown        }
26124fa6c0dd42df057729e1a258388183f94da7f82Jeff Brown
26224fa6c0dd42df057729e1a258388183f94da7f82Jeff Brown        /**
26324fa6c0dd42df057729e1a258388183f94da7f82Jeff Brown         * Override to handle changes to the current metadata.
26424fa6c0dd42df057729e1a258388183f94da7f82Jeff Brown         *
26524fa6c0dd42df057729e1a258388183f94da7f82Jeff Brown         * @param metadata The current metadata for the session or null if none.
26624fa6c0dd42df057729e1a258388183f94da7f82Jeff Brown         * @see MediaMetadata
26724fa6c0dd42df057729e1a258388183f94da7f82Jeff Brown         */
26824fa6c0dd42df057729e1a258388183f94da7f82Jeff Brown        public void onMetadataChanged(MediaMetadataCompat metadata) {
26924fa6c0dd42df057729e1a258388183f94da7f82Jeff Brown        }
27024fa6c0dd42df057729e1a258388183f94da7f82Jeff Brown
27124fa6c0dd42df057729e1a258388183f94da7f82Jeff Brown        private class StubApi21 implements MediaControllerCompatApi21.Callback {
27224fa6c0dd42df057729e1a258388183f94da7f82Jeff Brown            @Override
27323138c4b9be07abdab0cfdde2c62186359c9e7faRoboErik            public void onSessionDestroyed() {
27423138c4b9be07abdab0cfdde2c62186359c9e7faRoboErik                Callback.this.onSessionDestroyed();
27523138c4b9be07abdab0cfdde2c62186359c9e7faRoboErik            }
27623138c4b9be07abdab0cfdde2c62186359c9e7faRoboErik
27723138c4b9be07abdab0cfdde2c62186359c9e7faRoboErik            @Override
27824fa6c0dd42df057729e1a258388183f94da7f82Jeff Brown            public void onSessionEvent(String event, Bundle extras) {
27924fa6c0dd42df057729e1a258388183f94da7f82Jeff Brown                Callback.this.onSessionEvent(event, extras);
28024fa6c0dd42df057729e1a258388183f94da7f82Jeff Brown            }
28124fa6c0dd42df057729e1a258388183f94da7f82Jeff Brown
28224fa6c0dd42df057729e1a258388183f94da7f82Jeff Brown            @Override
28324fa6c0dd42df057729e1a258388183f94da7f82Jeff Brown            public void onPlaybackStateChanged(Object stateObj) {
28424fa6c0dd42df057729e1a258388183f94da7f82Jeff Brown                Callback.this.onPlaybackStateChanged(
28524fa6c0dd42df057729e1a258388183f94da7f82Jeff Brown                        PlaybackStateCompat.fromPlaybackState(stateObj));
28624fa6c0dd42df057729e1a258388183f94da7f82Jeff Brown            }
28724fa6c0dd42df057729e1a258388183f94da7f82Jeff Brown
28824fa6c0dd42df057729e1a258388183f94da7f82Jeff Brown            @Override
28924fa6c0dd42df057729e1a258388183f94da7f82Jeff Brown            public void onMetadataChanged(Object metadataObj) {
29024fa6c0dd42df057729e1a258388183f94da7f82Jeff Brown                Callback.this.onMetadataChanged(
29124fa6c0dd42df057729e1a258388183f94da7f82Jeff Brown                        MediaMetadataCompat.fromMediaMetadata(metadataObj));
29224fa6c0dd42df057729e1a258388183f94da7f82Jeff Brown            }
29324fa6c0dd42df057729e1a258388183f94da7f82Jeff Brown        }
29424fa6c0dd42df057729e1a258388183f94da7f82Jeff Brown    }
29524fa6c0dd42df057729e1a258388183f94da7f82Jeff Brown
29624fa6c0dd42df057729e1a258388183f94da7f82Jeff Brown    /**
29724fa6c0dd42df057729e1a258388183f94da7f82Jeff Brown     * Interface for controlling media playback on a session. This allows an app
29824fa6c0dd42df057729e1a258388183f94da7f82Jeff Brown     * to send media transport commands to the session.
29924fa6c0dd42df057729e1a258388183f94da7f82Jeff Brown     */
30024fa6c0dd42df057729e1a258388183f94da7f82Jeff Brown    public static abstract class TransportControls {
30124fa6c0dd42df057729e1a258388183f94da7f82Jeff Brown        TransportControls() {
30224fa6c0dd42df057729e1a258388183f94da7f82Jeff Brown        }
30324fa6c0dd42df057729e1a258388183f94da7f82Jeff Brown
30424fa6c0dd42df057729e1a258388183f94da7f82Jeff Brown        /**
30524fa6c0dd42df057729e1a258388183f94da7f82Jeff Brown         * Request that the player start its playback at its current position.
30624fa6c0dd42df057729e1a258388183f94da7f82Jeff Brown         */
30724fa6c0dd42df057729e1a258388183f94da7f82Jeff Brown        public abstract void play();
30824fa6c0dd42df057729e1a258388183f94da7f82Jeff Brown
30924fa6c0dd42df057729e1a258388183f94da7f82Jeff Brown        /**
31024fa6c0dd42df057729e1a258388183f94da7f82Jeff Brown         * Request that the player pause its playback and stay at its current
31124fa6c0dd42df057729e1a258388183f94da7f82Jeff Brown         * position.
31224fa6c0dd42df057729e1a258388183f94da7f82Jeff Brown         */
31324fa6c0dd42df057729e1a258388183f94da7f82Jeff Brown        public abstract void pause();
31424fa6c0dd42df057729e1a258388183f94da7f82Jeff Brown
31524fa6c0dd42df057729e1a258388183f94da7f82Jeff Brown        /**
31624fa6c0dd42df057729e1a258388183f94da7f82Jeff Brown         * Request that the player stop its playback; it may clear its state in
31724fa6c0dd42df057729e1a258388183f94da7f82Jeff Brown         * whatever way is appropriate.
31824fa6c0dd42df057729e1a258388183f94da7f82Jeff Brown         */
31924fa6c0dd42df057729e1a258388183f94da7f82Jeff Brown        public abstract void stop();
32024fa6c0dd42df057729e1a258388183f94da7f82Jeff Brown
32124fa6c0dd42df057729e1a258388183f94da7f82Jeff Brown        /**
32224fa6c0dd42df057729e1a258388183f94da7f82Jeff Brown         * Move to a new location in the media stream.
32324fa6c0dd42df057729e1a258388183f94da7f82Jeff Brown         *
32424fa6c0dd42df057729e1a258388183f94da7f82Jeff Brown         * @param pos Position to move to, in milliseconds.
32524fa6c0dd42df057729e1a258388183f94da7f82Jeff Brown         */
32624fa6c0dd42df057729e1a258388183f94da7f82Jeff Brown        public abstract void seekTo(long pos);
32724fa6c0dd42df057729e1a258388183f94da7f82Jeff Brown
32824fa6c0dd42df057729e1a258388183f94da7f82Jeff Brown        /**
32924fa6c0dd42df057729e1a258388183f94da7f82Jeff Brown         * Start fast forwarding. If playback is already fast forwarding this
33024fa6c0dd42df057729e1a258388183f94da7f82Jeff Brown         * may increase the rate.
33124fa6c0dd42df057729e1a258388183f94da7f82Jeff Brown         */
33224fa6c0dd42df057729e1a258388183f94da7f82Jeff Brown        public abstract void fastForward();
33324fa6c0dd42df057729e1a258388183f94da7f82Jeff Brown
33424fa6c0dd42df057729e1a258388183f94da7f82Jeff Brown        /**
33524fa6c0dd42df057729e1a258388183f94da7f82Jeff Brown         * Skip to the next item.
33624fa6c0dd42df057729e1a258388183f94da7f82Jeff Brown         */
33724fa6c0dd42df057729e1a258388183f94da7f82Jeff Brown        public abstract void skipToNext();
33824fa6c0dd42df057729e1a258388183f94da7f82Jeff Brown
33924fa6c0dd42df057729e1a258388183f94da7f82Jeff Brown        /**
34024fa6c0dd42df057729e1a258388183f94da7f82Jeff Brown         * Start rewinding. If playback is already rewinding this may increase
34124fa6c0dd42df057729e1a258388183f94da7f82Jeff Brown         * the rate.
34224fa6c0dd42df057729e1a258388183f94da7f82Jeff Brown         */
34324fa6c0dd42df057729e1a258388183f94da7f82Jeff Brown        public abstract void rewind();
34424fa6c0dd42df057729e1a258388183f94da7f82Jeff Brown
34524fa6c0dd42df057729e1a258388183f94da7f82Jeff Brown        /**
34624fa6c0dd42df057729e1a258388183f94da7f82Jeff Brown         * Skip to the previous item.
34724fa6c0dd42df057729e1a258388183f94da7f82Jeff Brown         */
34824fa6c0dd42df057729e1a258388183f94da7f82Jeff Brown        public abstract void skipToPrevious();
34924fa6c0dd42df057729e1a258388183f94da7f82Jeff Brown
35024fa6c0dd42df057729e1a258388183f94da7f82Jeff Brown        /**
35124fa6c0dd42df057729e1a258388183f94da7f82Jeff Brown         * Rate the current content. This will cause the rating to be set for
35224fa6c0dd42df057729e1a258388183f94da7f82Jeff Brown         * the current user. The Rating type must match the type returned by
35324fa6c0dd42df057729e1a258388183f94da7f82Jeff Brown         * {@link #getRatingType()}.
35424fa6c0dd42df057729e1a258388183f94da7f82Jeff Brown         *
35524fa6c0dd42df057729e1a258388183f94da7f82Jeff Brown         * @param rating The rating to set for the current content
35624fa6c0dd42df057729e1a258388183f94da7f82Jeff Brown         */
35724fa6c0dd42df057729e1a258388183f94da7f82Jeff Brown        public abstract void setRating(RatingCompat rating);
35824fa6c0dd42df057729e1a258388183f94da7f82Jeff Brown    }
35924fa6c0dd42df057729e1a258388183f94da7f82Jeff Brown
36024fa6c0dd42df057729e1a258388183f94da7f82Jeff Brown    /**
36124fa6c0dd42df057729e1a258388183f94da7f82Jeff Brown     * Holds information about the way volume is handled for this session.
36224fa6c0dd42df057729e1a258388183f94da7f82Jeff Brown     */
36324fa6c0dd42df057729e1a258388183f94da7f82Jeff Brown    public static final class VolumeInfo {
36424fa6c0dd42df057729e1a258388183f94da7f82Jeff Brown        private final int mVolumeType;
3651435afe32073dee10e721dfb6122ce6a194a6412RoboErik        // TODO update audio stream with AudioAttributes support version
36624fa6c0dd42df057729e1a258388183f94da7f82Jeff Brown        private final int mAudioStream;
36724fa6c0dd42df057729e1a258388183f94da7f82Jeff Brown        private final int mVolumeControl;
36824fa6c0dd42df057729e1a258388183f94da7f82Jeff Brown        private final int mMaxVolume;
36924fa6c0dd42df057729e1a258388183f94da7f82Jeff Brown        private final int mCurrentVolume;
37024fa6c0dd42df057729e1a258388183f94da7f82Jeff Brown
37124fa6c0dd42df057729e1a258388183f94da7f82Jeff Brown        VolumeInfo(int type, int stream, int control, int max, int current) {
37224fa6c0dd42df057729e1a258388183f94da7f82Jeff Brown            mVolumeType = type;
37324fa6c0dd42df057729e1a258388183f94da7f82Jeff Brown            mAudioStream = stream;
37424fa6c0dd42df057729e1a258388183f94da7f82Jeff Brown            mVolumeControl = control;
37524fa6c0dd42df057729e1a258388183f94da7f82Jeff Brown            mMaxVolume = max;
37624fa6c0dd42df057729e1a258388183f94da7f82Jeff Brown            mCurrentVolume = current;
37724fa6c0dd42df057729e1a258388183f94da7f82Jeff Brown        }
37824fa6c0dd42df057729e1a258388183f94da7f82Jeff Brown
37924fa6c0dd42df057729e1a258388183f94da7f82Jeff Brown        /**
38024fa6c0dd42df057729e1a258388183f94da7f82Jeff Brown         * Get the type of volume handling, either local or remote. One of:
38124fa6c0dd42df057729e1a258388183f94da7f82Jeff Brown         * <ul>
38224fa6c0dd42df057729e1a258388183f94da7f82Jeff Brown         * <li>{@link MediaSessionCompat#VOLUME_TYPE_LOCAL}</li>
38324fa6c0dd42df057729e1a258388183f94da7f82Jeff Brown         * <li>{@link MediaSessionCompat#VOLUME_TYPE_REMOTE}</li>
38424fa6c0dd42df057729e1a258388183f94da7f82Jeff Brown         * </ul>
38524fa6c0dd42df057729e1a258388183f94da7f82Jeff Brown         *
38624fa6c0dd42df057729e1a258388183f94da7f82Jeff Brown         * @return The type of volume handling this session is using.
38724fa6c0dd42df057729e1a258388183f94da7f82Jeff Brown         */
38824fa6c0dd42df057729e1a258388183f94da7f82Jeff Brown        public int getVolumeType() {
38924fa6c0dd42df057729e1a258388183f94da7f82Jeff Brown            return mVolumeType;
39024fa6c0dd42df057729e1a258388183f94da7f82Jeff Brown        }
39124fa6c0dd42df057729e1a258388183f94da7f82Jeff Brown
39224fa6c0dd42df057729e1a258388183f94da7f82Jeff Brown        /**
39324fa6c0dd42df057729e1a258388183f94da7f82Jeff Brown         * Get the stream this is currently controlling volume on. When the volume
39424fa6c0dd42df057729e1a258388183f94da7f82Jeff Brown         * type is {@link MediaSessionCompat#VOLUME_TYPE_REMOTE} this value does not
39524fa6c0dd42df057729e1a258388183f94da7f82Jeff Brown         * have meaning and should be ignored.
39624fa6c0dd42df057729e1a258388183f94da7f82Jeff Brown         *
39724fa6c0dd42df057729e1a258388183f94da7f82Jeff Brown         * @return The stream this session is playing on.
39824fa6c0dd42df057729e1a258388183f94da7f82Jeff Brown         */
39924fa6c0dd42df057729e1a258388183f94da7f82Jeff Brown        public int getAudioStream() {
40024fa6c0dd42df057729e1a258388183f94da7f82Jeff Brown            return mAudioStream;
40124fa6c0dd42df057729e1a258388183f94da7f82Jeff Brown        }
40224fa6c0dd42df057729e1a258388183f94da7f82Jeff Brown
40324fa6c0dd42df057729e1a258388183f94da7f82Jeff Brown        /**
40424fa6c0dd42df057729e1a258388183f94da7f82Jeff Brown         * Get the type of volume control that can be used. One of:
40524fa6c0dd42df057729e1a258388183f94da7f82Jeff Brown         * <ul>
40624fa6c0dd42df057729e1a258388183f94da7f82Jeff Brown         * <li>{@link VolumeProviderCompat#VOLUME_CONTROL_ABSOLUTE}</li>
40724fa6c0dd42df057729e1a258388183f94da7f82Jeff Brown         * <li>{@link VolumeProviderCompat#VOLUME_CONTROL_RELATIVE}</li>
40824fa6c0dd42df057729e1a258388183f94da7f82Jeff Brown         * <li>{@link VolumeProviderCompat#VOLUME_CONTROL_FIXED}</li>
40924fa6c0dd42df057729e1a258388183f94da7f82Jeff Brown         * </ul>
41024fa6c0dd42df057729e1a258388183f94da7f82Jeff Brown         *
41124fa6c0dd42df057729e1a258388183f94da7f82Jeff Brown         * @return The type of volume control that may be used with this
41224fa6c0dd42df057729e1a258388183f94da7f82Jeff Brown         *         session.
41324fa6c0dd42df057729e1a258388183f94da7f82Jeff Brown         */
41424fa6c0dd42df057729e1a258388183f94da7f82Jeff Brown        public int getVolumeControl() {
41524fa6c0dd42df057729e1a258388183f94da7f82Jeff Brown            return mVolumeControl;
41624fa6c0dd42df057729e1a258388183f94da7f82Jeff Brown        }
41724fa6c0dd42df057729e1a258388183f94da7f82Jeff Brown
41824fa6c0dd42df057729e1a258388183f94da7f82Jeff Brown        /**
41924fa6c0dd42df057729e1a258388183f94da7f82Jeff Brown         * Get the maximum volume that may be set for this session.
42024fa6c0dd42df057729e1a258388183f94da7f82Jeff Brown         *
42124fa6c0dd42df057729e1a258388183f94da7f82Jeff Brown         * @return The maximum allowed volume where this session is playing.
42224fa6c0dd42df057729e1a258388183f94da7f82Jeff Brown         */
42324fa6c0dd42df057729e1a258388183f94da7f82Jeff Brown        public int getMaxVolume() {
42424fa6c0dd42df057729e1a258388183f94da7f82Jeff Brown            return mMaxVolume;
42524fa6c0dd42df057729e1a258388183f94da7f82Jeff Brown        }
42624fa6c0dd42df057729e1a258388183f94da7f82Jeff Brown
42724fa6c0dd42df057729e1a258388183f94da7f82Jeff Brown        /**
42824fa6c0dd42df057729e1a258388183f94da7f82Jeff Brown         * Get the current volume for this session.
42924fa6c0dd42df057729e1a258388183f94da7f82Jeff Brown         *
43024fa6c0dd42df057729e1a258388183f94da7f82Jeff Brown         * @return The current volume where this session is playing.
43124fa6c0dd42df057729e1a258388183f94da7f82Jeff Brown         */
43224fa6c0dd42df057729e1a258388183f94da7f82Jeff Brown        public int getCurrentVolume() {
43324fa6c0dd42df057729e1a258388183f94da7f82Jeff Brown            return mCurrentVolume;
43424fa6c0dd42df057729e1a258388183f94da7f82Jeff Brown        }
43524fa6c0dd42df057729e1a258388183f94da7f82Jeff Brown    }
43624fa6c0dd42df057729e1a258388183f94da7f82Jeff Brown
43724fa6c0dd42df057729e1a258388183f94da7f82Jeff Brown    interface MediaControllerImpl {
43824fa6c0dd42df057729e1a258388183f94da7f82Jeff Brown        void addCallback(Callback callback, Handler handler);
43924fa6c0dd42df057729e1a258388183f94da7f82Jeff Brown        void removeCallback(Callback callback);
44024fa6c0dd42df057729e1a258388183f94da7f82Jeff Brown        boolean dispatchMediaButtonEvent(KeyEvent keyEvent);
44124fa6c0dd42df057729e1a258388183f94da7f82Jeff Brown        TransportControls getTransportControls();
44224fa6c0dd42df057729e1a258388183f94da7f82Jeff Brown        PlaybackStateCompat getPlaybackState();
44324fa6c0dd42df057729e1a258388183f94da7f82Jeff Brown        MediaMetadataCompat getMetadata();
44424fa6c0dd42df057729e1a258388183f94da7f82Jeff Brown        int getRatingType();
44524fa6c0dd42df057729e1a258388183f94da7f82Jeff Brown        VolumeInfo getVolumeInfo();
446b530c89bba371d2d575f10480b2e90914b0d3f3fGabriel Peal        void sendCommand(String command, Bundle params, ResultReceiver cb);
44724fa6c0dd42df057729e1a258388183f94da7f82Jeff Brown        Object getMediaController();
44824fa6c0dd42df057729e1a258388183f94da7f82Jeff Brown    }
44924fa6c0dd42df057729e1a258388183f94da7f82Jeff Brown
45024fa6c0dd42df057729e1a258388183f94da7f82Jeff Brown    // TODO: compatibility implementation
45124fa6c0dd42df057729e1a258388183f94da7f82Jeff Brown    static class MediaControllerImplBase implements MediaControllerImpl {
45224fa6c0dd42df057729e1a258388183f94da7f82Jeff Brown        @Override
45324fa6c0dd42df057729e1a258388183f94da7f82Jeff Brown        public void addCallback(Callback callback, Handler handler) {
45424fa6c0dd42df057729e1a258388183f94da7f82Jeff Brown        }
45524fa6c0dd42df057729e1a258388183f94da7f82Jeff Brown
45624fa6c0dd42df057729e1a258388183f94da7f82Jeff Brown        @Override
45724fa6c0dd42df057729e1a258388183f94da7f82Jeff Brown        public void removeCallback(Callback callback) {
45824fa6c0dd42df057729e1a258388183f94da7f82Jeff Brown        }
45924fa6c0dd42df057729e1a258388183f94da7f82Jeff Brown
46024fa6c0dd42df057729e1a258388183f94da7f82Jeff Brown        @Override
46124fa6c0dd42df057729e1a258388183f94da7f82Jeff Brown        public boolean dispatchMediaButtonEvent(KeyEvent event) {
46224fa6c0dd42df057729e1a258388183f94da7f82Jeff Brown            return false;
46324fa6c0dd42df057729e1a258388183f94da7f82Jeff Brown        }
46424fa6c0dd42df057729e1a258388183f94da7f82Jeff Brown
46524fa6c0dd42df057729e1a258388183f94da7f82Jeff Brown        @Override
46624fa6c0dd42df057729e1a258388183f94da7f82Jeff Brown        public TransportControls getTransportControls() {
46724fa6c0dd42df057729e1a258388183f94da7f82Jeff Brown            return null;
46824fa6c0dd42df057729e1a258388183f94da7f82Jeff Brown        }
46924fa6c0dd42df057729e1a258388183f94da7f82Jeff Brown
47024fa6c0dd42df057729e1a258388183f94da7f82Jeff Brown        @Override
47124fa6c0dd42df057729e1a258388183f94da7f82Jeff Brown        public PlaybackStateCompat getPlaybackState() {
47224fa6c0dd42df057729e1a258388183f94da7f82Jeff Brown            return null;
47324fa6c0dd42df057729e1a258388183f94da7f82Jeff Brown        }
47424fa6c0dd42df057729e1a258388183f94da7f82Jeff Brown
47524fa6c0dd42df057729e1a258388183f94da7f82Jeff Brown        @Override
47624fa6c0dd42df057729e1a258388183f94da7f82Jeff Brown        public MediaMetadataCompat getMetadata() {
47724fa6c0dd42df057729e1a258388183f94da7f82Jeff Brown            return null;
47824fa6c0dd42df057729e1a258388183f94da7f82Jeff Brown        }
47924fa6c0dd42df057729e1a258388183f94da7f82Jeff Brown
48024fa6c0dd42df057729e1a258388183f94da7f82Jeff Brown        @Override
48124fa6c0dd42df057729e1a258388183f94da7f82Jeff Brown        public int getRatingType() {
48224fa6c0dd42df057729e1a258388183f94da7f82Jeff Brown            return 0;
48324fa6c0dd42df057729e1a258388183f94da7f82Jeff Brown        }
48424fa6c0dd42df057729e1a258388183f94da7f82Jeff Brown
48524fa6c0dd42df057729e1a258388183f94da7f82Jeff Brown        @Override
48624fa6c0dd42df057729e1a258388183f94da7f82Jeff Brown        public VolumeInfo getVolumeInfo() {
48724fa6c0dd42df057729e1a258388183f94da7f82Jeff Brown            return null;
48824fa6c0dd42df057729e1a258388183f94da7f82Jeff Brown        }
48924fa6c0dd42df057729e1a258388183f94da7f82Jeff Brown
49024fa6c0dd42df057729e1a258388183f94da7f82Jeff Brown        @Override
491b530c89bba371d2d575f10480b2e90914b0d3f3fGabriel Peal        public void sendCommand(String command, Bundle params, ResultReceiver cb) {
49224fa6c0dd42df057729e1a258388183f94da7f82Jeff Brown        }
49324fa6c0dd42df057729e1a258388183f94da7f82Jeff Brown
49424fa6c0dd42df057729e1a258388183f94da7f82Jeff Brown        @Override
49524fa6c0dd42df057729e1a258388183f94da7f82Jeff Brown        public Object getMediaController() {
49624fa6c0dd42df057729e1a258388183f94da7f82Jeff Brown            return null;
49724fa6c0dd42df057729e1a258388183f94da7f82Jeff Brown        }
49824fa6c0dd42df057729e1a258388183f94da7f82Jeff Brown    }
49924fa6c0dd42df057729e1a258388183f94da7f82Jeff Brown
50024fa6c0dd42df057729e1a258388183f94da7f82Jeff Brown    static class MediaControllerImplApi21 implements MediaControllerImpl {
50124fa6c0dd42df057729e1a258388183f94da7f82Jeff Brown        private final Object mControllerObj;
50224fa6c0dd42df057729e1a258388183f94da7f82Jeff Brown
50324fa6c0dd42df057729e1a258388183f94da7f82Jeff Brown        public MediaControllerImplApi21(Context context, MediaSessionCompat session) {
5045c41750574ba65da432b69f89cd32dc356281005RoboErik            mControllerObj = MediaControllerCompatApi21.fromToken(context,
50524fa6c0dd42df057729e1a258388183f94da7f82Jeff Brown                    session.getSessionToken().getToken());
50624fa6c0dd42df057729e1a258388183f94da7f82Jeff Brown        }
50724fa6c0dd42df057729e1a258388183f94da7f82Jeff Brown
50824fa6c0dd42df057729e1a258388183f94da7f82Jeff Brown        public MediaControllerImplApi21(Context context, MediaSessionCompat.Token sessionToken)
50924fa6c0dd42df057729e1a258388183f94da7f82Jeff Brown                throws RemoteException {
51024fa6c0dd42df057729e1a258388183f94da7f82Jeff Brown            // TODO: refactor framework implementation
5115c41750574ba65da432b69f89cd32dc356281005RoboErik            mControllerObj = MediaControllerCompatApi21.fromToken(context,
51224fa6c0dd42df057729e1a258388183f94da7f82Jeff Brown                    sessionToken.getToken());
51324fa6c0dd42df057729e1a258388183f94da7f82Jeff Brown            if (mControllerObj == null) throw new RemoteException();
51424fa6c0dd42df057729e1a258388183f94da7f82Jeff Brown        }
51524fa6c0dd42df057729e1a258388183f94da7f82Jeff Brown
51624fa6c0dd42df057729e1a258388183f94da7f82Jeff Brown        @Override
51724fa6c0dd42df057729e1a258388183f94da7f82Jeff Brown        public void addCallback(Callback callback, Handler handler) {
51824fa6c0dd42df057729e1a258388183f94da7f82Jeff Brown            MediaControllerCompatApi21.addCallback(mControllerObj, callback.mCallbackObj, handler);
51924fa6c0dd42df057729e1a258388183f94da7f82Jeff Brown        }
52024fa6c0dd42df057729e1a258388183f94da7f82Jeff Brown
52124fa6c0dd42df057729e1a258388183f94da7f82Jeff Brown        @Override
52224fa6c0dd42df057729e1a258388183f94da7f82Jeff Brown        public void removeCallback(Callback callback) {
52324fa6c0dd42df057729e1a258388183f94da7f82Jeff Brown            MediaControllerCompatApi21.removeCallback(mControllerObj, callback.mCallbackObj);
52424fa6c0dd42df057729e1a258388183f94da7f82Jeff Brown        }
52524fa6c0dd42df057729e1a258388183f94da7f82Jeff Brown
52624fa6c0dd42df057729e1a258388183f94da7f82Jeff Brown        @Override
52724fa6c0dd42df057729e1a258388183f94da7f82Jeff Brown        public boolean dispatchMediaButtonEvent(KeyEvent event) {
52824fa6c0dd42df057729e1a258388183f94da7f82Jeff Brown            return MediaControllerCompatApi21.dispatchMediaButtonEvent(mControllerObj, event);
52924fa6c0dd42df057729e1a258388183f94da7f82Jeff Brown        }
53024fa6c0dd42df057729e1a258388183f94da7f82Jeff Brown
53124fa6c0dd42df057729e1a258388183f94da7f82Jeff Brown        @Override
53224fa6c0dd42df057729e1a258388183f94da7f82Jeff Brown        public TransportControls getTransportControls() {
5331435afe32073dee10e721dfb6122ce6a194a6412RoboErik            Object controlsObj = MediaControllerCompatApi21.getTransportControls(mControllerObj);
53424fa6c0dd42df057729e1a258388183f94da7f82Jeff Brown            return controlsObj != null ? new TransportControlsApi21(controlsObj) : null;
53524fa6c0dd42df057729e1a258388183f94da7f82Jeff Brown        }
53624fa6c0dd42df057729e1a258388183f94da7f82Jeff Brown
53724fa6c0dd42df057729e1a258388183f94da7f82Jeff Brown        @Override
53824fa6c0dd42df057729e1a258388183f94da7f82Jeff Brown        public PlaybackStateCompat getPlaybackState() {
53924fa6c0dd42df057729e1a258388183f94da7f82Jeff Brown            Object stateObj = MediaControllerCompatApi21.getPlaybackState(mControllerObj);
54024fa6c0dd42df057729e1a258388183f94da7f82Jeff Brown            return stateObj != null ? PlaybackStateCompat.fromPlaybackState(stateObj) : null;
54124fa6c0dd42df057729e1a258388183f94da7f82Jeff Brown        }
54224fa6c0dd42df057729e1a258388183f94da7f82Jeff Brown
54324fa6c0dd42df057729e1a258388183f94da7f82Jeff Brown        @Override
54424fa6c0dd42df057729e1a258388183f94da7f82Jeff Brown        public MediaMetadataCompat getMetadata() {
54524fa6c0dd42df057729e1a258388183f94da7f82Jeff Brown            Object metadataObj = MediaControllerCompatApi21.getMetadata(mControllerObj);
54624fa6c0dd42df057729e1a258388183f94da7f82Jeff Brown            return metadataObj != null ? MediaMetadataCompat.fromMediaMetadata(metadataObj) : null;
54724fa6c0dd42df057729e1a258388183f94da7f82Jeff Brown        }
54824fa6c0dd42df057729e1a258388183f94da7f82Jeff Brown
54924fa6c0dd42df057729e1a258388183f94da7f82Jeff Brown        @Override
55024fa6c0dd42df057729e1a258388183f94da7f82Jeff Brown        public int getRatingType() {
55124fa6c0dd42df057729e1a258388183f94da7f82Jeff Brown            return MediaControllerCompatApi21.getRatingType(mControllerObj);
55224fa6c0dd42df057729e1a258388183f94da7f82Jeff Brown        }
55324fa6c0dd42df057729e1a258388183f94da7f82Jeff Brown
55424fa6c0dd42df057729e1a258388183f94da7f82Jeff Brown        @Override
55524fa6c0dd42df057729e1a258388183f94da7f82Jeff Brown        public VolumeInfo getVolumeInfo() {
55624fa6c0dd42df057729e1a258388183f94da7f82Jeff Brown            Object volumeInfoObj = MediaControllerCompatApi21.getVolumeInfo(mControllerObj);
55724fa6c0dd42df057729e1a258388183f94da7f82Jeff Brown            return volumeInfoObj != null ? new VolumeInfo(
55824fa6c0dd42df057729e1a258388183f94da7f82Jeff Brown                    MediaControllerCompatApi21.VolumeInfo.getVolumeType(volumeInfoObj),
5591435afe32073dee10e721dfb6122ce6a194a6412RoboErik                    MediaControllerCompatApi21.VolumeInfo.getLegacyAudioStream(volumeInfoObj),
56024fa6c0dd42df057729e1a258388183f94da7f82Jeff Brown                    MediaControllerCompatApi21.VolumeInfo.getVolumeControl(volumeInfoObj),
56124fa6c0dd42df057729e1a258388183f94da7f82Jeff Brown                    MediaControllerCompatApi21.VolumeInfo.getMaxVolume(volumeInfoObj),
56224fa6c0dd42df057729e1a258388183f94da7f82Jeff Brown                    MediaControllerCompatApi21.VolumeInfo.getCurrentVolume(volumeInfoObj)) : null;
56324fa6c0dd42df057729e1a258388183f94da7f82Jeff Brown        }
56424fa6c0dd42df057729e1a258388183f94da7f82Jeff Brown
56524fa6c0dd42df057729e1a258388183f94da7f82Jeff Brown        @Override
566b530c89bba371d2d575f10480b2e90914b0d3f3fGabriel Peal        public void sendCommand(String command, Bundle params, ResultReceiver cb) {
567b530c89bba371d2d575f10480b2e90914b0d3f3fGabriel Peal            MediaControllerCompatApi21.sendCommand(mControllerObj, command, params, cb);
56824fa6c0dd42df057729e1a258388183f94da7f82Jeff Brown        }
56924fa6c0dd42df057729e1a258388183f94da7f82Jeff Brown
57024fa6c0dd42df057729e1a258388183f94da7f82Jeff Brown        @Override
57124fa6c0dd42df057729e1a258388183f94da7f82Jeff Brown        public Object getMediaController() {
57224fa6c0dd42df057729e1a258388183f94da7f82Jeff Brown            return mControllerObj;
57324fa6c0dd42df057729e1a258388183f94da7f82Jeff Brown        }
57424fa6c0dd42df057729e1a258388183f94da7f82Jeff Brown    }
57524fa6c0dd42df057729e1a258388183f94da7f82Jeff Brown
57624fa6c0dd42df057729e1a258388183f94da7f82Jeff Brown    static class TransportControlsApi21 extends TransportControls {
57724fa6c0dd42df057729e1a258388183f94da7f82Jeff Brown        private final Object mControlsObj;
57824fa6c0dd42df057729e1a258388183f94da7f82Jeff Brown
57924fa6c0dd42df057729e1a258388183f94da7f82Jeff Brown        public TransportControlsApi21(Object controlsObj) {
58024fa6c0dd42df057729e1a258388183f94da7f82Jeff Brown            mControlsObj = controlsObj;
58124fa6c0dd42df057729e1a258388183f94da7f82Jeff Brown        }
58224fa6c0dd42df057729e1a258388183f94da7f82Jeff Brown
58324fa6c0dd42df057729e1a258388183f94da7f82Jeff Brown        @Override
58424fa6c0dd42df057729e1a258388183f94da7f82Jeff Brown        public void play() {
58524fa6c0dd42df057729e1a258388183f94da7f82Jeff Brown            MediaControllerCompatApi21.TransportControls.play(mControlsObj);
58624fa6c0dd42df057729e1a258388183f94da7f82Jeff Brown        }
58724fa6c0dd42df057729e1a258388183f94da7f82Jeff Brown
58824fa6c0dd42df057729e1a258388183f94da7f82Jeff Brown        @Override
58924fa6c0dd42df057729e1a258388183f94da7f82Jeff Brown        public void pause() {
59024fa6c0dd42df057729e1a258388183f94da7f82Jeff Brown            MediaControllerCompatApi21.TransportControls.pause(mControlsObj);
59124fa6c0dd42df057729e1a258388183f94da7f82Jeff Brown        }
59224fa6c0dd42df057729e1a258388183f94da7f82Jeff Brown
59324fa6c0dd42df057729e1a258388183f94da7f82Jeff Brown        @Override
59424fa6c0dd42df057729e1a258388183f94da7f82Jeff Brown        public void stop() {
59524fa6c0dd42df057729e1a258388183f94da7f82Jeff Brown            MediaControllerCompatApi21.TransportControls.stop(mControlsObj);
59624fa6c0dd42df057729e1a258388183f94da7f82Jeff Brown        }
59724fa6c0dd42df057729e1a258388183f94da7f82Jeff Brown
59824fa6c0dd42df057729e1a258388183f94da7f82Jeff Brown        @Override
59924fa6c0dd42df057729e1a258388183f94da7f82Jeff Brown        public void seekTo(long pos) {
60024fa6c0dd42df057729e1a258388183f94da7f82Jeff Brown            MediaControllerCompatApi21.TransportControls.seekTo(mControlsObj, pos);
60124fa6c0dd42df057729e1a258388183f94da7f82Jeff Brown        }
60224fa6c0dd42df057729e1a258388183f94da7f82Jeff Brown
60324fa6c0dd42df057729e1a258388183f94da7f82Jeff Brown        @Override
60424fa6c0dd42df057729e1a258388183f94da7f82Jeff Brown        public void fastForward() {
60524fa6c0dd42df057729e1a258388183f94da7f82Jeff Brown            MediaControllerCompatApi21.TransportControls.fastForward(mControlsObj);
60624fa6c0dd42df057729e1a258388183f94da7f82Jeff Brown        }
60724fa6c0dd42df057729e1a258388183f94da7f82Jeff Brown
60824fa6c0dd42df057729e1a258388183f94da7f82Jeff Brown        @Override
60924fa6c0dd42df057729e1a258388183f94da7f82Jeff Brown        public void rewind() {
61024fa6c0dd42df057729e1a258388183f94da7f82Jeff Brown            MediaControllerCompatApi21.TransportControls.rewind(mControlsObj);
61124fa6c0dd42df057729e1a258388183f94da7f82Jeff Brown        }
61224fa6c0dd42df057729e1a258388183f94da7f82Jeff Brown
61324fa6c0dd42df057729e1a258388183f94da7f82Jeff Brown        @Override
61424fa6c0dd42df057729e1a258388183f94da7f82Jeff Brown        public void skipToNext() {
61524fa6c0dd42df057729e1a258388183f94da7f82Jeff Brown            MediaControllerCompatApi21.TransportControls.skipToNext(mControlsObj);
61624fa6c0dd42df057729e1a258388183f94da7f82Jeff Brown        }
61724fa6c0dd42df057729e1a258388183f94da7f82Jeff Brown
61824fa6c0dd42df057729e1a258388183f94da7f82Jeff Brown        @Override
61924fa6c0dd42df057729e1a258388183f94da7f82Jeff Brown        public void skipToPrevious() {
62024fa6c0dd42df057729e1a258388183f94da7f82Jeff Brown            MediaControllerCompatApi21.TransportControls.skipToPrevious(mControlsObj);
62124fa6c0dd42df057729e1a258388183f94da7f82Jeff Brown        }
62224fa6c0dd42df057729e1a258388183f94da7f82Jeff Brown
62324fa6c0dd42df057729e1a258388183f94da7f82Jeff Brown        @Override
62424fa6c0dd42df057729e1a258388183f94da7f82Jeff Brown        public void setRating(RatingCompat rating) {
62524fa6c0dd42df057729e1a258388183f94da7f82Jeff Brown            MediaControllerCompatApi21.TransportControls.setRating(mControlsObj,
62624fa6c0dd42df057729e1a258388183f94da7f82Jeff Brown                    rating != null ? rating.getRating() : null);
62724fa6c0dd42df057729e1a258388183f94da7f82Jeff Brown        }
62824fa6c0dd42df057729e1a258388183f94da7f82Jeff Brown    }
62924fa6c0dd42df057729e1a258388183f94da7f82Jeff Brown}
630