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
19aeb95a772d4365008145407ed52dfbaa61d3c4acRoboErikimport android.app.PendingIntent;
2024fa6c0dd42df057729e1a258388183f94da7f82Jeff Brownimport android.content.Context;
21aeb95a772d4365008145407ed52dfbaa61d3c4acRoboErikimport android.media.AudioManager;
22aeb95a772d4365008145407ed52dfbaa61d3c4acRoboErikimport android.net.Uri;
2324fa6c0dd42df057729e1a258388183f94da7f82Jeff Brownimport android.os.Bundle;
2424fa6c0dd42df057729e1a258388183f94da7f82Jeff Brownimport android.os.Handler;
25e49860b0f76d8336c1d41831ed370b0ff94278efRoboErikimport android.os.IBinder;
26e49860b0f76d8336c1d41831ed370b0ff94278efRoboErikimport android.os.Looper;
27e49860b0f76d8336c1d41831ed370b0ff94278efRoboErikimport android.os.Message;
2824fa6c0dd42df057729e1a258388183f94da7f82Jeff Brownimport android.os.RemoteException;
2924fa6c0dd42df057729e1a258388183f94da7f82Jeff Brownimport android.os.ResultReceiver;
3024fa6c0dd42df057729e1a258388183f94da7f82Jeff Brownimport android.support.v4.media.MediaMetadataCompat;
3124fa6c0dd42df057729e1a258388183f94da7f82Jeff Brownimport android.support.v4.media.RatingCompat;
3224fa6c0dd42df057729e1a258388183f94da7f82Jeff Brownimport android.support.v4.media.VolumeProviderCompat;
33e49860b0f76d8336c1d41831ed370b0ff94278efRoboErikimport android.support.v4.media.session.MediaSessionCompat.QueueItem;
34aeb95a772d4365008145407ed52dfbaa61d3c4acRoboErikimport android.support.v4.media.session.PlaybackStateCompat.CustomAction;
3524fa6c0dd42df057729e1a258388183f94da7f82Jeff Brownimport android.text.TextUtils;
36e49860b0f76d8336c1d41831ed370b0ff94278efRoboErikimport android.util.Log;
3724fa6c0dd42df057729e1a258388183f94da7f82Jeff Brownimport android.view.KeyEvent;
3824fa6c0dd42df057729e1a258388183f94da7f82Jeff Brown
39aeb95a772d4365008145407ed52dfbaa61d3c4acRoboErikimport java.util.ArrayList;
40aeb95a772d4365008145407ed52dfbaa61d3c4acRoboErikimport java.util.List;
41aeb95a772d4365008145407ed52dfbaa61d3c4acRoboErik
4224fa6c0dd42df057729e1a258388183f94da7f82Jeff Brown/**
4324fa6c0dd42df057729e1a258388183f94da7f82Jeff Brown * Allows an app to interact with an ongoing media session. Media buttons and
4424fa6c0dd42df057729e1a258388183f94da7f82Jeff Brown * other commands can be sent to the session. A callback may be registered to
4524fa6c0dd42df057729e1a258388183f94da7f82Jeff Brown * receive updates from the session, such as metadata and play state changes.
4624fa6c0dd42df057729e1a258388183f94da7f82Jeff Brown * <p>
4724fa6c0dd42df057729e1a258388183f94da7f82Jeff Brown * A MediaController can be created if you have a {@link MediaSessionCompat.Token}
4824fa6c0dd42df057729e1a258388183f94da7f82Jeff Brown * from the session owner.
4924fa6c0dd42df057729e1a258388183f94da7f82Jeff Brown * <p>
5024fa6c0dd42df057729e1a258388183f94da7f82Jeff Brown * MediaController objects are thread-safe.
5124fa6c0dd42df057729e1a258388183f94da7f82Jeff Brown * <p>
5224fa6c0dd42df057729e1a258388183f94da7f82Jeff Brown * This is a helper for accessing features in {@link android.media.session.MediaSession}
5324fa6c0dd42df057729e1a258388183f94da7f82Jeff Brown * introduced after API level 4 in a backwards compatible fashion.
5424fa6c0dd42df057729e1a258388183f94da7f82Jeff Brown */
5524fa6c0dd42df057729e1a258388183f94da7f82Jeff Brownpublic final class MediaControllerCompat {
56e49860b0f76d8336c1d41831ed370b0ff94278efRoboErik    private static final String TAG = "MediaControllerCompat";
57e49860b0f76d8336c1d41831ed370b0ff94278efRoboErik
5824fa6c0dd42df057729e1a258388183f94da7f82Jeff Brown    private final MediaControllerImpl mImpl;
5994be6100218126ce6a08bf1f56209578500b361fRoboErik    private final MediaSessionCompat.Token mToken;
6024fa6c0dd42df057729e1a258388183f94da7f82Jeff Brown
6124fa6c0dd42df057729e1a258388183f94da7f82Jeff Brown    /**
6224fa6c0dd42df057729e1a258388183f94da7f82Jeff Brown     * Creates a media controller from a session.
6324fa6c0dd42df057729e1a258388183f94da7f82Jeff Brown     *
6424fa6c0dd42df057729e1a258388183f94da7f82Jeff Brown     * @param session The session to be controlled.
6524fa6c0dd42df057729e1a258388183f94da7f82Jeff Brown     */
6624fa6c0dd42df057729e1a258388183f94da7f82Jeff Brown    public MediaControllerCompat(Context context, MediaSessionCompat session) {
6724fa6c0dd42df057729e1a258388183f94da7f82Jeff Brown        if (session == null) {
6824fa6c0dd42df057729e1a258388183f94da7f82Jeff Brown            throw new IllegalArgumentException("session must not be null");
6924fa6c0dd42df057729e1a258388183f94da7f82Jeff Brown        }
7094be6100218126ce6a08bf1f56209578500b361fRoboErik        mToken = session.getSessionToken();
7124fa6c0dd42df057729e1a258388183f94da7f82Jeff Brown
72b51f456b92aeb62d5aa9d67e1fb2725b2035fdddIan Lake        if (android.os.Build.VERSION.SDK_INT >= 23) {
73b51f456b92aeb62d5aa9d67e1fb2725b2035fdddIan Lake            mImpl = new MediaControllerImplApi23(context, session);
74b51f456b92aeb62d5aa9d67e1fb2725b2035fdddIan Lake        } else if (android.os.Build.VERSION.SDK_INT >= 21) {
7524fa6c0dd42df057729e1a258388183f94da7f82Jeff Brown            mImpl = new MediaControllerImplApi21(context, session);
7624fa6c0dd42df057729e1a258388183f94da7f82Jeff Brown        } else {
77e49860b0f76d8336c1d41831ed370b0ff94278efRoboErik            mImpl = new MediaControllerImplBase(mToken);
7824fa6c0dd42df057729e1a258388183f94da7f82Jeff Brown        }
7924fa6c0dd42df057729e1a258388183f94da7f82Jeff Brown    }
8024fa6c0dd42df057729e1a258388183f94da7f82Jeff Brown
8124fa6c0dd42df057729e1a258388183f94da7f82Jeff Brown    /**
8224fa6c0dd42df057729e1a258388183f94da7f82Jeff Brown     * Creates a media controller from a session token which may have
8324fa6c0dd42df057729e1a258388183f94da7f82Jeff Brown     * been obtained from another process.
8424fa6c0dd42df057729e1a258388183f94da7f82Jeff Brown     *
8524fa6c0dd42df057729e1a258388183f94da7f82Jeff Brown     * @param sessionToken The token of the session to be controlled.
8624fa6c0dd42df057729e1a258388183f94da7f82Jeff Brown     * @throws RemoteException if the session is not accessible.
8724fa6c0dd42df057729e1a258388183f94da7f82Jeff Brown     */
8824fa6c0dd42df057729e1a258388183f94da7f82Jeff Brown    public MediaControllerCompat(Context context, MediaSessionCompat.Token sessionToken)
8924fa6c0dd42df057729e1a258388183f94da7f82Jeff Brown            throws RemoteException {
9024fa6c0dd42df057729e1a258388183f94da7f82Jeff Brown        if (sessionToken == null) {
9124fa6c0dd42df057729e1a258388183f94da7f82Jeff Brown            throw new IllegalArgumentException("sessionToken must not be null");
9224fa6c0dd42df057729e1a258388183f94da7f82Jeff Brown        }
9394be6100218126ce6a08bf1f56209578500b361fRoboErik        mToken = sessionToken;
9424fa6c0dd42df057729e1a258388183f94da7f82Jeff Brown
9524fa6c0dd42df057729e1a258388183f94da7f82Jeff Brown        if (android.os.Build.VERSION.SDK_INT >= 21) {
9624fa6c0dd42df057729e1a258388183f94da7f82Jeff Brown            mImpl = new MediaControllerImplApi21(context, sessionToken);
9724fa6c0dd42df057729e1a258388183f94da7f82Jeff Brown        } else {
98e49860b0f76d8336c1d41831ed370b0ff94278efRoboErik            mImpl = new MediaControllerImplBase(mToken);
9924fa6c0dd42df057729e1a258388183f94da7f82Jeff Brown        }
10024fa6c0dd42df057729e1a258388183f94da7f82Jeff Brown    }
10124fa6c0dd42df057729e1a258388183f94da7f82Jeff Brown
10224fa6c0dd42df057729e1a258388183f94da7f82Jeff Brown    /**
10324fa6c0dd42df057729e1a258388183f94da7f82Jeff Brown     * Get a {@link TransportControls} instance for this session.
10424fa6c0dd42df057729e1a258388183f94da7f82Jeff Brown     *
10524fa6c0dd42df057729e1a258388183f94da7f82Jeff Brown     * @return A controls instance
10624fa6c0dd42df057729e1a258388183f94da7f82Jeff Brown     */
10724fa6c0dd42df057729e1a258388183f94da7f82Jeff Brown    public TransportControls getTransportControls() {
10824fa6c0dd42df057729e1a258388183f94da7f82Jeff Brown        return mImpl.getTransportControls();
10924fa6c0dd42df057729e1a258388183f94da7f82Jeff Brown    }
11024fa6c0dd42df057729e1a258388183f94da7f82Jeff Brown
11124fa6c0dd42df057729e1a258388183f94da7f82Jeff Brown    /**
11224fa6c0dd42df057729e1a258388183f94da7f82Jeff Brown     * Send the specified media button event to the session. Only media keys can
11324fa6c0dd42df057729e1a258388183f94da7f82Jeff Brown     * be sent by this method, other keys will be ignored.
11424fa6c0dd42df057729e1a258388183f94da7f82Jeff Brown     *
11524fa6c0dd42df057729e1a258388183f94da7f82Jeff Brown     * @param keyEvent The media button event to dispatch.
11624fa6c0dd42df057729e1a258388183f94da7f82Jeff Brown     * @return true if the event was sent to the session, false otherwise.
11724fa6c0dd42df057729e1a258388183f94da7f82Jeff Brown     */
11824fa6c0dd42df057729e1a258388183f94da7f82Jeff Brown    public boolean dispatchMediaButtonEvent(KeyEvent keyEvent) {
11924fa6c0dd42df057729e1a258388183f94da7f82Jeff Brown        if (keyEvent == null) {
12024fa6c0dd42df057729e1a258388183f94da7f82Jeff Brown            throw new IllegalArgumentException("KeyEvent may not be null");
12124fa6c0dd42df057729e1a258388183f94da7f82Jeff Brown        }
12224fa6c0dd42df057729e1a258388183f94da7f82Jeff Brown        return mImpl.dispatchMediaButtonEvent(keyEvent);
12324fa6c0dd42df057729e1a258388183f94da7f82Jeff Brown    }
12424fa6c0dd42df057729e1a258388183f94da7f82Jeff Brown
12524fa6c0dd42df057729e1a258388183f94da7f82Jeff Brown    /**
12624fa6c0dd42df057729e1a258388183f94da7f82Jeff Brown     * Get the current playback state for this session.
12724fa6c0dd42df057729e1a258388183f94da7f82Jeff Brown     *
12824fa6c0dd42df057729e1a258388183f94da7f82Jeff Brown     * @return The current PlaybackState or null
12924fa6c0dd42df057729e1a258388183f94da7f82Jeff Brown     */
13024fa6c0dd42df057729e1a258388183f94da7f82Jeff Brown    public PlaybackStateCompat getPlaybackState() {
13124fa6c0dd42df057729e1a258388183f94da7f82Jeff Brown        return mImpl.getPlaybackState();
13224fa6c0dd42df057729e1a258388183f94da7f82Jeff Brown    }
13324fa6c0dd42df057729e1a258388183f94da7f82Jeff Brown
13424fa6c0dd42df057729e1a258388183f94da7f82Jeff Brown    /**
13524fa6c0dd42df057729e1a258388183f94da7f82Jeff Brown     * Get the current metadata for this session.
13624fa6c0dd42df057729e1a258388183f94da7f82Jeff Brown     *
13724fa6c0dd42df057729e1a258388183f94da7f82Jeff Brown     * @return The current MediaMetadata or null.
13824fa6c0dd42df057729e1a258388183f94da7f82Jeff Brown     */
13924fa6c0dd42df057729e1a258388183f94da7f82Jeff Brown    public MediaMetadataCompat getMetadata() {
14024fa6c0dd42df057729e1a258388183f94da7f82Jeff Brown        return mImpl.getMetadata();
14124fa6c0dd42df057729e1a258388183f94da7f82Jeff Brown    }
14224fa6c0dd42df057729e1a258388183f94da7f82Jeff Brown
14324fa6c0dd42df057729e1a258388183f94da7f82Jeff Brown    /**
144aeb95a772d4365008145407ed52dfbaa61d3c4acRoboErik     * Get the current play queue for this session if one is set. If you only
145aeb95a772d4365008145407ed52dfbaa61d3c4acRoboErik     * care about the current item {@link #getMetadata()} should be used.
146aeb95a772d4365008145407ed52dfbaa61d3c4acRoboErik     *
147aeb95a772d4365008145407ed52dfbaa61d3c4acRoboErik     * @return The current play queue or null.
148aeb95a772d4365008145407ed52dfbaa61d3c4acRoboErik     */
149aeb95a772d4365008145407ed52dfbaa61d3c4acRoboErik    public List<MediaSessionCompat.QueueItem> getQueue() {
150aeb95a772d4365008145407ed52dfbaa61d3c4acRoboErik        return mImpl.getQueue();
151aeb95a772d4365008145407ed52dfbaa61d3c4acRoboErik    }
152aeb95a772d4365008145407ed52dfbaa61d3c4acRoboErik
153aeb95a772d4365008145407ed52dfbaa61d3c4acRoboErik    /**
154aeb95a772d4365008145407ed52dfbaa61d3c4acRoboErik     * Get the queue title for this session.
155aeb95a772d4365008145407ed52dfbaa61d3c4acRoboErik     */
156aeb95a772d4365008145407ed52dfbaa61d3c4acRoboErik    public CharSequence getQueueTitle() {
157aeb95a772d4365008145407ed52dfbaa61d3c4acRoboErik        return mImpl.getQueueTitle();
158aeb95a772d4365008145407ed52dfbaa61d3c4acRoboErik    }
159aeb95a772d4365008145407ed52dfbaa61d3c4acRoboErik
160aeb95a772d4365008145407ed52dfbaa61d3c4acRoboErik    /**
161aeb95a772d4365008145407ed52dfbaa61d3c4acRoboErik     * Get the extras for this session.
162aeb95a772d4365008145407ed52dfbaa61d3c4acRoboErik     */
163aeb95a772d4365008145407ed52dfbaa61d3c4acRoboErik    public Bundle getExtras() {
164aeb95a772d4365008145407ed52dfbaa61d3c4acRoboErik        return mImpl.getExtras();
165aeb95a772d4365008145407ed52dfbaa61d3c4acRoboErik    }
166aeb95a772d4365008145407ed52dfbaa61d3c4acRoboErik
167aeb95a772d4365008145407ed52dfbaa61d3c4acRoboErik    /**
16824fa6c0dd42df057729e1a258388183f94da7f82Jeff Brown     * Get the rating type supported by the session. One of:
16924fa6c0dd42df057729e1a258388183f94da7f82Jeff Brown     * <ul>
17024fa6c0dd42df057729e1a258388183f94da7f82Jeff Brown     * <li>{@link RatingCompat#RATING_NONE}</li>
17124fa6c0dd42df057729e1a258388183f94da7f82Jeff Brown     * <li>{@link RatingCompat#RATING_HEART}</li>
17224fa6c0dd42df057729e1a258388183f94da7f82Jeff Brown     * <li>{@link RatingCompat#RATING_THUMB_UP_DOWN}</li>
17324fa6c0dd42df057729e1a258388183f94da7f82Jeff Brown     * <li>{@link RatingCompat#RATING_3_STARS}</li>
17424fa6c0dd42df057729e1a258388183f94da7f82Jeff Brown     * <li>{@link RatingCompat#RATING_4_STARS}</li>
17524fa6c0dd42df057729e1a258388183f94da7f82Jeff Brown     * <li>{@link RatingCompat#RATING_5_STARS}</li>
17624fa6c0dd42df057729e1a258388183f94da7f82Jeff Brown     * <li>{@link RatingCompat#RATING_PERCENTAGE}</li>
17724fa6c0dd42df057729e1a258388183f94da7f82Jeff Brown     * </ul>
17824fa6c0dd42df057729e1a258388183f94da7f82Jeff Brown     *
17924fa6c0dd42df057729e1a258388183f94da7f82Jeff Brown     * @return The supported rating type
18024fa6c0dd42df057729e1a258388183f94da7f82Jeff Brown     */
18124fa6c0dd42df057729e1a258388183f94da7f82Jeff Brown    public int getRatingType() {
18224fa6c0dd42df057729e1a258388183f94da7f82Jeff Brown        return mImpl.getRatingType();
18324fa6c0dd42df057729e1a258388183f94da7f82Jeff Brown    }
18424fa6c0dd42df057729e1a258388183f94da7f82Jeff Brown
18524fa6c0dd42df057729e1a258388183f94da7f82Jeff Brown    /**
186aeb95a772d4365008145407ed52dfbaa61d3c4acRoboErik     * Get the flags for this session. Flags are defined in
187aeb95a772d4365008145407ed52dfbaa61d3c4acRoboErik     * {@link MediaSessionCompat}.
188aeb95a772d4365008145407ed52dfbaa61d3c4acRoboErik     *
189aeb95a772d4365008145407ed52dfbaa61d3c4acRoboErik     * @return The current set of flags for the session.
190aeb95a772d4365008145407ed52dfbaa61d3c4acRoboErik     */
191aeb95a772d4365008145407ed52dfbaa61d3c4acRoboErik    public long getFlags() {
192aeb95a772d4365008145407ed52dfbaa61d3c4acRoboErik        return mImpl.getFlags();
193aeb95a772d4365008145407ed52dfbaa61d3c4acRoboErik    }
194aeb95a772d4365008145407ed52dfbaa61d3c4acRoboErik
195aeb95a772d4365008145407ed52dfbaa61d3c4acRoboErik    /**
196312f13dea7b4a9229dff784c6e94b0ec0c722b74RoboErik     * Get the current playback info for this session.
19724fa6c0dd42df057729e1a258388183f94da7f82Jeff Brown     *
198312f13dea7b4a9229dff784c6e94b0ec0c722b74RoboErik     * @return The current playback info or null.
19924fa6c0dd42df057729e1a258388183f94da7f82Jeff Brown     */
200312f13dea7b4a9229dff784c6e94b0ec0c722b74RoboErik    public PlaybackInfo getPlaybackInfo() {
201312f13dea7b4a9229dff784c6e94b0ec0c722b74RoboErik        return mImpl.getPlaybackInfo();
20224fa6c0dd42df057729e1a258388183f94da7f82Jeff Brown    }
20324fa6c0dd42df057729e1a258388183f94da7f82Jeff Brown
20424fa6c0dd42df057729e1a258388183f94da7f82Jeff Brown    /**
205aeb95a772d4365008145407ed52dfbaa61d3c4acRoboErik     * Get an intent for launching UI associated with this session if one
206aeb95a772d4365008145407ed52dfbaa61d3c4acRoboErik     * exists.
207aeb95a772d4365008145407ed52dfbaa61d3c4acRoboErik     *
208aeb95a772d4365008145407ed52dfbaa61d3c4acRoboErik     * @return A {@link PendingIntent} to launch UI or null.
209aeb95a772d4365008145407ed52dfbaa61d3c4acRoboErik     */
210aeb95a772d4365008145407ed52dfbaa61d3c4acRoboErik    public PendingIntent getSessionActivity() {
211aeb95a772d4365008145407ed52dfbaa61d3c4acRoboErik        return mImpl.getSessionActivity();
212aeb95a772d4365008145407ed52dfbaa61d3c4acRoboErik    }
213aeb95a772d4365008145407ed52dfbaa61d3c4acRoboErik
214aeb95a772d4365008145407ed52dfbaa61d3c4acRoboErik    /**
21594be6100218126ce6a08bf1f56209578500b361fRoboErik     * Get the token for the session this controller is connected to.
21694be6100218126ce6a08bf1f56209578500b361fRoboErik     *
21794be6100218126ce6a08bf1f56209578500b361fRoboErik     * @return The session's token.
21894be6100218126ce6a08bf1f56209578500b361fRoboErik     */
21994be6100218126ce6a08bf1f56209578500b361fRoboErik    public MediaSessionCompat.Token getSessionToken() {
22094be6100218126ce6a08bf1f56209578500b361fRoboErik        return mToken;
22194be6100218126ce6a08bf1f56209578500b361fRoboErik    }
22294be6100218126ce6a08bf1f56209578500b361fRoboErik
22394be6100218126ce6a08bf1f56209578500b361fRoboErik    /**
224aeb95a772d4365008145407ed52dfbaa61d3c4acRoboErik     * Set the volume of the output this session is playing on. The command will
225aeb95a772d4365008145407ed52dfbaa61d3c4acRoboErik     * be ignored if it does not support
226aeb95a772d4365008145407ed52dfbaa61d3c4acRoboErik     * {@link VolumeProviderCompat#VOLUME_CONTROL_ABSOLUTE}. The flags in
227aeb95a772d4365008145407ed52dfbaa61d3c4acRoboErik     * {@link AudioManager} may be used to affect the handling.
228aeb95a772d4365008145407ed52dfbaa61d3c4acRoboErik     *
229aeb95a772d4365008145407ed52dfbaa61d3c4acRoboErik     * @see #getPlaybackInfo()
230aeb95a772d4365008145407ed52dfbaa61d3c4acRoboErik     * @param value The value to set it to, between 0 and the reported max.
231aeb95a772d4365008145407ed52dfbaa61d3c4acRoboErik     * @param flags Flags from {@link AudioManager} to include with the volume
232aeb95a772d4365008145407ed52dfbaa61d3c4acRoboErik     *            request.
233aeb95a772d4365008145407ed52dfbaa61d3c4acRoboErik     */
234aeb95a772d4365008145407ed52dfbaa61d3c4acRoboErik    public void setVolumeTo(int value, int flags) {
235aeb95a772d4365008145407ed52dfbaa61d3c4acRoboErik        mImpl.setVolumeTo(value, flags);
236aeb95a772d4365008145407ed52dfbaa61d3c4acRoboErik    }
237aeb95a772d4365008145407ed52dfbaa61d3c4acRoboErik
238aeb95a772d4365008145407ed52dfbaa61d3c4acRoboErik    /**
239aeb95a772d4365008145407ed52dfbaa61d3c4acRoboErik     * Adjust the volume of the output this session is playing on. The direction
240aeb95a772d4365008145407ed52dfbaa61d3c4acRoboErik     * must be one of {@link AudioManager#ADJUST_LOWER},
241aeb95a772d4365008145407ed52dfbaa61d3c4acRoboErik     * {@link AudioManager#ADJUST_RAISE}, or {@link AudioManager#ADJUST_SAME}.
242aeb95a772d4365008145407ed52dfbaa61d3c4acRoboErik     * The command will be ignored if the session does not support
243aeb95a772d4365008145407ed52dfbaa61d3c4acRoboErik     * {@link VolumeProviderCompat#VOLUME_CONTROL_RELATIVE} or
244aeb95a772d4365008145407ed52dfbaa61d3c4acRoboErik     * {@link VolumeProviderCompat#VOLUME_CONTROL_ABSOLUTE}. The flags in
245aeb95a772d4365008145407ed52dfbaa61d3c4acRoboErik     * {@link AudioManager} may be used to affect the handling.
246aeb95a772d4365008145407ed52dfbaa61d3c4acRoboErik     *
247aeb95a772d4365008145407ed52dfbaa61d3c4acRoboErik     * @see #getPlaybackInfo()
248aeb95a772d4365008145407ed52dfbaa61d3c4acRoboErik     * @param direction The direction to adjust the volume in.
249aeb95a772d4365008145407ed52dfbaa61d3c4acRoboErik     * @param flags Any flags to pass with the command.
250aeb95a772d4365008145407ed52dfbaa61d3c4acRoboErik     */
251aeb95a772d4365008145407ed52dfbaa61d3c4acRoboErik    public void adjustVolume(int direction, int flags) {
252aeb95a772d4365008145407ed52dfbaa61d3c4acRoboErik        mImpl.adjustVolume(direction, flags);
253aeb95a772d4365008145407ed52dfbaa61d3c4acRoboErik    }
254aeb95a772d4365008145407ed52dfbaa61d3c4acRoboErik
255aeb95a772d4365008145407ed52dfbaa61d3c4acRoboErik    /**
25624fa6c0dd42df057729e1a258388183f94da7f82Jeff Brown     * Adds a callback to receive updates from the Session. Updates will be
25724fa6c0dd42df057729e1a258388183f94da7f82Jeff Brown     * posted on the caller's thread.
25824fa6c0dd42df057729e1a258388183f94da7f82Jeff Brown     *
25924fa6c0dd42df057729e1a258388183f94da7f82Jeff Brown     * @param callback The callback object, must not be null.
26024fa6c0dd42df057729e1a258388183f94da7f82Jeff Brown     */
26116ac83bebda2a19930b9d692789f3b507c49951bRoboErik    public void registerCallback(Callback callback) {
26216ac83bebda2a19930b9d692789f3b507c49951bRoboErik        registerCallback(callback, null);
26324fa6c0dd42df057729e1a258388183f94da7f82Jeff Brown    }
26424fa6c0dd42df057729e1a258388183f94da7f82Jeff Brown
26524fa6c0dd42df057729e1a258388183f94da7f82Jeff Brown    /**
26624fa6c0dd42df057729e1a258388183f94da7f82Jeff Brown     * Adds a callback to receive updates from the session. Updates will be
26724fa6c0dd42df057729e1a258388183f94da7f82Jeff Brown     * posted on the specified handler's thread.
26824fa6c0dd42df057729e1a258388183f94da7f82Jeff Brown     *
26924fa6c0dd42df057729e1a258388183f94da7f82Jeff Brown     * @param callback The callback object, must not be null.
27024fa6c0dd42df057729e1a258388183f94da7f82Jeff Brown     * @param handler The handler to post updates on. If null the callers thread
27124fa6c0dd42df057729e1a258388183f94da7f82Jeff Brown     *            will be used.
27224fa6c0dd42df057729e1a258388183f94da7f82Jeff Brown     */
27316ac83bebda2a19930b9d692789f3b507c49951bRoboErik    public void registerCallback(Callback callback, Handler handler) {
27424fa6c0dd42df057729e1a258388183f94da7f82Jeff Brown        if (callback == null) {
27524fa6c0dd42df057729e1a258388183f94da7f82Jeff Brown            throw new IllegalArgumentException("callback cannot be null");
27624fa6c0dd42df057729e1a258388183f94da7f82Jeff Brown        }
27724fa6c0dd42df057729e1a258388183f94da7f82Jeff Brown        if (handler == null) {
27824fa6c0dd42df057729e1a258388183f94da7f82Jeff Brown            handler = new Handler();
27924fa6c0dd42df057729e1a258388183f94da7f82Jeff Brown        }
28016ac83bebda2a19930b9d692789f3b507c49951bRoboErik        mImpl.registerCallback(callback, handler);
28124fa6c0dd42df057729e1a258388183f94da7f82Jeff Brown    }
28224fa6c0dd42df057729e1a258388183f94da7f82Jeff Brown
28324fa6c0dd42df057729e1a258388183f94da7f82Jeff Brown    /**
28424fa6c0dd42df057729e1a258388183f94da7f82Jeff Brown     * Stop receiving updates on the specified callback. If an update has
28524fa6c0dd42df057729e1a258388183f94da7f82Jeff Brown     * already been posted you may still receive it after calling this method.
28624fa6c0dd42df057729e1a258388183f94da7f82Jeff Brown     *
28724fa6c0dd42df057729e1a258388183f94da7f82Jeff Brown     * @param callback The callback to remove
28824fa6c0dd42df057729e1a258388183f94da7f82Jeff Brown     */
28916ac83bebda2a19930b9d692789f3b507c49951bRoboErik    public void unregisterCallback(Callback callback) {
29024fa6c0dd42df057729e1a258388183f94da7f82Jeff Brown        if (callback == null) {
29124fa6c0dd42df057729e1a258388183f94da7f82Jeff Brown            throw new IllegalArgumentException("callback cannot be null");
29224fa6c0dd42df057729e1a258388183f94da7f82Jeff Brown        }
29316ac83bebda2a19930b9d692789f3b507c49951bRoboErik        mImpl.unregisterCallback(callback);
29424fa6c0dd42df057729e1a258388183f94da7f82Jeff Brown    }
29524fa6c0dd42df057729e1a258388183f94da7f82Jeff Brown
29624fa6c0dd42df057729e1a258388183f94da7f82Jeff Brown    /**
29724fa6c0dd42df057729e1a258388183f94da7f82Jeff Brown     * Sends a generic command to the session. It is up to the session creator
29824fa6c0dd42df057729e1a258388183f94da7f82Jeff Brown     * to decide what commands and parameters they will support. As such,
29924fa6c0dd42df057729e1a258388183f94da7f82Jeff Brown     * commands should only be sent to sessions that the controller owns.
30024fa6c0dd42df057729e1a258388183f94da7f82Jeff Brown     *
30124fa6c0dd42df057729e1a258388183f94da7f82Jeff Brown     * @param command The command to send
30224fa6c0dd42df057729e1a258388183f94da7f82Jeff Brown     * @param params Any parameters to include with the command
30324fa6c0dd42df057729e1a258388183f94da7f82Jeff Brown     * @param cb The callback to receive the result on
30424fa6c0dd42df057729e1a258388183f94da7f82Jeff Brown     */
305b530c89bba371d2d575f10480b2e90914b0d3f3fGabriel Peal    public void sendCommand(String command, Bundle params, ResultReceiver cb) {
30624fa6c0dd42df057729e1a258388183f94da7f82Jeff Brown        if (TextUtils.isEmpty(command)) {
30724fa6c0dd42df057729e1a258388183f94da7f82Jeff Brown            throw new IllegalArgumentException("command cannot be null or empty");
30824fa6c0dd42df057729e1a258388183f94da7f82Jeff Brown        }
309b530c89bba371d2d575f10480b2e90914b0d3f3fGabriel Peal        mImpl.sendCommand(command, params, cb);
31024fa6c0dd42df057729e1a258388183f94da7f82Jeff Brown    }
31124fa6c0dd42df057729e1a258388183f94da7f82Jeff Brown
31224fa6c0dd42df057729e1a258388183f94da7f82Jeff Brown    /**
313aeb95a772d4365008145407ed52dfbaa61d3c4acRoboErik     * Get the session owner's package name.
314aeb95a772d4365008145407ed52dfbaa61d3c4acRoboErik     *
315aeb95a772d4365008145407ed52dfbaa61d3c4acRoboErik     * @return The package name of of the session owner.
316aeb95a772d4365008145407ed52dfbaa61d3c4acRoboErik     */
317aeb95a772d4365008145407ed52dfbaa61d3c4acRoboErik    public String getPackageName() {
318aeb95a772d4365008145407ed52dfbaa61d3c4acRoboErik        return mImpl.getPackageName();
319aeb95a772d4365008145407ed52dfbaa61d3c4acRoboErik    }
320aeb95a772d4365008145407ed52dfbaa61d3c4acRoboErik
321aeb95a772d4365008145407ed52dfbaa61d3c4acRoboErik    /**
322aeb95a772d4365008145407ed52dfbaa61d3c4acRoboErik     * Gets the underlying framework
323aeb95a772d4365008145407ed52dfbaa61d3c4acRoboErik     * {@link android.media.session.MediaController} object.
32424fa6c0dd42df057729e1a258388183f94da7f82Jeff Brown     * <p>
32524fa6c0dd42df057729e1a258388183f94da7f82Jeff Brown     * This method is only supported on API 21+.
32624fa6c0dd42df057729e1a258388183f94da7f82Jeff Brown     * </p>
32724fa6c0dd42df057729e1a258388183f94da7f82Jeff Brown     *
328aeb95a772d4365008145407ed52dfbaa61d3c4acRoboErik     * @return The underlying {@link android.media.session.MediaController}
329aeb95a772d4365008145407ed52dfbaa61d3c4acRoboErik     *         object, or null if none.
33024fa6c0dd42df057729e1a258388183f94da7f82Jeff Brown     */
33124fa6c0dd42df057729e1a258388183f94da7f82Jeff Brown    public Object getMediaController() {
33224fa6c0dd42df057729e1a258388183f94da7f82Jeff Brown        return mImpl.getMediaController();
33324fa6c0dd42df057729e1a258388183f94da7f82Jeff Brown    }
33424fa6c0dd42df057729e1a258388183f94da7f82Jeff Brown
33524fa6c0dd42df057729e1a258388183f94da7f82Jeff Brown    /**
33624fa6c0dd42df057729e1a258388183f94da7f82Jeff Brown     * Callback for receiving updates on from the session. A Callback can be
33716ac83bebda2a19930b9d692789f3b507c49951bRoboErik     * registered using {@link #registerCallback}
33824fa6c0dd42df057729e1a258388183f94da7f82Jeff Brown     */
339e49860b0f76d8336c1d41831ed370b0ff94278efRoboErik    public static abstract class Callback implements IBinder.DeathRecipient {
340e49860b0f76d8336c1d41831ed370b0ff94278efRoboErik        private final Object mCallbackObj;
341e49860b0f76d8336c1d41831ed370b0ff94278efRoboErik        private MessageHandler mHandler;
342e49860b0f76d8336c1d41831ed370b0ff94278efRoboErik
343e49860b0f76d8336c1d41831ed370b0ff94278efRoboErik        private boolean mRegistered = false;
34424fa6c0dd42df057729e1a258388183f94da7f82Jeff Brown
34524fa6c0dd42df057729e1a258388183f94da7f82Jeff Brown        public Callback() {
34624fa6c0dd42df057729e1a258388183f94da7f82Jeff Brown            if (android.os.Build.VERSION.SDK_INT >= 21) {
34724fa6c0dd42df057729e1a258388183f94da7f82Jeff Brown                mCallbackObj = MediaControllerCompatApi21.createCallback(new StubApi21());
34824fa6c0dd42df057729e1a258388183f94da7f82Jeff Brown            } else {
349e49860b0f76d8336c1d41831ed370b0ff94278efRoboErik                mCallbackObj = new StubCompat();
35024fa6c0dd42df057729e1a258388183f94da7f82Jeff Brown            }
35124fa6c0dd42df057729e1a258388183f94da7f82Jeff Brown        }
35224fa6c0dd42df057729e1a258388183f94da7f82Jeff Brown
35324fa6c0dd42df057729e1a258388183f94da7f82Jeff Brown        /**
35423138c4b9be07abdab0cfdde2c62186359c9e7faRoboErik         * Override to handle the session being destroyed. The session is no
35523138c4b9be07abdab0cfdde2c62186359c9e7faRoboErik         * longer valid after this call and calls to it will be ignored.
35623138c4b9be07abdab0cfdde2c62186359c9e7faRoboErik         */
35723138c4b9be07abdab0cfdde2c62186359c9e7faRoboErik        public void onSessionDestroyed() {
35823138c4b9be07abdab0cfdde2c62186359c9e7faRoboErik        }
35923138c4b9be07abdab0cfdde2c62186359c9e7faRoboErik
36023138c4b9be07abdab0cfdde2c62186359c9e7faRoboErik        /**
36124fa6c0dd42df057729e1a258388183f94da7f82Jeff Brown         * Override to handle custom events sent by the session owner without a
36224fa6c0dd42df057729e1a258388183f94da7f82Jeff Brown         * specified interface. Controllers should only handle these for
36324fa6c0dd42df057729e1a258388183f94da7f82Jeff Brown         * sessions they own.
36424fa6c0dd42df057729e1a258388183f94da7f82Jeff Brown         *
36524fa6c0dd42df057729e1a258388183f94da7f82Jeff Brown         * @param event The event from the session.
36624fa6c0dd42df057729e1a258388183f94da7f82Jeff Brown         * @param extras Optional parameters for the event.
36724fa6c0dd42df057729e1a258388183f94da7f82Jeff Brown         */
36824fa6c0dd42df057729e1a258388183f94da7f82Jeff Brown        public void onSessionEvent(String event, Bundle extras) {
36924fa6c0dd42df057729e1a258388183f94da7f82Jeff Brown        }
37024fa6c0dd42df057729e1a258388183f94da7f82Jeff Brown
37124fa6c0dd42df057729e1a258388183f94da7f82Jeff Brown        /**
37224fa6c0dd42df057729e1a258388183f94da7f82Jeff Brown         * Override to handle changes in playback state.
37324fa6c0dd42df057729e1a258388183f94da7f82Jeff Brown         *
37424fa6c0dd42df057729e1a258388183f94da7f82Jeff Brown         * @param state The new playback state of the session
37524fa6c0dd42df057729e1a258388183f94da7f82Jeff Brown         */
37624fa6c0dd42df057729e1a258388183f94da7f82Jeff Brown        public void onPlaybackStateChanged(PlaybackStateCompat state) {
37724fa6c0dd42df057729e1a258388183f94da7f82Jeff Brown        }
37824fa6c0dd42df057729e1a258388183f94da7f82Jeff Brown
37924fa6c0dd42df057729e1a258388183f94da7f82Jeff Brown        /**
38024fa6c0dd42df057729e1a258388183f94da7f82Jeff Brown         * Override to handle changes to the current metadata.
38124fa6c0dd42df057729e1a258388183f94da7f82Jeff Brown         *
38224fa6c0dd42df057729e1a258388183f94da7f82Jeff Brown         * @param metadata The current metadata for the session or null if none.
38324fa6c0dd42df057729e1a258388183f94da7f82Jeff Brown         * @see MediaMetadata
38424fa6c0dd42df057729e1a258388183f94da7f82Jeff Brown         */
38524fa6c0dd42df057729e1a258388183f94da7f82Jeff Brown        public void onMetadataChanged(MediaMetadataCompat metadata) {
38624fa6c0dd42df057729e1a258388183f94da7f82Jeff Brown        }
38724fa6c0dd42df057729e1a258388183f94da7f82Jeff Brown
388aeb95a772d4365008145407ed52dfbaa61d3c4acRoboErik        /**
389aeb95a772d4365008145407ed52dfbaa61d3c4acRoboErik         * Override to handle changes to items in the queue.
390aeb95a772d4365008145407ed52dfbaa61d3c4acRoboErik         *
391aeb95a772d4365008145407ed52dfbaa61d3c4acRoboErik         * @see MediaSessionCompat.QueueItem
392aeb95a772d4365008145407ed52dfbaa61d3c4acRoboErik         * @param queue A list of items in the current play queue. It should
393aeb95a772d4365008145407ed52dfbaa61d3c4acRoboErik         *            include the currently playing item as well as previous and
394aeb95a772d4365008145407ed52dfbaa61d3c4acRoboErik         *            upcoming items if applicable.
395aeb95a772d4365008145407ed52dfbaa61d3c4acRoboErik         */
396aeb95a772d4365008145407ed52dfbaa61d3c4acRoboErik        public void onQueueChanged(List<MediaSessionCompat.QueueItem> queue) {
397aeb95a772d4365008145407ed52dfbaa61d3c4acRoboErik        }
398aeb95a772d4365008145407ed52dfbaa61d3c4acRoboErik
399aeb95a772d4365008145407ed52dfbaa61d3c4acRoboErik        /**
400aeb95a772d4365008145407ed52dfbaa61d3c4acRoboErik         * Override to handle changes to the queue title.
401aeb95a772d4365008145407ed52dfbaa61d3c4acRoboErik         *
402aeb95a772d4365008145407ed52dfbaa61d3c4acRoboErik         * @param title The title that should be displayed along with the play
403aeb95a772d4365008145407ed52dfbaa61d3c4acRoboErik         *            queue such as "Now Playing". May be null if there is no
404aeb95a772d4365008145407ed52dfbaa61d3c4acRoboErik         *            such title.
405aeb95a772d4365008145407ed52dfbaa61d3c4acRoboErik         */
406aeb95a772d4365008145407ed52dfbaa61d3c4acRoboErik        public void onQueueTitleChanged(CharSequence title) {
407aeb95a772d4365008145407ed52dfbaa61d3c4acRoboErik        }
408aeb95a772d4365008145407ed52dfbaa61d3c4acRoboErik
409aeb95a772d4365008145407ed52dfbaa61d3c4acRoboErik        /**
410aeb95a772d4365008145407ed52dfbaa61d3c4acRoboErik         * Override to handle chagnes to the {@link MediaSessionCompat} extras.
411aeb95a772d4365008145407ed52dfbaa61d3c4acRoboErik         *
412aeb95a772d4365008145407ed52dfbaa61d3c4acRoboErik         * @param extras The extras that can include other information
413aeb95a772d4365008145407ed52dfbaa61d3c4acRoboErik         *            associated with the {@link MediaSessionCompat}.
414aeb95a772d4365008145407ed52dfbaa61d3c4acRoboErik         */
415aeb95a772d4365008145407ed52dfbaa61d3c4acRoboErik        public void onExtrasChanged(Bundle extras) {
416aeb95a772d4365008145407ed52dfbaa61d3c4acRoboErik        }
417aeb95a772d4365008145407ed52dfbaa61d3c4acRoboErik
418aeb95a772d4365008145407ed52dfbaa61d3c4acRoboErik        /**
419aeb95a772d4365008145407ed52dfbaa61d3c4acRoboErik         * Override to handle changes to the audio info.
420aeb95a772d4365008145407ed52dfbaa61d3c4acRoboErik         *
421aeb95a772d4365008145407ed52dfbaa61d3c4acRoboErik         * @param info The current audio info for this session.
422aeb95a772d4365008145407ed52dfbaa61d3c4acRoboErik         */
423aeb95a772d4365008145407ed52dfbaa61d3c4acRoboErik        public void onAudioInfoChanged(PlaybackInfo info) {
424aeb95a772d4365008145407ed52dfbaa61d3c4acRoboErik        }
425aeb95a772d4365008145407ed52dfbaa61d3c4acRoboErik
426e49860b0f76d8336c1d41831ed370b0ff94278efRoboErik        @Override
427e49860b0f76d8336c1d41831ed370b0ff94278efRoboErik        public void binderDied() {
428e49860b0f76d8336c1d41831ed370b0ff94278efRoboErik            onSessionDestroyed();
429e49860b0f76d8336c1d41831ed370b0ff94278efRoboErik        }
430e49860b0f76d8336c1d41831ed370b0ff94278efRoboErik
431e49860b0f76d8336c1d41831ed370b0ff94278efRoboErik        /**
432e49860b0f76d8336c1d41831ed370b0ff94278efRoboErik         * Set the handler to use for pre 21 callbacks.
433e49860b0f76d8336c1d41831ed370b0ff94278efRoboErik         */
434e49860b0f76d8336c1d41831ed370b0ff94278efRoboErik        private void setHandler(Handler handler) {
435e49860b0f76d8336c1d41831ed370b0ff94278efRoboErik            mHandler = new MessageHandler(handler.getLooper());
436e49860b0f76d8336c1d41831ed370b0ff94278efRoboErik        }
437e49860b0f76d8336c1d41831ed370b0ff94278efRoboErik
43824fa6c0dd42df057729e1a258388183f94da7f82Jeff Brown        private class StubApi21 implements MediaControllerCompatApi21.Callback {
43924fa6c0dd42df057729e1a258388183f94da7f82Jeff Brown            @Override
44023138c4b9be07abdab0cfdde2c62186359c9e7faRoboErik            public void onSessionDestroyed() {
44123138c4b9be07abdab0cfdde2c62186359c9e7faRoboErik                Callback.this.onSessionDestroyed();
44223138c4b9be07abdab0cfdde2c62186359c9e7faRoboErik            }
44323138c4b9be07abdab0cfdde2c62186359c9e7faRoboErik
44423138c4b9be07abdab0cfdde2c62186359c9e7faRoboErik            @Override
44524fa6c0dd42df057729e1a258388183f94da7f82Jeff Brown            public void onSessionEvent(String event, Bundle extras) {
44624fa6c0dd42df057729e1a258388183f94da7f82Jeff Brown                Callback.this.onSessionEvent(event, extras);
44724fa6c0dd42df057729e1a258388183f94da7f82Jeff Brown            }
44824fa6c0dd42df057729e1a258388183f94da7f82Jeff Brown
44924fa6c0dd42df057729e1a258388183f94da7f82Jeff Brown            @Override
45024fa6c0dd42df057729e1a258388183f94da7f82Jeff Brown            public void onPlaybackStateChanged(Object stateObj) {
45124fa6c0dd42df057729e1a258388183f94da7f82Jeff Brown                Callback.this.onPlaybackStateChanged(
45224fa6c0dd42df057729e1a258388183f94da7f82Jeff Brown                        PlaybackStateCompat.fromPlaybackState(stateObj));
45324fa6c0dd42df057729e1a258388183f94da7f82Jeff Brown            }
45424fa6c0dd42df057729e1a258388183f94da7f82Jeff Brown
45524fa6c0dd42df057729e1a258388183f94da7f82Jeff Brown            @Override
45624fa6c0dd42df057729e1a258388183f94da7f82Jeff Brown            public void onMetadataChanged(Object metadataObj) {
45724fa6c0dd42df057729e1a258388183f94da7f82Jeff Brown                Callback.this.onMetadataChanged(
45824fa6c0dd42df057729e1a258388183f94da7f82Jeff Brown                        MediaMetadataCompat.fromMediaMetadata(metadataObj));
45924fa6c0dd42df057729e1a258388183f94da7f82Jeff Brown            }
46024fa6c0dd42df057729e1a258388183f94da7f82Jeff Brown        }
461e49860b0f76d8336c1d41831ed370b0ff94278efRoboErik
462e49860b0f76d8336c1d41831ed370b0ff94278efRoboErik        private class StubCompat extends IMediaControllerCallback.Stub {
463e49860b0f76d8336c1d41831ed370b0ff94278efRoboErik
464e49860b0f76d8336c1d41831ed370b0ff94278efRoboErik            @Override
465e49860b0f76d8336c1d41831ed370b0ff94278efRoboErik            public void onEvent(String event, Bundle extras) throws RemoteException {
466e49860b0f76d8336c1d41831ed370b0ff94278efRoboErik                mHandler.post(MessageHandler.MSG_EVENT, event, extras);
467e49860b0f76d8336c1d41831ed370b0ff94278efRoboErik            }
468e49860b0f76d8336c1d41831ed370b0ff94278efRoboErik
469e49860b0f76d8336c1d41831ed370b0ff94278efRoboErik            @Override
470e49860b0f76d8336c1d41831ed370b0ff94278efRoboErik            public void onSessionDestroyed() throws RemoteException {
471e49860b0f76d8336c1d41831ed370b0ff94278efRoboErik                mHandler.post(MessageHandler.MSG_DESTROYED, null, null);
472e49860b0f76d8336c1d41831ed370b0ff94278efRoboErik            }
473e49860b0f76d8336c1d41831ed370b0ff94278efRoboErik
474e49860b0f76d8336c1d41831ed370b0ff94278efRoboErik            @Override
475e49860b0f76d8336c1d41831ed370b0ff94278efRoboErik            public void onPlaybackStateChanged(PlaybackStateCompat state) throws RemoteException {
476e49860b0f76d8336c1d41831ed370b0ff94278efRoboErik                mHandler.post(MessageHandler.MSG_UPDATE_PLAYBACK_STATE, state, null);
477e49860b0f76d8336c1d41831ed370b0ff94278efRoboErik            }
478e49860b0f76d8336c1d41831ed370b0ff94278efRoboErik
479e49860b0f76d8336c1d41831ed370b0ff94278efRoboErik            @Override
480e49860b0f76d8336c1d41831ed370b0ff94278efRoboErik            public void onMetadataChanged(MediaMetadataCompat metadata) throws RemoteException {
481e49860b0f76d8336c1d41831ed370b0ff94278efRoboErik                mHandler.post(MessageHandler.MSG_UPDATE_METADATA, metadata, null);
482e49860b0f76d8336c1d41831ed370b0ff94278efRoboErik            }
483e49860b0f76d8336c1d41831ed370b0ff94278efRoboErik
484e49860b0f76d8336c1d41831ed370b0ff94278efRoboErik            @Override
485e49860b0f76d8336c1d41831ed370b0ff94278efRoboErik            public void onQueueChanged(List<QueueItem> queue) throws RemoteException {
486e49860b0f76d8336c1d41831ed370b0ff94278efRoboErik                mHandler.post(MessageHandler.MSG_UPDATE_QUEUE, queue, null);
487e49860b0f76d8336c1d41831ed370b0ff94278efRoboErik            }
488e49860b0f76d8336c1d41831ed370b0ff94278efRoboErik
489e49860b0f76d8336c1d41831ed370b0ff94278efRoboErik            @Override
490e49860b0f76d8336c1d41831ed370b0ff94278efRoboErik            public void onQueueTitleChanged(CharSequence title) throws RemoteException {
491e49860b0f76d8336c1d41831ed370b0ff94278efRoboErik                mHandler.post(MessageHandler.MSG_UPDATE_QUEUE_TITLE, title, null);
492e49860b0f76d8336c1d41831ed370b0ff94278efRoboErik            }
493e49860b0f76d8336c1d41831ed370b0ff94278efRoboErik
494e49860b0f76d8336c1d41831ed370b0ff94278efRoboErik            @Override
495e49860b0f76d8336c1d41831ed370b0ff94278efRoboErik            public void onExtrasChanged(Bundle extras) throws RemoteException {
496e49860b0f76d8336c1d41831ed370b0ff94278efRoboErik                mHandler.post(MessageHandler.MSG_UPDATE_EXTRAS, extras, null);
497e49860b0f76d8336c1d41831ed370b0ff94278efRoboErik            }
498e49860b0f76d8336c1d41831ed370b0ff94278efRoboErik
499e49860b0f76d8336c1d41831ed370b0ff94278efRoboErik            @Override
500e49860b0f76d8336c1d41831ed370b0ff94278efRoboErik            public void onVolumeInfoChanged(ParcelableVolumeInfo info) throws RemoteException {
501e49860b0f76d8336c1d41831ed370b0ff94278efRoboErik                PlaybackInfo pi = null;
502e49860b0f76d8336c1d41831ed370b0ff94278efRoboErik                if (info != null) {
503e49860b0f76d8336c1d41831ed370b0ff94278efRoboErik                    pi = new PlaybackInfo(info.volumeType, info.audioStream, info.controlType,
504e49860b0f76d8336c1d41831ed370b0ff94278efRoboErik                            info.maxVolume, info.currentVolume);
505e49860b0f76d8336c1d41831ed370b0ff94278efRoboErik                }
506e49860b0f76d8336c1d41831ed370b0ff94278efRoboErik                mHandler.post(MessageHandler.MSG_UPDATE_VOLUME, pi, null);
507e49860b0f76d8336c1d41831ed370b0ff94278efRoboErik            }
508e49860b0f76d8336c1d41831ed370b0ff94278efRoboErik        }
509e49860b0f76d8336c1d41831ed370b0ff94278efRoboErik
510e49860b0f76d8336c1d41831ed370b0ff94278efRoboErik        private class MessageHandler extends Handler {
511e49860b0f76d8336c1d41831ed370b0ff94278efRoboErik            private static final int MSG_EVENT = 1;
512e49860b0f76d8336c1d41831ed370b0ff94278efRoboErik            private static final int MSG_UPDATE_PLAYBACK_STATE = 2;
513e49860b0f76d8336c1d41831ed370b0ff94278efRoboErik            private static final int MSG_UPDATE_METADATA = 3;
514e49860b0f76d8336c1d41831ed370b0ff94278efRoboErik            private static final int MSG_UPDATE_VOLUME = 4;
515e49860b0f76d8336c1d41831ed370b0ff94278efRoboErik            private static final int MSG_UPDATE_QUEUE = 5;
516e49860b0f76d8336c1d41831ed370b0ff94278efRoboErik            private static final int MSG_UPDATE_QUEUE_TITLE = 6;
517e49860b0f76d8336c1d41831ed370b0ff94278efRoboErik            private static final int MSG_UPDATE_EXTRAS = 7;
518e49860b0f76d8336c1d41831ed370b0ff94278efRoboErik            private static final int MSG_DESTROYED = 8;
519e49860b0f76d8336c1d41831ed370b0ff94278efRoboErik
520e49860b0f76d8336c1d41831ed370b0ff94278efRoboErik            public MessageHandler(Looper looper) {
521e49860b0f76d8336c1d41831ed370b0ff94278efRoboErik                super(looper);
522e49860b0f76d8336c1d41831ed370b0ff94278efRoboErik            }
523e49860b0f76d8336c1d41831ed370b0ff94278efRoboErik
524e49860b0f76d8336c1d41831ed370b0ff94278efRoboErik            @Override
525e49860b0f76d8336c1d41831ed370b0ff94278efRoboErik            public void handleMessage(Message msg) {
526e49860b0f76d8336c1d41831ed370b0ff94278efRoboErik                if (!mRegistered) {
527e49860b0f76d8336c1d41831ed370b0ff94278efRoboErik                    return;
528e49860b0f76d8336c1d41831ed370b0ff94278efRoboErik                }
529e49860b0f76d8336c1d41831ed370b0ff94278efRoboErik                switch (msg.what) {
530e49860b0f76d8336c1d41831ed370b0ff94278efRoboErik                    case MSG_EVENT:
531e49860b0f76d8336c1d41831ed370b0ff94278efRoboErik                        onSessionEvent((String) msg.obj, msg.getData());
532e49860b0f76d8336c1d41831ed370b0ff94278efRoboErik                        break;
533e49860b0f76d8336c1d41831ed370b0ff94278efRoboErik                    case MSG_UPDATE_PLAYBACK_STATE:
534e49860b0f76d8336c1d41831ed370b0ff94278efRoboErik                        onPlaybackStateChanged((PlaybackStateCompat) msg.obj);
535e49860b0f76d8336c1d41831ed370b0ff94278efRoboErik                        break;
536e49860b0f76d8336c1d41831ed370b0ff94278efRoboErik                    case MSG_UPDATE_METADATA:
537e49860b0f76d8336c1d41831ed370b0ff94278efRoboErik                        onMetadataChanged((MediaMetadataCompat) msg.obj);
538e49860b0f76d8336c1d41831ed370b0ff94278efRoboErik                        break;
539e49860b0f76d8336c1d41831ed370b0ff94278efRoboErik                    case MSG_UPDATE_QUEUE:
540e49860b0f76d8336c1d41831ed370b0ff94278efRoboErik                        onQueueChanged((List<MediaSessionCompat.QueueItem>) msg.obj);
541e49860b0f76d8336c1d41831ed370b0ff94278efRoboErik                        break;
542e49860b0f76d8336c1d41831ed370b0ff94278efRoboErik                    case MSG_UPDATE_QUEUE_TITLE:
543e49860b0f76d8336c1d41831ed370b0ff94278efRoboErik                        onQueueTitleChanged((CharSequence) msg.obj);
544e49860b0f76d8336c1d41831ed370b0ff94278efRoboErik                        break;
545e49860b0f76d8336c1d41831ed370b0ff94278efRoboErik                    case MSG_UPDATE_EXTRAS:
546e49860b0f76d8336c1d41831ed370b0ff94278efRoboErik                        onExtrasChanged((Bundle) msg.obj);
547e49860b0f76d8336c1d41831ed370b0ff94278efRoboErik                        break;
548e49860b0f76d8336c1d41831ed370b0ff94278efRoboErik                    case MSG_UPDATE_VOLUME:
549e49860b0f76d8336c1d41831ed370b0ff94278efRoboErik                        onAudioInfoChanged((PlaybackInfo) msg.obj);
550e49860b0f76d8336c1d41831ed370b0ff94278efRoboErik                        break;
551e49860b0f76d8336c1d41831ed370b0ff94278efRoboErik                    case MSG_DESTROYED:
552e49860b0f76d8336c1d41831ed370b0ff94278efRoboErik                        onSessionDestroyed();
553e49860b0f76d8336c1d41831ed370b0ff94278efRoboErik                        break;
554e49860b0f76d8336c1d41831ed370b0ff94278efRoboErik                }
555e49860b0f76d8336c1d41831ed370b0ff94278efRoboErik            }
556e49860b0f76d8336c1d41831ed370b0ff94278efRoboErik
557e49860b0f76d8336c1d41831ed370b0ff94278efRoboErik            public void post(int what, Object obj, Bundle data) {
558e49860b0f76d8336c1d41831ed370b0ff94278efRoboErik                obtainMessage(what, obj).sendToTarget();
559e49860b0f76d8336c1d41831ed370b0ff94278efRoboErik            }
560e49860b0f76d8336c1d41831ed370b0ff94278efRoboErik        }
56124fa6c0dd42df057729e1a258388183f94da7f82Jeff Brown    }
56224fa6c0dd42df057729e1a258388183f94da7f82Jeff Brown
56324fa6c0dd42df057729e1a258388183f94da7f82Jeff Brown    /**
56424fa6c0dd42df057729e1a258388183f94da7f82Jeff Brown     * Interface for controlling media playback on a session. This allows an app
56524fa6c0dd42df057729e1a258388183f94da7f82Jeff Brown     * to send media transport commands to the session.
56624fa6c0dd42df057729e1a258388183f94da7f82Jeff Brown     */
56724fa6c0dd42df057729e1a258388183f94da7f82Jeff Brown    public static abstract class TransportControls {
56824fa6c0dd42df057729e1a258388183f94da7f82Jeff Brown        TransportControls() {
56924fa6c0dd42df057729e1a258388183f94da7f82Jeff Brown        }
57024fa6c0dd42df057729e1a258388183f94da7f82Jeff Brown
57124fa6c0dd42df057729e1a258388183f94da7f82Jeff Brown        /**
57224fa6c0dd42df057729e1a258388183f94da7f82Jeff Brown         * Request that the player start its playback at its current position.
57324fa6c0dd42df057729e1a258388183f94da7f82Jeff Brown         */
57424fa6c0dd42df057729e1a258388183f94da7f82Jeff Brown        public abstract void play();
57524fa6c0dd42df057729e1a258388183f94da7f82Jeff Brown
57624fa6c0dd42df057729e1a258388183f94da7f82Jeff Brown        /**
577aeb95a772d4365008145407ed52dfbaa61d3c4acRoboErik         * Request that the player start playback for a specific {@link Uri}.
578aeb95a772d4365008145407ed52dfbaa61d3c4acRoboErik         *
579aeb95a772d4365008145407ed52dfbaa61d3c4acRoboErik         * @param mediaId The uri of the requested media.
580aeb95a772d4365008145407ed52dfbaa61d3c4acRoboErik         * @param extras Optional extras that can include extra information
581aeb95a772d4365008145407ed52dfbaa61d3c4acRoboErik         *            about the media item to be played.
582aeb95a772d4365008145407ed52dfbaa61d3c4acRoboErik         */
583aeb95a772d4365008145407ed52dfbaa61d3c4acRoboErik        public abstract void playFromMediaId(String mediaId, Bundle extras);
584aeb95a772d4365008145407ed52dfbaa61d3c4acRoboErik
585aeb95a772d4365008145407ed52dfbaa61d3c4acRoboErik        /**
586aeb95a772d4365008145407ed52dfbaa61d3c4acRoboErik         * Request that the player start playback for a specific search query.
587aeb95a772d4365008145407ed52dfbaa61d3c4acRoboErik         * An empty or null query should be treated as a request to play any
588aeb95a772d4365008145407ed52dfbaa61d3c4acRoboErik         * music.
589aeb95a772d4365008145407ed52dfbaa61d3c4acRoboErik         *
590aeb95a772d4365008145407ed52dfbaa61d3c4acRoboErik         * @param query The search query.
591aeb95a772d4365008145407ed52dfbaa61d3c4acRoboErik         * @param extras Optional extras that can include extra information
592aeb95a772d4365008145407ed52dfbaa61d3c4acRoboErik         *            about the query.
593aeb95a772d4365008145407ed52dfbaa61d3c4acRoboErik         */
594aeb95a772d4365008145407ed52dfbaa61d3c4acRoboErik        public abstract void playFromSearch(String query, Bundle extras);
595aeb95a772d4365008145407ed52dfbaa61d3c4acRoboErik
596aeb95a772d4365008145407ed52dfbaa61d3c4acRoboErik        /**
597b51f456b92aeb62d5aa9d67e1fb2725b2035fdddIan Lake         * Request that the player start playback for a specific {@link Uri}.
598229e04bbc3590e8371b5f4b0d46300adaea7f1d5Dongwon Kang         * <p>
599229e04bbc3590e8371b5f4b0d46300adaea7f1d5Dongwon Kang         * This method is not supported on API 21-22.
600229e04bbc3590e8371b5f4b0d46300adaea7f1d5Dongwon Kang         * </p>
601b51f456b92aeb62d5aa9d67e1fb2725b2035fdddIan Lake         *
602b51f456b92aeb62d5aa9d67e1fb2725b2035fdddIan Lake         * @param uri  The URI of the requested media.
603b51f456b92aeb62d5aa9d67e1fb2725b2035fdddIan Lake         * @param extras Optional extras that can include extra information about the media item
604b51f456b92aeb62d5aa9d67e1fb2725b2035fdddIan Lake         *               to be played.
605b51f456b92aeb62d5aa9d67e1fb2725b2035fdddIan Lake         */
606b51f456b92aeb62d5aa9d67e1fb2725b2035fdddIan Lake        public abstract void playFromUri(Uri uri, Bundle extras);
607b51f456b92aeb62d5aa9d67e1fb2725b2035fdddIan Lake
608b51f456b92aeb62d5aa9d67e1fb2725b2035fdddIan Lake        /**
609aeb95a772d4365008145407ed52dfbaa61d3c4acRoboErik         * Play an item with a specific id in the play queue. If you specify an
610aeb95a772d4365008145407ed52dfbaa61d3c4acRoboErik         * id that is not in the play queue, the behavior is undefined.
611aeb95a772d4365008145407ed52dfbaa61d3c4acRoboErik         */
612aeb95a772d4365008145407ed52dfbaa61d3c4acRoboErik        public abstract void skipToQueueItem(long id);
613aeb95a772d4365008145407ed52dfbaa61d3c4acRoboErik
614aeb95a772d4365008145407ed52dfbaa61d3c4acRoboErik        /**
61524fa6c0dd42df057729e1a258388183f94da7f82Jeff Brown         * Request that the player pause its playback and stay at its current
61624fa6c0dd42df057729e1a258388183f94da7f82Jeff Brown         * position.
61724fa6c0dd42df057729e1a258388183f94da7f82Jeff Brown         */
61824fa6c0dd42df057729e1a258388183f94da7f82Jeff Brown        public abstract void pause();
61924fa6c0dd42df057729e1a258388183f94da7f82Jeff Brown
62024fa6c0dd42df057729e1a258388183f94da7f82Jeff Brown        /**
62124fa6c0dd42df057729e1a258388183f94da7f82Jeff Brown         * Request that the player stop its playback; it may clear its state in
62224fa6c0dd42df057729e1a258388183f94da7f82Jeff Brown         * whatever way is appropriate.
62324fa6c0dd42df057729e1a258388183f94da7f82Jeff Brown         */
62424fa6c0dd42df057729e1a258388183f94da7f82Jeff Brown        public abstract void stop();
62524fa6c0dd42df057729e1a258388183f94da7f82Jeff Brown
62624fa6c0dd42df057729e1a258388183f94da7f82Jeff Brown        /**
62724fa6c0dd42df057729e1a258388183f94da7f82Jeff Brown         * Move to a new location in the media stream.
62824fa6c0dd42df057729e1a258388183f94da7f82Jeff Brown         *
62924fa6c0dd42df057729e1a258388183f94da7f82Jeff Brown         * @param pos Position to move to, in milliseconds.
63024fa6c0dd42df057729e1a258388183f94da7f82Jeff Brown         */
63124fa6c0dd42df057729e1a258388183f94da7f82Jeff Brown        public abstract void seekTo(long pos);
63224fa6c0dd42df057729e1a258388183f94da7f82Jeff Brown
63324fa6c0dd42df057729e1a258388183f94da7f82Jeff Brown        /**
63424fa6c0dd42df057729e1a258388183f94da7f82Jeff Brown         * Start fast forwarding. If playback is already fast forwarding this
63524fa6c0dd42df057729e1a258388183f94da7f82Jeff Brown         * may increase the rate.
63624fa6c0dd42df057729e1a258388183f94da7f82Jeff Brown         */
63724fa6c0dd42df057729e1a258388183f94da7f82Jeff Brown        public abstract void fastForward();
63824fa6c0dd42df057729e1a258388183f94da7f82Jeff Brown
63924fa6c0dd42df057729e1a258388183f94da7f82Jeff Brown        /**
64024fa6c0dd42df057729e1a258388183f94da7f82Jeff Brown         * Skip to the next item.
64124fa6c0dd42df057729e1a258388183f94da7f82Jeff Brown         */
64224fa6c0dd42df057729e1a258388183f94da7f82Jeff Brown        public abstract void skipToNext();
64324fa6c0dd42df057729e1a258388183f94da7f82Jeff Brown
64424fa6c0dd42df057729e1a258388183f94da7f82Jeff Brown        /**
64524fa6c0dd42df057729e1a258388183f94da7f82Jeff Brown         * Start rewinding. If playback is already rewinding this may increase
64624fa6c0dd42df057729e1a258388183f94da7f82Jeff Brown         * the rate.
64724fa6c0dd42df057729e1a258388183f94da7f82Jeff Brown         */
64824fa6c0dd42df057729e1a258388183f94da7f82Jeff Brown        public abstract void rewind();
64924fa6c0dd42df057729e1a258388183f94da7f82Jeff Brown
65024fa6c0dd42df057729e1a258388183f94da7f82Jeff Brown        /**
65124fa6c0dd42df057729e1a258388183f94da7f82Jeff Brown         * Skip to the previous item.
65224fa6c0dd42df057729e1a258388183f94da7f82Jeff Brown         */
65324fa6c0dd42df057729e1a258388183f94da7f82Jeff Brown        public abstract void skipToPrevious();
65424fa6c0dd42df057729e1a258388183f94da7f82Jeff Brown
65524fa6c0dd42df057729e1a258388183f94da7f82Jeff Brown        /**
65624fa6c0dd42df057729e1a258388183f94da7f82Jeff Brown         * Rate the current content. This will cause the rating to be set for
65724fa6c0dd42df057729e1a258388183f94da7f82Jeff Brown         * the current user. The Rating type must match the type returned by
65824fa6c0dd42df057729e1a258388183f94da7f82Jeff Brown         * {@link #getRatingType()}.
65924fa6c0dd42df057729e1a258388183f94da7f82Jeff Brown         *
66024fa6c0dd42df057729e1a258388183f94da7f82Jeff Brown         * @param rating The rating to set for the current content
66124fa6c0dd42df057729e1a258388183f94da7f82Jeff Brown         */
66224fa6c0dd42df057729e1a258388183f94da7f82Jeff Brown        public abstract void setRating(RatingCompat rating);
663aeb95a772d4365008145407ed52dfbaa61d3c4acRoboErik
664aeb95a772d4365008145407ed52dfbaa61d3c4acRoboErik        /**
665aeb95a772d4365008145407ed52dfbaa61d3c4acRoboErik         * Send a custom action for the {@link MediaSessionCompat} to perform.
666aeb95a772d4365008145407ed52dfbaa61d3c4acRoboErik         *
667aeb95a772d4365008145407ed52dfbaa61d3c4acRoboErik         * @param customAction The action to perform.
668aeb95a772d4365008145407ed52dfbaa61d3c4acRoboErik         * @param args Optional arguments to supply to the
669aeb95a772d4365008145407ed52dfbaa61d3c4acRoboErik         *            {@link MediaSessionCompat} for this custom action.
670aeb95a772d4365008145407ed52dfbaa61d3c4acRoboErik         */
671aeb95a772d4365008145407ed52dfbaa61d3c4acRoboErik        public abstract void sendCustomAction(PlaybackStateCompat.CustomAction customAction,
672aeb95a772d4365008145407ed52dfbaa61d3c4acRoboErik                Bundle args);
673aeb95a772d4365008145407ed52dfbaa61d3c4acRoboErik
674aeb95a772d4365008145407ed52dfbaa61d3c4acRoboErik        /**
675aeb95a772d4365008145407ed52dfbaa61d3c4acRoboErik         * Send the id and args from a custom action for the
676aeb95a772d4365008145407ed52dfbaa61d3c4acRoboErik         * {@link MediaSessionCompat} to perform.
677aeb95a772d4365008145407ed52dfbaa61d3c4acRoboErik         *
678aeb95a772d4365008145407ed52dfbaa61d3c4acRoboErik         * @see #sendCustomAction(PlaybackStateCompat.CustomAction action,
679aeb95a772d4365008145407ed52dfbaa61d3c4acRoboErik         *      Bundle args)
680aeb95a772d4365008145407ed52dfbaa61d3c4acRoboErik         * @param action The action identifier of the
681aeb95a772d4365008145407ed52dfbaa61d3c4acRoboErik         *            {@link PlaybackStateCompat.CustomAction} as specified by
682aeb95a772d4365008145407ed52dfbaa61d3c4acRoboErik         *            the {@link MediaSessionCompat}.
683aeb95a772d4365008145407ed52dfbaa61d3c4acRoboErik         * @param args Optional arguments to supply to the
684aeb95a772d4365008145407ed52dfbaa61d3c4acRoboErik         *            {@link MediaSessionCompat} for this custom action.
685aeb95a772d4365008145407ed52dfbaa61d3c4acRoboErik         */
686aeb95a772d4365008145407ed52dfbaa61d3c4acRoboErik        public abstract void sendCustomAction(String action, Bundle args);
68724fa6c0dd42df057729e1a258388183f94da7f82Jeff Brown    }
68824fa6c0dd42df057729e1a258388183f94da7f82Jeff Brown
68924fa6c0dd42df057729e1a258388183f94da7f82Jeff Brown    /**
69024fa6c0dd42df057729e1a258388183f94da7f82Jeff Brown     * Holds information about the way volume is handled for this session.
69124fa6c0dd42df057729e1a258388183f94da7f82Jeff Brown     */
692312f13dea7b4a9229dff784c6e94b0ec0c722b74RoboErik    public static final class PlaybackInfo {
693312f13dea7b4a9229dff784c6e94b0ec0c722b74RoboErik        /**
694312f13dea7b4a9229dff784c6e94b0ec0c722b74RoboErik         * The session uses local playback.
695312f13dea7b4a9229dff784c6e94b0ec0c722b74RoboErik         */
696312f13dea7b4a9229dff784c6e94b0ec0c722b74RoboErik        public static final int PLAYBACK_TYPE_LOCAL = 1;
697312f13dea7b4a9229dff784c6e94b0ec0c722b74RoboErik        /**
698312f13dea7b4a9229dff784c6e94b0ec0c722b74RoboErik         * The session uses remote playback.
699312f13dea7b4a9229dff784c6e94b0ec0c722b74RoboErik         */
700312f13dea7b4a9229dff784c6e94b0ec0c722b74RoboErik        public static final int PLAYBACK_TYPE_REMOTE = 2;
701312f13dea7b4a9229dff784c6e94b0ec0c722b74RoboErik
702312f13dea7b4a9229dff784c6e94b0ec0c722b74RoboErik        private final int mPlaybackType;
7031435afe32073dee10e721dfb6122ce6a194a6412RoboErik        // TODO update audio stream with AudioAttributes support version
70424fa6c0dd42df057729e1a258388183f94da7f82Jeff Brown        private final int mAudioStream;
70524fa6c0dd42df057729e1a258388183f94da7f82Jeff Brown        private final int mVolumeControl;
70624fa6c0dd42df057729e1a258388183f94da7f82Jeff Brown        private final int mMaxVolume;
70724fa6c0dd42df057729e1a258388183f94da7f82Jeff Brown        private final int mCurrentVolume;
70824fa6c0dd42df057729e1a258388183f94da7f82Jeff Brown
709312f13dea7b4a9229dff784c6e94b0ec0c722b74RoboErik        PlaybackInfo(int type, int stream, int control, int max, int current) {
710312f13dea7b4a9229dff784c6e94b0ec0c722b74RoboErik            mPlaybackType = type;
71124fa6c0dd42df057729e1a258388183f94da7f82Jeff Brown            mAudioStream = stream;
71224fa6c0dd42df057729e1a258388183f94da7f82Jeff Brown            mVolumeControl = control;
71324fa6c0dd42df057729e1a258388183f94da7f82Jeff Brown            mMaxVolume = max;
71424fa6c0dd42df057729e1a258388183f94da7f82Jeff Brown            mCurrentVolume = current;
71524fa6c0dd42df057729e1a258388183f94da7f82Jeff Brown        }
71624fa6c0dd42df057729e1a258388183f94da7f82Jeff Brown
71724fa6c0dd42df057729e1a258388183f94da7f82Jeff Brown        /**
71824fa6c0dd42df057729e1a258388183f94da7f82Jeff Brown         * Get the type of volume handling, either local or remote. One of:
71924fa6c0dd42df057729e1a258388183f94da7f82Jeff Brown         * <ul>
720312f13dea7b4a9229dff784c6e94b0ec0c722b74RoboErik         * <li>{@link PlaybackInfo#PLAYBACK_TYPE_LOCAL}</li>
721312f13dea7b4a9229dff784c6e94b0ec0c722b74RoboErik         * <li>{@link PlaybackInfo#PLAYBACK_TYPE_REMOTE}</li>
72224fa6c0dd42df057729e1a258388183f94da7f82Jeff Brown         * </ul>
72324fa6c0dd42df057729e1a258388183f94da7f82Jeff Brown         *
72424fa6c0dd42df057729e1a258388183f94da7f82Jeff Brown         * @return The type of volume handling this session is using.
72524fa6c0dd42df057729e1a258388183f94da7f82Jeff Brown         */
726312f13dea7b4a9229dff784c6e94b0ec0c722b74RoboErik        public int getPlaybackType() {
727312f13dea7b4a9229dff784c6e94b0ec0c722b74RoboErik            return mPlaybackType;
72824fa6c0dd42df057729e1a258388183f94da7f82Jeff Brown        }
72924fa6c0dd42df057729e1a258388183f94da7f82Jeff Brown
73024fa6c0dd42df057729e1a258388183f94da7f82Jeff Brown        /**
73124fa6c0dd42df057729e1a258388183f94da7f82Jeff Brown         * Get the stream this is currently controlling volume on. When the volume
732312f13dea7b4a9229dff784c6e94b0ec0c722b74RoboErik         * type is {@link PlaybackInfo#PLAYBACK_TYPE_REMOTE} this value does not
73324fa6c0dd42df057729e1a258388183f94da7f82Jeff Brown         * have meaning and should be ignored.
73424fa6c0dd42df057729e1a258388183f94da7f82Jeff Brown         *
73524fa6c0dd42df057729e1a258388183f94da7f82Jeff Brown         * @return The stream this session is playing on.
73624fa6c0dd42df057729e1a258388183f94da7f82Jeff Brown         */
73724fa6c0dd42df057729e1a258388183f94da7f82Jeff Brown        public int getAudioStream() {
738aeb95a772d4365008145407ed52dfbaa61d3c4acRoboErik            // TODO switch to AudioAttributesCompat when it is added.
73924fa6c0dd42df057729e1a258388183f94da7f82Jeff Brown            return mAudioStream;
74024fa6c0dd42df057729e1a258388183f94da7f82Jeff Brown        }
74124fa6c0dd42df057729e1a258388183f94da7f82Jeff Brown
74224fa6c0dd42df057729e1a258388183f94da7f82Jeff Brown        /**
74324fa6c0dd42df057729e1a258388183f94da7f82Jeff Brown         * Get the type of volume control that can be used. One of:
74424fa6c0dd42df057729e1a258388183f94da7f82Jeff Brown         * <ul>
74524fa6c0dd42df057729e1a258388183f94da7f82Jeff Brown         * <li>{@link VolumeProviderCompat#VOLUME_CONTROL_ABSOLUTE}</li>
74624fa6c0dd42df057729e1a258388183f94da7f82Jeff Brown         * <li>{@link VolumeProviderCompat#VOLUME_CONTROL_RELATIVE}</li>
74724fa6c0dd42df057729e1a258388183f94da7f82Jeff Brown         * <li>{@link VolumeProviderCompat#VOLUME_CONTROL_FIXED}</li>
74824fa6c0dd42df057729e1a258388183f94da7f82Jeff Brown         * </ul>
74924fa6c0dd42df057729e1a258388183f94da7f82Jeff Brown         *
75024fa6c0dd42df057729e1a258388183f94da7f82Jeff Brown         * @return The type of volume control that may be used with this
75124fa6c0dd42df057729e1a258388183f94da7f82Jeff Brown         *         session.
75224fa6c0dd42df057729e1a258388183f94da7f82Jeff Brown         */
75324fa6c0dd42df057729e1a258388183f94da7f82Jeff Brown        public int getVolumeControl() {
75424fa6c0dd42df057729e1a258388183f94da7f82Jeff Brown            return mVolumeControl;
75524fa6c0dd42df057729e1a258388183f94da7f82Jeff Brown        }
75624fa6c0dd42df057729e1a258388183f94da7f82Jeff Brown
75724fa6c0dd42df057729e1a258388183f94da7f82Jeff Brown        /**
75824fa6c0dd42df057729e1a258388183f94da7f82Jeff Brown         * Get the maximum volume that may be set for this session.
75924fa6c0dd42df057729e1a258388183f94da7f82Jeff Brown         *
76024fa6c0dd42df057729e1a258388183f94da7f82Jeff Brown         * @return The maximum allowed volume where this session is playing.
76124fa6c0dd42df057729e1a258388183f94da7f82Jeff Brown         */
76224fa6c0dd42df057729e1a258388183f94da7f82Jeff Brown        public int getMaxVolume() {
76324fa6c0dd42df057729e1a258388183f94da7f82Jeff Brown            return mMaxVolume;
76424fa6c0dd42df057729e1a258388183f94da7f82Jeff Brown        }
76524fa6c0dd42df057729e1a258388183f94da7f82Jeff Brown
76624fa6c0dd42df057729e1a258388183f94da7f82Jeff Brown        /**
76724fa6c0dd42df057729e1a258388183f94da7f82Jeff Brown         * Get the current volume for this session.
76824fa6c0dd42df057729e1a258388183f94da7f82Jeff Brown         *
76924fa6c0dd42df057729e1a258388183f94da7f82Jeff Brown         * @return The current volume where this session is playing.
77024fa6c0dd42df057729e1a258388183f94da7f82Jeff Brown         */
77124fa6c0dd42df057729e1a258388183f94da7f82Jeff Brown        public int getCurrentVolume() {
77224fa6c0dd42df057729e1a258388183f94da7f82Jeff Brown            return mCurrentVolume;
77324fa6c0dd42df057729e1a258388183f94da7f82Jeff Brown        }
77424fa6c0dd42df057729e1a258388183f94da7f82Jeff Brown    }
77524fa6c0dd42df057729e1a258388183f94da7f82Jeff Brown
77624fa6c0dd42df057729e1a258388183f94da7f82Jeff Brown    interface MediaControllerImpl {
77716ac83bebda2a19930b9d692789f3b507c49951bRoboErik        void registerCallback(Callback callback, Handler handler);
77816ac83bebda2a19930b9d692789f3b507c49951bRoboErik
77916ac83bebda2a19930b9d692789f3b507c49951bRoboErik        void unregisterCallback(Callback callback);
78024fa6c0dd42df057729e1a258388183f94da7f82Jeff Brown        boolean dispatchMediaButtonEvent(KeyEvent keyEvent);
78124fa6c0dd42df057729e1a258388183f94da7f82Jeff Brown        TransportControls getTransportControls();
78224fa6c0dd42df057729e1a258388183f94da7f82Jeff Brown        PlaybackStateCompat getPlaybackState();
78324fa6c0dd42df057729e1a258388183f94da7f82Jeff Brown        MediaMetadataCompat getMetadata();
784aeb95a772d4365008145407ed52dfbaa61d3c4acRoboErik
785aeb95a772d4365008145407ed52dfbaa61d3c4acRoboErik        List<MediaSessionCompat.QueueItem> getQueue();
786aeb95a772d4365008145407ed52dfbaa61d3c4acRoboErik        CharSequence getQueueTitle();
787aeb95a772d4365008145407ed52dfbaa61d3c4acRoboErik        Bundle getExtras();
78824fa6c0dd42df057729e1a258388183f94da7f82Jeff Brown        int getRatingType();
789aeb95a772d4365008145407ed52dfbaa61d3c4acRoboErik        long getFlags();
790312f13dea7b4a9229dff784c6e94b0ec0c722b74RoboErik        PlaybackInfo getPlaybackInfo();
791aeb95a772d4365008145407ed52dfbaa61d3c4acRoboErik        PendingIntent getSessionActivity();
792aeb95a772d4365008145407ed52dfbaa61d3c4acRoboErik
793aeb95a772d4365008145407ed52dfbaa61d3c4acRoboErik        void setVolumeTo(int value, int flags);
794aeb95a772d4365008145407ed52dfbaa61d3c4acRoboErik        void adjustVolume(int direction, int flags);
795b530c89bba371d2d575f10480b2e90914b0d3f3fGabriel Peal        void sendCommand(String command, Bundle params, ResultReceiver cb);
796aeb95a772d4365008145407ed52dfbaa61d3c4acRoboErik
797aeb95a772d4365008145407ed52dfbaa61d3c4acRoboErik        String getPackageName();
79824fa6c0dd42df057729e1a258388183f94da7f82Jeff Brown        Object getMediaController();
79924fa6c0dd42df057729e1a258388183f94da7f82Jeff Brown    }
80024fa6c0dd42df057729e1a258388183f94da7f82Jeff Brown
80124fa6c0dd42df057729e1a258388183f94da7f82Jeff Brown    static class MediaControllerImplBase implements MediaControllerImpl {
802e49860b0f76d8336c1d41831ed370b0ff94278efRoboErik        private MediaSessionCompat.Token mToken;
803e49860b0f76d8336c1d41831ed370b0ff94278efRoboErik        private IMediaSession mBinder;
804e49860b0f76d8336c1d41831ed370b0ff94278efRoboErik        private TransportControls mTransportControls;
805e49860b0f76d8336c1d41831ed370b0ff94278efRoboErik
806e49860b0f76d8336c1d41831ed370b0ff94278efRoboErik        public MediaControllerImplBase(MediaSessionCompat.Token token) {
807e49860b0f76d8336c1d41831ed370b0ff94278efRoboErik            mToken = token;
808e49860b0f76d8336c1d41831ed370b0ff94278efRoboErik            mBinder = IMediaSession.Stub.asInterface((IBinder) token.getToken());
809e49860b0f76d8336c1d41831ed370b0ff94278efRoboErik        }
810e49860b0f76d8336c1d41831ed370b0ff94278efRoboErik
81124fa6c0dd42df057729e1a258388183f94da7f82Jeff Brown        @Override
81216ac83bebda2a19930b9d692789f3b507c49951bRoboErik        public void registerCallback(Callback callback, Handler handler) {
813e49860b0f76d8336c1d41831ed370b0ff94278efRoboErik            if (callback == null) {
814e49860b0f76d8336c1d41831ed370b0ff94278efRoboErik                throw new IllegalArgumentException("callback may not be null.");
815e49860b0f76d8336c1d41831ed370b0ff94278efRoboErik            }
816e49860b0f76d8336c1d41831ed370b0ff94278efRoboErik            try {
817e49860b0f76d8336c1d41831ed370b0ff94278efRoboErik                mBinder.asBinder().linkToDeath(callback, 0);
818e49860b0f76d8336c1d41831ed370b0ff94278efRoboErik                mBinder.registerCallbackListener((IMediaControllerCallback) callback.mCallbackObj);
819e49860b0f76d8336c1d41831ed370b0ff94278efRoboErik                callback.setHandler(handler);
820e49860b0f76d8336c1d41831ed370b0ff94278efRoboErik                callback.mRegistered = true;
821e49860b0f76d8336c1d41831ed370b0ff94278efRoboErik            } catch (RemoteException e) {
822e49860b0f76d8336c1d41831ed370b0ff94278efRoboErik                Log.e(TAG, "Dead object in registerCallback. " + e);
823e49860b0f76d8336c1d41831ed370b0ff94278efRoboErik                callback.onSessionDestroyed();
824e49860b0f76d8336c1d41831ed370b0ff94278efRoboErik            }
82524fa6c0dd42df057729e1a258388183f94da7f82Jeff Brown        }
82624fa6c0dd42df057729e1a258388183f94da7f82Jeff Brown
82724fa6c0dd42df057729e1a258388183f94da7f82Jeff Brown        @Override
82816ac83bebda2a19930b9d692789f3b507c49951bRoboErik        public void unregisterCallback(Callback callback) {
829e49860b0f76d8336c1d41831ed370b0ff94278efRoboErik            if (callback == null) {
830e49860b0f76d8336c1d41831ed370b0ff94278efRoboErik                throw new IllegalArgumentException("callback may not be null.");
831e49860b0f76d8336c1d41831ed370b0ff94278efRoboErik            }
832e49860b0f76d8336c1d41831ed370b0ff94278efRoboErik            try {
833e49860b0f76d8336c1d41831ed370b0ff94278efRoboErik                mBinder.unregisterCallbackListener(
834e49860b0f76d8336c1d41831ed370b0ff94278efRoboErik                        (IMediaControllerCallback) callback.mCallbackObj);
835e49860b0f76d8336c1d41831ed370b0ff94278efRoboErik                mBinder.asBinder().unlinkToDeath(callback, 0);
836e49860b0f76d8336c1d41831ed370b0ff94278efRoboErik                callback.mRegistered = false;
837e49860b0f76d8336c1d41831ed370b0ff94278efRoboErik            } catch (RemoteException e) {
838e49860b0f76d8336c1d41831ed370b0ff94278efRoboErik                Log.e(TAG, "Dead object in unregisterCallback. " + e);
839e49860b0f76d8336c1d41831ed370b0ff94278efRoboErik            }
84024fa6c0dd42df057729e1a258388183f94da7f82Jeff Brown        }
84124fa6c0dd42df057729e1a258388183f94da7f82Jeff Brown
84224fa6c0dd42df057729e1a258388183f94da7f82Jeff Brown        @Override
84324fa6c0dd42df057729e1a258388183f94da7f82Jeff Brown        public boolean dispatchMediaButtonEvent(KeyEvent event) {
844e49860b0f76d8336c1d41831ed370b0ff94278efRoboErik            if (event == null) {
845e49860b0f76d8336c1d41831ed370b0ff94278efRoboErik                throw new IllegalArgumentException("event may not be null.");
846e49860b0f76d8336c1d41831ed370b0ff94278efRoboErik            }
847e49860b0f76d8336c1d41831ed370b0ff94278efRoboErik            try {
848e49860b0f76d8336c1d41831ed370b0ff94278efRoboErik                mBinder.sendMediaButton(event);
849e49860b0f76d8336c1d41831ed370b0ff94278efRoboErik            } catch (RemoteException e) {
850e49860b0f76d8336c1d41831ed370b0ff94278efRoboErik                Log.e(TAG, "Dead object in dispatchMediaButtonEvent. " + e);
851e49860b0f76d8336c1d41831ed370b0ff94278efRoboErik            }
85224fa6c0dd42df057729e1a258388183f94da7f82Jeff Brown            return false;
85324fa6c0dd42df057729e1a258388183f94da7f82Jeff Brown        }
85424fa6c0dd42df057729e1a258388183f94da7f82Jeff Brown
85524fa6c0dd42df057729e1a258388183f94da7f82Jeff Brown        @Override
85624fa6c0dd42df057729e1a258388183f94da7f82Jeff Brown        public TransportControls getTransportControls() {
857e49860b0f76d8336c1d41831ed370b0ff94278efRoboErik            if (mTransportControls == null) {
858e49860b0f76d8336c1d41831ed370b0ff94278efRoboErik                mTransportControls = new TransportControlsBase(mBinder);
859e49860b0f76d8336c1d41831ed370b0ff94278efRoboErik            }
860e49860b0f76d8336c1d41831ed370b0ff94278efRoboErik
861e49860b0f76d8336c1d41831ed370b0ff94278efRoboErik            return mTransportControls;
86224fa6c0dd42df057729e1a258388183f94da7f82Jeff Brown        }
86324fa6c0dd42df057729e1a258388183f94da7f82Jeff Brown
86424fa6c0dd42df057729e1a258388183f94da7f82Jeff Brown        @Override
86524fa6c0dd42df057729e1a258388183f94da7f82Jeff Brown        public PlaybackStateCompat getPlaybackState() {
866e49860b0f76d8336c1d41831ed370b0ff94278efRoboErik            try {
867e49860b0f76d8336c1d41831ed370b0ff94278efRoboErik                return mBinder.getPlaybackState();
868e49860b0f76d8336c1d41831ed370b0ff94278efRoboErik            } catch (RemoteException e) {
869e49860b0f76d8336c1d41831ed370b0ff94278efRoboErik                Log.e(TAG, "Dead object in getPlaybackState. " + e);
870e49860b0f76d8336c1d41831ed370b0ff94278efRoboErik            }
87124fa6c0dd42df057729e1a258388183f94da7f82Jeff Brown            return null;
87224fa6c0dd42df057729e1a258388183f94da7f82Jeff Brown        }
87324fa6c0dd42df057729e1a258388183f94da7f82Jeff Brown
87424fa6c0dd42df057729e1a258388183f94da7f82Jeff Brown        @Override
87524fa6c0dd42df057729e1a258388183f94da7f82Jeff Brown        public MediaMetadataCompat getMetadata() {
876e49860b0f76d8336c1d41831ed370b0ff94278efRoboErik            try {
877e49860b0f76d8336c1d41831ed370b0ff94278efRoboErik                return mBinder.getMetadata();
878e49860b0f76d8336c1d41831ed370b0ff94278efRoboErik            } catch (RemoteException e) {
879e49860b0f76d8336c1d41831ed370b0ff94278efRoboErik                Log.e(TAG, "Dead object in getMetadata. " + e);
880e49860b0f76d8336c1d41831ed370b0ff94278efRoboErik            }
88124fa6c0dd42df057729e1a258388183f94da7f82Jeff Brown            return null;
88224fa6c0dd42df057729e1a258388183f94da7f82Jeff Brown        }
88324fa6c0dd42df057729e1a258388183f94da7f82Jeff Brown
88424fa6c0dd42df057729e1a258388183f94da7f82Jeff Brown        @Override
885aeb95a772d4365008145407ed52dfbaa61d3c4acRoboErik        public List<MediaSessionCompat.QueueItem> getQueue() {
886e49860b0f76d8336c1d41831ed370b0ff94278efRoboErik            try {
887e49860b0f76d8336c1d41831ed370b0ff94278efRoboErik                return mBinder.getQueue();
888e49860b0f76d8336c1d41831ed370b0ff94278efRoboErik            } catch (RemoteException e) {
889e49860b0f76d8336c1d41831ed370b0ff94278efRoboErik                Log.e(TAG, "Dead object in getQueue. " + e);
890e49860b0f76d8336c1d41831ed370b0ff94278efRoboErik            }
891aeb95a772d4365008145407ed52dfbaa61d3c4acRoboErik            return null;
892aeb95a772d4365008145407ed52dfbaa61d3c4acRoboErik        }
893aeb95a772d4365008145407ed52dfbaa61d3c4acRoboErik
894aeb95a772d4365008145407ed52dfbaa61d3c4acRoboErik        @Override
895aeb95a772d4365008145407ed52dfbaa61d3c4acRoboErik        public CharSequence getQueueTitle() {
896e49860b0f76d8336c1d41831ed370b0ff94278efRoboErik            try {
897e49860b0f76d8336c1d41831ed370b0ff94278efRoboErik                return mBinder.getQueueTitle();
898e49860b0f76d8336c1d41831ed370b0ff94278efRoboErik            } catch (RemoteException e) {
899e49860b0f76d8336c1d41831ed370b0ff94278efRoboErik                Log.e(TAG, "Dead object in getQueueTitle. " + e);
900e49860b0f76d8336c1d41831ed370b0ff94278efRoboErik            }
901aeb95a772d4365008145407ed52dfbaa61d3c4acRoboErik            return null;
902aeb95a772d4365008145407ed52dfbaa61d3c4acRoboErik        }
903aeb95a772d4365008145407ed52dfbaa61d3c4acRoboErik
904aeb95a772d4365008145407ed52dfbaa61d3c4acRoboErik        @Override
905aeb95a772d4365008145407ed52dfbaa61d3c4acRoboErik        public Bundle getExtras() {
906e49860b0f76d8336c1d41831ed370b0ff94278efRoboErik            try {
907e49860b0f76d8336c1d41831ed370b0ff94278efRoboErik                return mBinder.getExtras();
908e49860b0f76d8336c1d41831ed370b0ff94278efRoboErik            } catch (RemoteException e) {
909e49860b0f76d8336c1d41831ed370b0ff94278efRoboErik                Log.e(TAG, "Dead object in getExtras. " + e);
910e49860b0f76d8336c1d41831ed370b0ff94278efRoboErik            }
911aeb95a772d4365008145407ed52dfbaa61d3c4acRoboErik            return null;
912aeb95a772d4365008145407ed52dfbaa61d3c4acRoboErik        }
913aeb95a772d4365008145407ed52dfbaa61d3c4acRoboErik
914aeb95a772d4365008145407ed52dfbaa61d3c4acRoboErik        @Override
91524fa6c0dd42df057729e1a258388183f94da7f82Jeff Brown        public int getRatingType() {
916e49860b0f76d8336c1d41831ed370b0ff94278efRoboErik            try {
917e49860b0f76d8336c1d41831ed370b0ff94278efRoboErik                return mBinder.getRatingType();
918e49860b0f76d8336c1d41831ed370b0ff94278efRoboErik            } catch (RemoteException e) {
919e49860b0f76d8336c1d41831ed370b0ff94278efRoboErik                Log.e(TAG, "Dead object in getRatingType. " + e);
920e49860b0f76d8336c1d41831ed370b0ff94278efRoboErik            }
92124fa6c0dd42df057729e1a258388183f94da7f82Jeff Brown            return 0;
92224fa6c0dd42df057729e1a258388183f94da7f82Jeff Brown        }
92324fa6c0dd42df057729e1a258388183f94da7f82Jeff Brown
92424fa6c0dd42df057729e1a258388183f94da7f82Jeff Brown        @Override
925aeb95a772d4365008145407ed52dfbaa61d3c4acRoboErik        public long getFlags() {
926e49860b0f76d8336c1d41831ed370b0ff94278efRoboErik            try {
927e49860b0f76d8336c1d41831ed370b0ff94278efRoboErik                return mBinder.getFlags();
928e49860b0f76d8336c1d41831ed370b0ff94278efRoboErik            } catch (RemoteException e) {
929e49860b0f76d8336c1d41831ed370b0ff94278efRoboErik                Log.e(TAG, "Dead object in getFlags. " + e);
930e49860b0f76d8336c1d41831ed370b0ff94278efRoboErik            }
931aeb95a772d4365008145407ed52dfbaa61d3c4acRoboErik            return 0;
932aeb95a772d4365008145407ed52dfbaa61d3c4acRoboErik        }
933aeb95a772d4365008145407ed52dfbaa61d3c4acRoboErik
934aeb95a772d4365008145407ed52dfbaa61d3c4acRoboErik        @Override
935312f13dea7b4a9229dff784c6e94b0ec0c722b74RoboErik        public PlaybackInfo getPlaybackInfo() {
936e49860b0f76d8336c1d41831ed370b0ff94278efRoboErik            try {
937e49860b0f76d8336c1d41831ed370b0ff94278efRoboErik                ParcelableVolumeInfo info = mBinder.getVolumeAttributes();
938e49860b0f76d8336c1d41831ed370b0ff94278efRoboErik                PlaybackInfo pi = new PlaybackInfo(info.volumeType, info.audioStream,
939e49860b0f76d8336c1d41831ed370b0ff94278efRoboErik                        info.controlType, info.maxVolume, info.currentVolume);
940e49860b0f76d8336c1d41831ed370b0ff94278efRoboErik                return pi;
941e49860b0f76d8336c1d41831ed370b0ff94278efRoboErik            } catch (RemoteException e) {
942e49860b0f76d8336c1d41831ed370b0ff94278efRoboErik                Log.e(TAG, "Dead object in getPlaybackInfo. " + e);
943e49860b0f76d8336c1d41831ed370b0ff94278efRoboErik            }
94424fa6c0dd42df057729e1a258388183f94da7f82Jeff Brown            return null;
94524fa6c0dd42df057729e1a258388183f94da7f82Jeff Brown        }
94624fa6c0dd42df057729e1a258388183f94da7f82Jeff Brown
94724fa6c0dd42df057729e1a258388183f94da7f82Jeff Brown        @Override
948aeb95a772d4365008145407ed52dfbaa61d3c4acRoboErik        public PendingIntent getSessionActivity() {
949e49860b0f76d8336c1d41831ed370b0ff94278efRoboErik            try {
950e49860b0f76d8336c1d41831ed370b0ff94278efRoboErik                return mBinder.getLaunchPendingIntent();
951e49860b0f76d8336c1d41831ed370b0ff94278efRoboErik            } catch (RemoteException e) {
952e49860b0f76d8336c1d41831ed370b0ff94278efRoboErik                Log.e(TAG, "Dead object in getSessionActivity. " + e);
953e49860b0f76d8336c1d41831ed370b0ff94278efRoboErik            }
954aeb95a772d4365008145407ed52dfbaa61d3c4acRoboErik            return null;
955aeb95a772d4365008145407ed52dfbaa61d3c4acRoboErik        }
956aeb95a772d4365008145407ed52dfbaa61d3c4acRoboErik
957aeb95a772d4365008145407ed52dfbaa61d3c4acRoboErik        @Override
958aeb95a772d4365008145407ed52dfbaa61d3c4acRoboErik        public void setVolumeTo(int value, int flags) {
959e49860b0f76d8336c1d41831ed370b0ff94278efRoboErik            try {
960e49860b0f76d8336c1d41831ed370b0ff94278efRoboErik                mBinder.setVolumeTo(value, flags, null);
961e49860b0f76d8336c1d41831ed370b0ff94278efRoboErik            } catch (RemoteException e) {
962e49860b0f76d8336c1d41831ed370b0ff94278efRoboErik                Log.e(TAG, "Dead object in setVolumeTo. " + e);
963e49860b0f76d8336c1d41831ed370b0ff94278efRoboErik            }
964aeb95a772d4365008145407ed52dfbaa61d3c4acRoboErik        }
965aeb95a772d4365008145407ed52dfbaa61d3c4acRoboErik
966aeb95a772d4365008145407ed52dfbaa61d3c4acRoboErik        @Override
967aeb95a772d4365008145407ed52dfbaa61d3c4acRoboErik        public void adjustVolume(int direction, int flags) {
968e49860b0f76d8336c1d41831ed370b0ff94278efRoboErik            try {
969e49860b0f76d8336c1d41831ed370b0ff94278efRoboErik                mBinder.adjustVolume(direction, flags, null);
970e49860b0f76d8336c1d41831ed370b0ff94278efRoboErik            } catch (RemoteException e) {
971e49860b0f76d8336c1d41831ed370b0ff94278efRoboErik                Log.e(TAG, "Dead object in adjustVolume. " + e);
972e49860b0f76d8336c1d41831ed370b0ff94278efRoboErik            }
973aeb95a772d4365008145407ed52dfbaa61d3c4acRoboErik        }
974aeb95a772d4365008145407ed52dfbaa61d3c4acRoboErik
975aeb95a772d4365008145407ed52dfbaa61d3c4acRoboErik        @Override
976b530c89bba371d2d575f10480b2e90914b0d3f3fGabriel Peal        public void sendCommand(String command, Bundle params, ResultReceiver cb) {
977e49860b0f76d8336c1d41831ed370b0ff94278efRoboErik            try {
978e49860b0f76d8336c1d41831ed370b0ff94278efRoboErik                mBinder.sendCommand(command, params,
979e49860b0f76d8336c1d41831ed370b0ff94278efRoboErik                        new MediaSessionCompat.ResultReceiverWrapper(cb));
980e49860b0f76d8336c1d41831ed370b0ff94278efRoboErik            } catch (RemoteException e) {
981e49860b0f76d8336c1d41831ed370b0ff94278efRoboErik                Log.e(TAG, "Dead object in sendCommand. " + e);
982e49860b0f76d8336c1d41831ed370b0ff94278efRoboErik            }
98324fa6c0dd42df057729e1a258388183f94da7f82Jeff Brown        }
98424fa6c0dd42df057729e1a258388183f94da7f82Jeff Brown
98524fa6c0dd42df057729e1a258388183f94da7f82Jeff Brown        @Override
986aeb95a772d4365008145407ed52dfbaa61d3c4acRoboErik        public String getPackageName() {
987e49860b0f76d8336c1d41831ed370b0ff94278efRoboErik            try {
988e49860b0f76d8336c1d41831ed370b0ff94278efRoboErik                return mBinder.getPackageName();
989e49860b0f76d8336c1d41831ed370b0ff94278efRoboErik            } catch (RemoteException e) {
990e49860b0f76d8336c1d41831ed370b0ff94278efRoboErik                Log.e(TAG, "Dead object in getPackageName. " + e);
991e49860b0f76d8336c1d41831ed370b0ff94278efRoboErik            }
992aeb95a772d4365008145407ed52dfbaa61d3c4acRoboErik            return null;
993aeb95a772d4365008145407ed52dfbaa61d3c4acRoboErik        }
994aeb95a772d4365008145407ed52dfbaa61d3c4acRoboErik
995aeb95a772d4365008145407ed52dfbaa61d3c4acRoboErik        @Override
99624fa6c0dd42df057729e1a258388183f94da7f82Jeff Brown        public Object getMediaController() {
99724fa6c0dd42df057729e1a258388183f94da7f82Jeff Brown            return null;
99824fa6c0dd42df057729e1a258388183f94da7f82Jeff Brown        }
99924fa6c0dd42df057729e1a258388183f94da7f82Jeff Brown    }
100024fa6c0dd42df057729e1a258388183f94da7f82Jeff Brown
1001e49860b0f76d8336c1d41831ed370b0ff94278efRoboErik    static class TransportControlsBase extends TransportControls {
1002e49860b0f76d8336c1d41831ed370b0ff94278efRoboErik        private IMediaSession mBinder;
1003e49860b0f76d8336c1d41831ed370b0ff94278efRoboErik
1004e49860b0f76d8336c1d41831ed370b0ff94278efRoboErik        public TransportControlsBase(IMediaSession binder) {
1005e49860b0f76d8336c1d41831ed370b0ff94278efRoboErik            mBinder = binder;
1006e49860b0f76d8336c1d41831ed370b0ff94278efRoboErik        }
1007e49860b0f76d8336c1d41831ed370b0ff94278efRoboErik
1008e49860b0f76d8336c1d41831ed370b0ff94278efRoboErik        @Override
1009e49860b0f76d8336c1d41831ed370b0ff94278efRoboErik        public void play() {
1010e49860b0f76d8336c1d41831ed370b0ff94278efRoboErik            try {
1011e49860b0f76d8336c1d41831ed370b0ff94278efRoboErik                mBinder.play();
1012e49860b0f76d8336c1d41831ed370b0ff94278efRoboErik            } catch (RemoteException e) {
1013e49860b0f76d8336c1d41831ed370b0ff94278efRoboErik                Log.e(TAG, "Dead object in play. " + e);
1014e49860b0f76d8336c1d41831ed370b0ff94278efRoboErik            }
1015e49860b0f76d8336c1d41831ed370b0ff94278efRoboErik        }
1016e49860b0f76d8336c1d41831ed370b0ff94278efRoboErik
1017e49860b0f76d8336c1d41831ed370b0ff94278efRoboErik        @Override
1018e49860b0f76d8336c1d41831ed370b0ff94278efRoboErik        public void playFromMediaId(String mediaId, Bundle extras) {
1019e49860b0f76d8336c1d41831ed370b0ff94278efRoboErik            try {
1020e49860b0f76d8336c1d41831ed370b0ff94278efRoboErik                mBinder.playFromMediaId(mediaId, extras);
1021e49860b0f76d8336c1d41831ed370b0ff94278efRoboErik            } catch (RemoteException e) {
1022e49860b0f76d8336c1d41831ed370b0ff94278efRoboErik                Log.e(TAG, "Dead object in playFromMediaId. " + e);
1023e49860b0f76d8336c1d41831ed370b0ff94278efRoboErik            }
1024e49860b0f76d8336c1d41831ed370b0ff94278efRoboErik        }
1025e49860b0f76d8336c1d41831ed370b0ff94278efRoboErik
1026e49860b0f76d8336c1d41831ed370b0ff94278efRoboErik        @Override
1027e49860b0f76d8336c1d41831ed370b0ff94278efRoboErik        public void playFromSearch(String query, Bundle extras) {
1028e49860b0f76d8336c1d41831ed370b0ff94278efRoboErik            try {
1029e49860b0f76d8336c1d41831ed370b0ff94278efRoboErik                mBinder.playFromSearch(query, extras);
1030e49860b0f76d8336c1d41831ed370b0ff94278efRoboErik            } catch (RemoteException e) {
1031e49860b0f76d8336c1d41831ed370b0ff94278efRoboErik                Log.e(TAG, "Dead object in playFromSearch. " + e);
1032e49860b0f76d8336c1d41831ed370b0ff94278efRoboErik            }
1033e49860b0f76d8336c1d41831ed370b0ff94278efRoboErik        }
1034e49860b0f76d8336c1d41831ed370b0ff94278efRoboErik
1035e49860b0f76d8336c1d41831ed370b0ff94278efRoboErik        @Override
1036b51f456b92aeb62d5aa9d67e1fb2725b2035fdddIan Lake        public void playFromUri(Uri uri, Bundle extras) {
1037b51f456b92aeb62d5aa9d67e1fb2725b2035fdddIan Lake            try {
1038b51f456b92aeb62d5aa9d67e1fb2725b2035fdddIan Lake                mBinder.playFromUri(uri, extras);
1039b51f456b92aeb62d5aa9d67e1fb2725b2035fdddIan Lake            } catch (RemoteException e) {
1040b51f456b92aeb62d5aa9d67e1fb2725b2035fdddIan Lake                Log.e(TAG, "Dead object in playFromUri. " + e);
1041b51f456b92aeb62d5aa9d67e1fb2725b2035fdddIan Lake            }
1042b51f456b92aeb62d5aa9d67e1fb2725b2035fdddIan Lake        }
1043b51f456b92aeb62d5aa9d67e1fb2725b2035fdddIan Lake
1044b51f456b92aeb62d5aa9d67e1fb2725b2035fdddIan Lake        @Override
1045e49860b0f76d8336c1d41831ed370b0ff94278efRoboErik        public void skipToQueueItem(long id) {
1046e49860b0f76d8336c1d41831ed370b0ff94278efRoboErik            try {
1047e49860b0f76d8336c1d41831ed370b0ff94278efRoboErik                mBinder.skipToQueueItem(id);
1048e49860b0f76d8336c1d41831ed370b0ff94278efRoboErik            } catch (RemoteException e) {
1049e49860b0f76d8336c1d41831ed370b0ff94278efRoboErik                Log.e(TAG, "Dead object in skipToQueueItem. " + e);
1050e49860b0f76d8336c1d41831ed370b0ff94278efRoboErik            }
1051e49860b0f76d8336c1d41831ed370b0ff94278efRoboErik        }
1052e49860b0f76d8336c1d41831ed370b0ff94278efRoboErik
1053e49860b0f76d8336c1d41831ed370b0ff94278efRoboErik        @Override
1054e49860b0f76d8336c1d41831ed370b0ff94278efRoboErik        public void pause() {
1055e49860b0f76d8336c1d41831ed370b0ff94278efRoboErik            try {
1056e49860b0f76d8336c1d41831ed370b0ff94278efRoboErik                mBinder.pause();
1057e49860b0f76d8336c1d41831ed370b0ff94278efRoboErik            } catch (RemoteException e) {
1058e49860b0f76d8336c1d41831ed370b0ff94278efRoboErik                Log.e(TAG, "Dead object in pause. " + e);
1059e49860b0f76d8336c1d41831ed370b0ff94278efRoboErik            }
1060e49860b0f76d8336c1d41831ed370b0ff94278efRoboErik        }
1061e49860b0f76d8336c1d41831ed370b0ff94278efRoboErik
1062e49860b0f76d8336c1d41831ed370b0ff94278efRoboErik        @Override
1063e49860b0f76d8336c1d41831ed370b0ff94278efRoboErik        public void stop() {
1064e49860b0f76d8336c1d41831ed370b0ff94278efRoboErik            try {
1065e49860b0f76d8336c1d41831ed370b0ff94278efRoboErik                mBinder.stop();
1066e49860b0f76d8336c1d41831ed370b0ff94278efRoboErik            } catch (RemoteException e) {
1067e49860b0f76d8336c1d41831ed370b0ff94278efRoboErik                Log.e(TAG, "Dead object in stop. " + e);
1068e49860b0f76d8336c1d41831ed370b0ff94278efRoboErik            }
1069e49860b0f76d8336c1d41831ed370b0ff94278efRoboErik        }
1070e49860b0f76d8336c1d41831ed370b0ff94278efRoboErik
1071e49860b0f76d8336c1d41831ed370b0ff94278efRoboErik        @Override
1072e49860b0f76d8336c1d41831ed370b0ff94278efRoboErik        public void seekTo(long pos) {
1073e49860b0f76d8336c1d41831ed370b0ff94278efRoboErik            try {
1074e49860b0f76d8336c1d41831ed370b0ff94278efRoboErik                mBinder.seekTo(pos);
1075e49860b0f76d8336c1d41831ed370b0ff94278efRoboErik            } catch (RemoteException e) {
1076e49860b0f76d8336c1d41831ed370b0ff94278efRoboErik                Log.e(TAG, "Dead object in seekTo. " + e);
1077e49860b0f76d8336c1d41831ed370b0ff94278efRoboErik            }
1078e49860b0f76d8336c1d41831ed370b0ff94278efRoboErik        }
1079e49860b0f76d8336c1d41831ed370b0ff94278efRoboErik
1080e49860b0f76d8336c1d41831ed370b0ff94278efRoboErik        @Override
1081e49860b0f76d8336c1d41831ed370b0ff94278efRoboErik        public void fastForward() {
1082e49860b0f76d8336c1d41831ed370b0ff94278efRoboErik            try {
1083e49860b0f76d8336c1d41831ed370b0ff94278efRoboErik                mBinder.fastForward();
1084e49860b0f76d8336c1d41831ed370b0ff94278efRoboErik            } catch (RemoteException e) {
1085e49860b0f76d8336c1d41831ed370b0ff94278efRoboErik                Log.e(TAG, "Dead object in fastForward. " + e);
1086e49860b0f76d8336c1d41831ed370b0ff94278efRoboErik            }
1087e49860b0f76d8336c1d41831ed370b0ff94278efRoboErik        }
1088e49860b0f76d8336c1d41831ed370b0ff94278efRoboErik
1089e49860b0f76d8336c1d41831ed370b0ff94278efRoboErik        @Override
1090e49860b0f76d8336c1d41831ed370b0ff94278efRoboErik        public void skipToNext() {
1091e49860b0f76d8336c1d41831ed370b0ff94278efRoboErik            try {
1092e49860b0f76d8336c1d41831ed370b0ff94278efRoboErik                mBinder.next();
1093e49860b0f76d8336c1d41831ed370b0ff94278efRoboErik            } catch (RemoteException e) {
1094e49860b0f76d8336c1d41831ed370b0ff94278efRoboErik                Log.e(TAG, "Dead object in skipToNext. " + e);
1095e49860b0f76d8336c1d41831ed370b0ff94278efRoboErik            }
1096e49860b0f76d8336c1d41831ed370b0ff94278efRoboErik        }
1097e49860b0f76d8336c1d41831ed370b0ff94278efRoboErik
1098e49860b0f76d8336c1d41831ed370b0ff94278efRoboErik        @Override
1099e49860b0f76d8336c1d41831ed370b0ff94278efRoboErik        public void rewind() {
1100e49860b0f76d8336c1d41831ed370b0ff94278efRoboErik            try {
1101e49860b0f76d8336c1d41831ed370b0ff94278efRoboErik                mBinder.rewind();
1102e49860b0f76d8336c1d41831ed370b0ff94278efRoboErik            } catch (RemoteException e) {
1103e49860b0f76d8336c1d41831ed370b0ff94278efRoboErik                Log.e(TAG, "Dead object in rewind. " + e);
1104e49860b0f76d8336c1d41831ed370b0ff94278efRoboErik            }
1105e49860b0f76d8336c1d41831ed370b0ff94278efRoboErik        }
1106e49860b0f76d8336c1d41831ed370b0ff94278efRoboErik
1107e49860b0f76d8336c1d41831ed370b0ff94278efRoboErik        @Override
1108e49860b0f76d8336c1d41831ed370b0ff94278efRoboErik        public void skipToPrevious() {
1109e49860b0f76d8336c1d41831ed370b0ff94278efRoboErik            try {
1110e49860b0f76d8336c1d41831ed370b0ff94278efRoboErik                mBinder.previous();
1111e49860b0f76d8336c1d41831ed370b0ff94278efRoboErik            } catch (RemoteException e) {
1112e49860b0f76d8336c1d41831ed370b0ff94278efRoboErik                Log.e(TAG, "Dead object in skipToPrevious. " + e);
1113e49860b0f76d8336c1d41831ed370b0ff94278efRoboErik            }
1114e49860b0f76d8336c1d41831ed370b0ff94278efRoboErik        }
1115e49860b0f76d8336c1d41831ed370b0ff94278efRoboErik
1116e49860b0f76d8336c1d41831ed370b0ff94278efRoboErik        @Override
1117e49860b0f76d8336c1d41831ed370b0ff94278efRoboErik        public void setRating(RatingCompat rating) {
1118e49860b0f76d8336c1d41831ed370b0ff94278efRoboErik            try {
1119e49860b0f76d8336c1d41831ed370b0ff94278efRoboErik                mBinder.rate(rating);
1120e49860b0f76d8336c1d41831ed370b0ff94278efRoboErik            } catch (RemoteException e) {
1121e49860b0f76d8336c1d41831ed370b0ff94278efRoboErik                Log.e(TAG, "Dead object in setRating. " + e);
1122e49860b0f76d8336c1d41831ed370b0ff94278efRoboErik            }
1123e49860b0f76d8336c1d41831ed370b0ff94278efRoboErik        }
1124e49860b0f76d8336c1d41831ed370b0ff94278efRoboErik
1125e49860b0f76d8336c1d41831ed370b0ff94278efRoboErik        @Override
1126e49860b0f76d8336c1d41831ed370b0ff94278efRoboErik        public void sendCustomAction(CustomAction customAction, Bundle args) {
1127e49860b0f76d8336c1d41831ed370b0ff94278efRoboErik            sendCustomAction(customAction.getAction(), args);
1128e49860b0f76d8336c1d41831ed370b0ff94278efRoboErik        }
1129e49860b0f76d8336c1d41831ed370b0ff94278efRoboErik
1130e49860b0f76d8336c1d41831ed370b0ff94278efRoboErik        @Override
1131e49860b0f76d8336c1d41831ed370b0ff94278efRoboErik        public void sendCustomAction(String action, Bundle args) {
1132e49860b0f76d8336c1d41831ed370b0ff94278efRoboErik            try {
1133e49860b0f76d8336c1d41831ed370b0ff94278efRoboErik                mBinder.sendCustomAction(action, args);
1134e49860b0f76d8336c1d41831ed370b0ff94278efRoboErik            } catch (RemoteException e) {
1135e49860b0f76d8336c1d41831ed370b0ff94278efRoboErik                Log.e(TAG, "Dead object in sendCustomAction. " + e);
1136e49860b0f76d8336c1d41831ed370b0ff94278efRoboErik            }
1137e49860b0f76d8336c1d41831ed370b0ff94278efRoboErik        }
1138e49860b0f76d8336c1d41831ed370b0ff94278efRoboErik    }
1139e49860b0f76d8336c1d41831ed370b0ff94278efRoboErik
114024fa6c0dd42df057729e1a258388183f94da7f82Jeff Brown    static class MediaControllerImplApi21 implements MediaControllerImpl {
1141b51f456b92aeb62d5aa9d67e1fb2725b2035fdddIan Lake        protected final Object mControllerObj;
114224fa6c0dd42df057729e1a258388183f94da7f82Jeff Brown
114324fa6c0dd42df057729e1a258388183f94da7f82Jeff Brown        public MediaControllerImplApi21(Context context, MediaSessionCompat session) {
11445c41750574ba65da432b69f89cd32dc356281005RoboErik            mControllerObj = MediaControllerCompatApi21.fromToken(context,
114524fa6c0dd42df057729e1a258388183f94da7f82Jeff Brown                    session.getSessionToken().getToken());
114624fa6c0dd42df057729e1a258388183f94da7f82Jeff Brown        }
114724fa6c0dd42df057729e1a258388183f94da7f82Jeff Brown
114824fa6c0dd42df057729e1a258388183f94da7f82Jeff Brown        public MediaControllerImplApi21(Context context, MediaSessionCompat.Token sessionToken)
114924fa6c0dd42df057729e1a258388183f94da7f82Jeff Brown                throws RemoteException {
11505c41750574ba65da432b69f89cd32dc356281005RoboErik            mControllerObj = MediaControllerCompatApi21.fromToken(context,
115124fa6c0dd42df057729e1a258388183f94da7f82Jeff Brown                    sessionToken.getToken());
115224fa6c0dd42df057729e1a258388183f94da7f82Jeff Brown            if (mControllerObj == null) throw new RemoteException();
115324fa6c0dd42df057729e1a258388183f94da7f82Jeff Brown        }
115424fa6c0dd42df057729e1a258388183f94da7f82Jeff Brown
115524fa6c0dd42df057729e1a258388183f94da7f82Jeff Brown        @Override
115616ac83bebda2a19930b9d692789f3b507c49951bRoboErik        public void registerCallback(Callback callback, Handler handler) {
115716ac83bebda2a19930b9d692789f3b507c49951bRoboErik            MediaControllerCompatApi21.registerCallback(mControllerObj, callback.mCallbackObj, handler);
115824fa6c0dd42df057729e1a258388183f94da7f82Jeff Brown        }
115924fa6c0dd42df057729e1a258388183f94da7f82Jeff Brown
116024fa6c0dd42df057729e1a258388183f94da7f82Jeff Brown        @Override
116116ac83bebda2a19930b9d692789f3b507c49951bRoboErik        public void unregisterCallback(Callback callback) {
116216ac83bebda2a19930b9d692789f3b507c49951bRoboErik            MediaControllerCompatApi21.unregisterCallback(mControllerObj, callback.mCallbackObj);
116324fa6c0dd42df057729e1a258388183f94da7f82Jeff Brown        }
116424fa6c0dd42df057729e1a258388183f94da7f82Jeff Brown
116524fa6c0dd42df057729e1a258388183f94da7f82Jeff Brown        @Override
116624fa6c0dd42df057729e1a258388183f94da7f82Jeff Brown        public boolean dispatchMediaButtonEvent(KeyEvent event) {
116724fa6c0dd42df057729e1a258388183f94da7f82Jeff Brown            return MediaControllerCompatApi21.dispatchMediaButtonEvent(mControllerObj, event);
116824fa6c0dd42df057729e1a258388183f94da7f82Jeff Brown        }
116924fa6c0dd42df057729e1a258388183f94da7f82Jeff Brown
117024fa6c0dd42df057729e1a258388183f94da7f82Jeff Brown        @Override
117124fa6c0dd42df057729e1a258388183f94da7f82Jeff Brown        public TransportControls getTransportControls() {
11721435afe32073dee10e721dfb6122ce6a194a6412RoboErik            Object controlsObj = MediaControllerCompatApi21.getTransportControls(mControllerObj);
117324fa6c0dd42df057729e1a258388183f94da7f82Jeff Brown            return controlsObj != null ? new TransportControlsApi21(controlsObj) : null;
117424fa6c0dd42df057729e1a258388183f94da7f82Jeff Brown        }
117524fa6c0dd42df057729e1a258388183f94da7f82Jeff Brown
117624fa6c0dd42df057729e1a258388183f94da7f82Jeff Brown        @Override
117724fa6c0dd42df057729e1a258388183f94da7f82Jeff Brown        public PlaybackStateCompat getPlaybackState() {
117824fa6c0dd42df057729e1a258388183f94da7f82Jeff Brown            Object stateObj = MediaControllerCompatApi21.getPlaybackState(mControllerObj);
117924fa6c0dd42df057729e1a258388183f94da7f82Jeff Brown            return stateObj != null ? PlaybackStateCompat.fromPlaybackState(stateObj) : null;
118024fa6c0dd42df057729e1a258388183f94da7f82Jeff Brown        }
118124fa6c0dd42df057729e1a258388183f94da7f82Jeff Brown
118224fa6c0dd42df057729e1a258388183f94da7f82Jeff Brown        @Override
118324fa6c0dd42df057729e1a258388183f94da7f82Jeff Brown        public MediaMetadataCompat getMetadata() {
118424fa6c0dd42df057729e1a258388183f94da7f82Jeff Brown            Object metadataObj = MediaControllerCompatApi21.getMetadata(mControllerObj);
118524fa6c0dd42df057729e1a258388183f94da7f82Jeff Brown            return metadataObj != null ? MediaMetadataCompat.fromMediaMetadata(metadataObj) : null;
118624fa6c0dd42df057729e1a258388183f94da7f82Jeff Brown        }
118724fa6c0dd42df057729e1a258388183f94da7f82Jeff Brown
118824fa6c0dd42df057729e1a258388183f94da7f82Jeff Brown        @Override
1189aeb95a772d4365008145407ed52dfbaa61d3c4acRoboErik        public List<MediaSessionCompat.QueueItem> getQueue() {
1190aeb95a772d4365008145407ed52dfbaa61d3c4acRoboErik            List<Object> queueObjs = MediaControllerCompatApi21.getQueue(mControllerObj);
1191aeb95a772d4365008145407ed52dfbaa61d3c4acRoboErik            if (queueObjs == null) {
1192aeb95a772d4365008145407ed52dfbaa61d3c4acRoboErik                return null;
1193aeb95a772d4365008145407ed52dfbaa61d3c4acRoboErik            }
1194aeb95a772d4365008145407ed52dfbaa61d3c4acRoboErik            List<MediaSessionCompat.QueueItem> queue =
1195aeb95a772d4365008145407ed52dfbaa61d3c4acRoboErik                    new ArrayList<MediaSessionCompat.QueueItem>();
1196aeb95a772d4365008145407ed52dfbaa61d3c4acRoboErik            for (Object item : queueObjs) {
1197aeb95a772d4365008145407ed52dfbaa61d3c4acRoboErik                queue.add(MediaSessionCompat.QueueItem.obtain(item));
1198aeb95a772d4365008145407ed52dfbaa61d3c4acRoboErik            }
1199aeb95a772d4365008145407ed52dfbaa61d3c4acRoboErik            return queue;
1200aeb95a772d4365008145407ed52dfbaa61d3c4acRoboErik        }
1201aeb95a772d4365008145407ed52dfbaa61d3c4acRoboErik
1202aeb95a772d4365008145407ed52dfbaa61d3c4acRoboErik        @Override
1203aeb95a772d4365008145407ed52dfbaa61d3c4acRoboErik        public CharSequence getQueueTitle() {
1204aeb95a772d4365008145407ed52dfbaa61d3c4acRoboErik            return MediaControllerCompatApi21.getQueueTitle(mControllerObj);
1205aeb95a772d4365008145407ed52dfbaa61d3c4acRoboErik        }
1206aeb95a772d4365008145407ed52dfbaa61d3c4acRoboErik
1207aeb95a772d4365008145407ed52dfbaa61d3c4acRoboErik        @Override
1208aeb95a772d4365008145407ed52dfbaa61d3c4acRoboErik        public Bundle getExtras() {
1209aeb95a772d4365008145407ed52dfbaa61d3c4acRoboErik            return MediaControllerCompatApi21.getExtras(mControllerObj);
1210aeb95a772d4365008145407ed52dfbaa61d3c4acRoboErik        }
1211aeb95a772d4365008145407ed52dfbaa61d3c4acRoboErik
1212aeb95a772d4365008145407ed52dfbaa61d3c4acRoboErik        @Override
121324fa6c0dd42df057729e1a258388183f94da7f82Jeff Brown        public int getRatingType() {
121424fa6c0dd42df057729e1a258388183f94da7f82Jeff Brown            return MediaControllerCompatApi21.getRatingType(mControllerObj);
121524fa6c0dd42df057729e1a258388183f94da7f82Jeff Brown        }
121624fa6c0dd42df057729e1a258388183f94da7f82Jeff Brown
121724fa6c0dd42df057729e1a258388183f94da7f82Jeff Brown        @Override
1218aeb95a772d4365008145407ed52dfbaa61d3c4acRoboErik        public long getFlags() {
1219aeb95a772d4365008145407ed52dfbaa61d3c4acRoboErik            return MediaControllerCompatApi21.getFlags(mControllerObj);
1220aeb95a772d4365008145407ed52dfbaa61d3c4acRoboErik        }
1221aeb95a772d4365008145407ed52dfbaa61d3c4acRoboErik
1222aeb95a772d4365008145407ed52dfbaa61d3c4acRoboErik        @Override
1223312f13dea7b4a9229dff784c6e94b0ec0c722b74RoboErik        public PlaybackInfo getPlaybackInfo() {
1224312f13dea7b4a9229dff784c6e94b0ec0c722b74RoboErik            Object volumeInfoObj = MediaControllerCompatApi21.getPlaybackInfo(mControllerObj);
1225312f13dea7b4a9229dff784c6e94b0ec0c722b74RoboErik            return volumeInfoObj != null ? new PlaybackInfo(
1226312f13dea7b4a9229dff784c6e94b0ec0c722b74RoboErik                    MediaControllerCompatApi21.PlaybackInfo.getPlaybackType(volumeInfoObj),
1227312f13dea7b4a9229dff784c6e94b0ec0c722b74RoboErik                    MediaControllerCompatApi21.PlaybackInfo.getLegacyAudioStream(volumeInfoObj),
1228312f13dea7b4a9229dff784c6e94b0ec0c722b74RoboErik                    MediaControllerCompatApi21.PlaybackInfo.getVolumeControl(volumeInfoObj),
1229312f13dea7b4a9229dff784c6e94b0ec0c722b74RoboErik                    MediaControllerCompatApi21.PlaybackInfo.getMaxVolume(volumeInfoObj),
1230312f13dea7b4a9229dff784c6e94b0ec0c722b74RoboErik                    MediaControllerCompatApi21.PlaybackInfo.getCurrentVolume(volumeInfoObj)) : null;
123124fa6c0dd42df057729e1a258388183f94da7f82Jeff Brown        }
123224fa6c0dd42df057729e1a258388183f94da7f82Jeff Brown
123324fa6c0dd42df057729e1a258388183f94da7f82Jeff Brown        @Override
1234aeb95a772d4365008145407ed52dfbaa61d3c4acRoboErik        public PendingIntent getSessionActivity() {
1235aeb95a772d4365008145407ed52dfbaa61d3c4acRoboErik            return MediaControllerCompatApi21.getSessionActivity(mControllerObj);
1236aeb95a772d4365008145407ed52dfbaa61d3c4acRoboErik        }
1237aeb95a772d4365008145407ed52dfbaa61d3c4acRoboErik
1238aeb95a772d4365008145407ed52dfbaa61d3c4acRoboErik        @Override
1239aeb95a772d4365008145407ed52dfbaa61d3c4acRoboErik        public void setVolumeTo(int value, int flags) {
1240aeb95a772d4365008145407ed52dfbaa61d3c4acRoboErik            MediaControllerCompatApi21.setVolumeTo(mControllerObj, value, flags);
1241aeb95a772d4365008145407ed52dfbaa61d3c4acRoboErik        }
1242aeb95a772d4365008145407ed52dfbaa61d3c4acRoboErik
1243aeb95a772d4365008145407ed52dfbaa61d3c4acRoboErik        @Override
1244aeb95a772d4365008145407ed52dfbaa61d3c4acRoboErik        public void adjustVolume(int direction, int flags) {
1245aeb95a772d4365008145407ed52dfbaa61d3c4acRoboErik            MediaControllerCompatApi21.adjustVolume(mControllerObj, direction, flags);
1246aeb95a772d4365008145407ed52dfbaa61d3c4acRoboErik        }
1247aeb95a772d4365008145407ed52dfbaa61d3c4acRoboErik
1248aeb95a772d4365008145407ed52dfbaa61d3c4acRoboErik        @Override
1249b530c89bba371d2d575f10480b2e90914b0d3f3fGabriel Peal        public void sendCommand(String command, Bundle params, ResultReceiver cb) {
1250b530c89bba371d2d575f10480b2e90914b0d3f3fGabriel Peal            MediaControllerCompatApi21.sendCommand(mControllerObj, command, params, cb);
125124fa6c0dd42df057729e1a258388183f94da7f82Jeff Brown        }
125224fa6c0dd42df057729e1a258388183f94da7f82Jeff Brown
125324fa6c0dd42df057729e1a258388183f94da7f82Jeff Brown        @Override
1254aeb95a772d4365008145407ed52dfbaa61d3c4acRoboErik        public String getPackageName() {
1255aeb95a772d4365008145407ed52dfbaa61d3c4acRoboErik            return MediaControllerCompatApi21.getPackageName(mControllerObj);
1256aeb95a772d4365008145407ed52dfbaa61d3c4acRoboErik        }
1257aeb95a772d4365008145407ed52dfbaa61d3c4acRoboErik
1258aeb95a772d4365008145407ed52dfbaa61d3c4acRoboErik        @Override
125924fa6c0dd42df057729e1a258388183f94da7f82Jeff Brown        public Object getMediaController() {
126024fa6c0dd42df057729e1a258388183f94da7f82Jeff Brown            return mControllerObj;
126124fa6c0dd42df057729e1a258388183f94da7f82Jeff Brown        }
126224fa6c0dd42df057729e1a258388183f94da7f82Jeff Brown    }
126324fa6c0dd42df057729e1a258388183f94da7f82Jeff Brown
126424fa6c0dd42df057729e1a258388183f94da7f82Jeff Brown    static class TransportControlsApi21 extends TransportControls {
1265b51f456b92aeb62d5aa9d67e1fb2725b2035fdddIan Lake        protected final Object mControlsObj;
126624fa6c0dd42df057729e1a258388183f94da7f82Jeff Brown
126724fa6c0dd42df057729e1a258388183f94da7f82Jeff Brown        public TransportControlsApi21(Object controlsObj) {
126824fa6c0dd42df057729e1a258388183f94da7f82Jeff Brown            mControlsObj = controlsObj;
126924fa6c0dd42df057729e1a258388183f94da7f82Jeff Brown        }
127024fa6c0dd42df057729e1a258388183f94da7f82Jeff Brown
127124fa6c0dd42df057729e1a258388183f94da7f82Jeff Brown        @Override
127224fa6c0dd42df057729e1a258388183f94da7f82Jeff Brown        public void play() {
127324fa6c0dd42df057729e1a258388183f94da7f82Jeff Brown            MediaControllerCompatApi21.TransportControls.play(mControlsObj);
127424fa6c0dd42df057729e1a258388183f94da7f82Jeff Brown        }
127524fa6c0dd42df057729e1a258388183f94da7f82Jeff Brown
127624fa6c0dd42df057729e1a258388183f94da7f82Jeff Brown        @Override
127724fa6c0dd42df057729e1a258388183f94da7f82Jeff Brown        public void pause() {
127824fa6c0dd42df057729e1a258388183f94da7f82Jeff Brown            MediaControllerCompatApi21.TransportControls.pause(mControlsObj);
127924fa6c0dd42df057729e1a258388183f94da7f82Jeff Brown        }
128024fa6c0dd42df057729e1a258388183f94da7f82Jeff Brown
128124fa6c0dd42df057729e1a258388183f94da7f82Jeff Brown        @Override
128224fa6c0dd42df057729e1a258388183f94da7f82Jeff Brown        public void stop() {
128324fa6c0dd42df057729e1a258388183f94da7f82Jeff Brown            MediaControllerCompatApi21.TransportControls.stop(mControlsObj);
128424fa6c0dd42df057729e1a258388183f94da7f82Jeff Brown        }
128524fa6c0dd42df057729e1a258388183f94da7f82Jeff Brown
128624fa6c0dd42df057729e1a258388183f94da7f82Jeff Brown        @Override
128724fa6c0dd42df057729e1a258388183f94da7f82Jeff Brown        public void seekTo(long pos) {
128824fa6c0dd42df057729e1a258388183f94da7f82Jeff Brown            MediaControllerCompatApi21.TransportControls.seekTo(mControlsObj, pos);
128924fa6c0dd42df057729e1a258388183f94da7f82Jeff Brown        }
129024fa6c0dd42df057729e1a258388183f94da7f82Jeff Brown
129124fa6c0dd42df057729e1a258388183f94da7f82Jeff Brown        @Override
129224fa6c0dd42df057729e1a258388183f94da7f82Jeff Brown        public void fastForward() {
129324fa6c0dd42df057729e1a258388183f94da7f82Jeff Brown            MediaControllerCompatApi21.TransportControls.fastForward(mControlsObj);
129424fa6c0dd42df057729e1a258388183f94da7f82Jeff Brown        }
129524fa6c0dd42df057729e1a258388183f94da7f82Jeff Brown
129624fa6c0dd42df057729e1a258388183f94da7f82Jeff Brown        @Override
129724fa6c0dd42df057729e1a258388183f94da7f82Jeff Brown        public void rewind() {
129824fa6c0dd42df057729e1a258388183f94da7f82Jeff Brown            MediaControllerCompatApi21.TransportControls.rewind(mControlsObj);
129924fa6c0dd42df057729e1a258388183f94da7f82Jeff Brown        }
130024fa6c0dd42df057729e1a258388183f94da7f82Jeff Brown
130124fa6c0dd42df057729e1a258388183f94da7f82Jeff Brown        @Override
130224fa6c0dd42df057729e1a258388183f94da7f82Jeff Brown        public void skipToNext() {
130324fa6c0dd42df057729e1a258388183f94da7f82Jeff Brown            MediaControllerCompatApi21.TransportControls.skipToNext(mControlsObj);
130424fa6c0dd42df057729e1a258388183f94da7f82Jeff Brown        }
130524fa6c0dd42df057729e1a258388183f94da7f82Jeff Brown
130624fa6c0dd42df057729e1a258388183f94da7f82Jeff Brown        @Override
130724fa6c0dd42df057729e1a258388183f94da7f82Jeff Brown        public void skipToPrevious() {
130824fa6c0dd42df057729e1a258388183f94da7f82Jeff Brown            MediaControllerCompatApi21.TransportControls.skipToPrevious(mControlsObj);
130924fa6c0dd42df057729e1a258388183f94da7f82Jeff Brown        }
131024fa6c0dd42df057729e1a258388183f94da7f82Jeff Brown
131124fa6c0dd42df057729e1a258388183f94da7f82Jeff Brown        @Override
131224fa6c0dd42df057729e1a258388183f94da7f82Jeff Brown        public void setRating(RatingCompat rating) {
131324fa6c0dd42df057729e1a258388183f94da7f82Jeff Brown            MediaControllerCompatApi21.TransportControls.setRating(mControlsObj,
131424fa6c0dd42df057729e1a258388183f94da7f82Jeff Brown                    rating != null ? rating.getRating() : null);
131524fa6c0dd42df057729e1a258388183f94da7f82Jeff Brown        }
1316aeb95a772d4365008145407ed52dfbaa61d3c4acRoboErik
1317aeb95a772d4365008145407ed52dfbaa61d3c4acRoboErik        @Override
1318aeb95a772d4365008145407ed52dfbaa61d3c4acRoboErik        public void playFromMediaId(String mediaId, Bundle extras) {
1319aeb95a772d4365008145407ed52dfbaa61d3c4acRoboErik            MediaControllerCompatApi21.TransportControls.playFromMediaId(mControlsObj, mediaId,
1320aeb95a772d4365008145407ed52dfbaa61d3c4acRoboErik                    extras);
1321aeb95a772d4365008145407ed52dfbaa61d3c4acRoboErik        }
1322aeb95a772d4365008145407ed52dfbaa61d3c4acRoboErik
1323aeb95a772d4365008145407ed52dfbaa61d3c4acRoboErik        @Override
1324aeb95a772d4365008145407ed52dfbaa61d3c4acRoboErik        public void playFromSearch(String query, Bundle extras) {
1325aeb95a772d4365008145407ed52dfbaa61d3c4acRoboErik            MediaControllerCompatApi21.TransportControls.playFromSearch(mControlsObj, query,
1326aeb95a772d4365008145407ed52dfbaa61d3c4acRoboErik                    extras);
1327aeb95a772d4365008145407ed52dfbaa61d3c4acRoboErik        }
1328aeb95a772d4365008145407ed52dfbaa61d3c4acRoboErik
1329aeb95a772d4365008145407ed52dfbaa61d3c4acRoboErik        @Override
1330b51f456b92aeb62d5aa9d67e1fb2725b2035fdddIan Lake        public void playFromUri(Uri uri, Bundle extras) {
1331b51f456b92aeb62d5aa9d67e1fb2725b2035fdddIan Lake        }
1332b51f456b92aeb62d5aa9d67e1fb2725b2035fdddIan Lake
1333b51f456b92aeb62d5aa9d67e1fb2725b2035fdddIan Lake        @Override
1334aeb95a772d4365008145407ed52dfbaa61d3c4acRoboErik        public void skipToQueueItem(long id) {
1335aeb95a772d4365008145407ed52dfbaa61d3c4acRoboErik            MediaControllerCompatApi21.TransportControls.skipToQueueItem(mControlsObj, id);
1336aeb95a772d4365008145407ed52dfbaa61d3c4acRoboErik        }
1337aeb95a772d4365008145407ed52dfbaa61d3c4acRoboErik
1338aeb95a772d4365008145407ed52dfbaa61d3c4acRoboErik        @Override
1339aeb95a772d4365008145407ed52dfbaa61d3c4acRoboErik        public void sendCustomAction(CustomAction customAction, Bundle args) {
1340aeb95a772d4365008145407ed52dfbaa61d3c4acRoboErik            MediaControllerCompatApi21.TransportControls.sendCustomAction(mControlsObj,
1341aeb95a772d4365008145407ed52dfbaa61d3c4acRoboErik                    customAction.getAction(), args);
1342aeb95a772d4365008145407ed52dfbaa61d3c4acRoboErik        }
1343aeb95a772d4365008145407ed52dfbaa61d3c4acRoboErik
1344aeb95a772d4365008145407ed52dfbaa61d3c4acRoboErik        @Override
1345aeb95a772d4365008145407ed52dfbaa61d3c4acRoboErik        public void sendCustomAction(String action, Bundle args) {
1346aeb95a772d4365008145407ed52dfbaa61d3c4acRoboErik            MediaControllerCompatApi21.TransportControls.sendCustomAction(mControlsObj, action,
1347aeb95a772d4365008145407ed52dfbaa61d3c4acRoboErik                    args);
1348aeb95a772d4365008145407ed52dfbaa61d3c4acRoboErik        }
134924fa6c0dd42df057729e1a258388183f94da7f82Jeff Brown    }
1350b51f456b92aeb62d5aa9d67e1fb2725b2035fdddIan Lake
1351b51f456b92aeb62d5aa9d67e1fb2725b2035fdddIan Lake    static class MediaControllerImplApi23 extends MediaControllerImplApi21 {
1352b51f456b92aeb62d5aa9d67e1fb2725b2035fdddIan Lake
1353b51f456b92aeb62d5aa9d67e1fb2725b2035fdddIan Lake        public MediaControllerImplApi23(Context context, MediaSessionCompat session) {
1354b51f456b92aeb62d5aa9d67e1fb2725b2035fdddIan Lake            super(context, session);
1355b51f456b92aeb62d5aa9d67e1fb2725b2035fdddIan Lake        }
1356b51f456b92aeb62d5aa9d67e1fb2725b2035fdddIan Lake
1357b51f456b92aeb62d5aa9d67e1fb2725b2035fdddIan Lake        public MediaControllerImplApi23(Context context, MediaSessionCompat.Token sessionToken)
1358b51f456b92aeb62d5aa9d67e1fb2725b2035fdddIan Lake                throws RemoteException {
1359b51f456b92aeb62d5aa9d67e1fb2725b2035fdddIan Lake            super(context, sessionToken);
1360b51f456b92aeb62d5aa9d67e1fb2725b2035fdddIan Lake        }
1361b51f456b92aeb62d5aa9d67e1fb2725b2035fdddIan Lake
1362b51f456b92aeb62d5aa9d67e1fb2725b2035fdddIan Lake        @Override
1363b51f456b92aeb62d5aa9d67e1fb2725b2035fdddIan Lake        public TransportControls getTransportControls() {
1364b51f456b92aeb62d5aa9d67e1fb2725b2035fdddIan Lake            Object controlsObj = MediaControllerCompatApi21.getTransportControls(mControllerObj);
1365b51f456b92aeb62d5aa9d67e1fb2725b2035fdddIan Lake            return controlsObj != null ? new TransportControlsApi23(controlsObj) : null;
1366b51f456b92aeb62d5aa9d67e1fb2725b2035fdddIan Lake        }
1367b51f456b92aeb62d5aa9d67e1fb2725b2035fdddIan Lake    }
1368b51f456b92aeb62d5aa9d67e1fb2725b2035fdddIan Lake
1369b51f456b92aeb62d5aa9d67e1fb2725b2035fdddIan Lake    static class TransportControlsApi23 extends TransportControlsApi21 {
1370b51f456b92aeb62d5aa9d67e1fb2725b2035fdddIan Lake
1371b51f456b92aeb62d5aa9d67e1fb2725b2035fdddIan Lake        public TransportControlsApi23(Object controlsObj) {
1372b51f456b92aeb62d5aa9d67e1fb2725b2035fdddIan Lake            super(controlsObj);
1373b51f456b92aeb62d5aa9d67e1fb2725b2035fdddIan Lake        }
1374b51f456b92aeb62d5aa9d67e1fb2725b2035fdddIan Lake
1375b51f456b92aeb62d5aa9d67e1fb2725b2035fdddIan Lake        @Override
1376b51f456b92aeb62d5aa9d67e1fb2725b2035fdddIan Lake        public void playFromUri(Uri uri, Bundle extras) {
1377b51f456b92aeb62d5aa9d67e1fb2725b2035fdddIan Lake            MediaControllerCompatApi23.TransportControls.playFromUri(mControlsObj, uri,
1378b51f456b92aeb62d5aa9d67e1fb2725b2035fdddIan Lake                    extras);
1379b51f456b92aeb62d5aa9d67e1fb2725b2035fdddIan Lake        }
1380b51f456b92aeb62d5aa9d67e1fb2725b2035fdddIan Lake    }
138124fa6c0dd42df057729e1a258388183f94da7f82Jeff Brown}
1382