MediaController.java revision f364f944962c4ec66f5e5b33dafe8480f38f6db6
101fe661ae5da3739215d93922412df4b24c859a2RoboErik/*
201fe661ae5da3739215d93922412df4b24c859a2RoboErik * Copyright (C) 2014 The Android Open Source Project
301fe661ae5da3739215d93922412df4b24c859a2RoboErik *
401fe661ae5da3739215d93922412df4b24c859a2RoboErik * Licensed under the Apache License, Version 2.0 (the "License");
501fe661ae5da3739215d93922412df4b24c859a2RoboErik * you may not use this file except in compliance with the License.
601fe661ae5da3739215d93922412df4b24c859a2RoboErik * You may obtain a copy of the License at
701fe661ae5da3739215d93922412df4b24c859a2RoboErik *
801fe661ae5da3739215d93922412df4b24c859a2RoboErik *      http://www.apache.org/licenses/LICENSE-2.0
901fe661ae5da3739215d93922412df4b24c859a2RoboErik *
1001fe661ae5da3739215d93922412df4b24c859a2RoboErik * Unless required by applicable law or agreed to in writing, software
1101fe661ae5da3739215d93922412df4b24c859a2RoboErik * distributed under the License is distributed on an "AS IS" BASIS,
1201fe661ae5da3739215d93922412df4b24c859a2RoboErik * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
1301fe661ae5da3739215d93922412df4b24c859a2RoboErik * See the License for the specific language governing permissions and
1401fe661ae5da3739215d93922412df4b24c859a2RoboErik * limitations under the License.
1501fe661ae5da3739215d93922412df4b24c859a2RoboErik */
1601fe661ae5da3739215d93922412df4b24c859a2RoboErik
172f5b057da7d05d5d699a272aa24fd7c97cdda820RoboErikpackage android.media.session;
1801fe661ae5da3739215d93922412df4b24c859a2RoboErik
19bf58d9b727f1007c7c620f622ac1d8003b1b211bJeff Brownimport android.annotation.NonNull;
20bf58d9b727f1007c7c620f622ac1d8003b1b211bJeff Brownimport android.annotation.Nullable;
21f0593bc17b61c872ae2d7705fb598c5e5056e679Gabriel Pealimport android.content.pm.ParceledListSlice;
229db9bf7034d7dcdf596dc22d521b18975d0dd2b9RoboErikimport android.media.AudioAttributes;
2319c9518f6a817d53d5234de0020313cab6950b2fRoboErikimport android.media.AudioManager;
2442ea7eecd149161ed192d3029f0d77d1d08a4aa5RoboErikimport android.media.MediaMetadata;
25c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErikimport android.media.Rating;
26ef3c9e9b057a5aac2d0d012e8e6385660478e203RoboErikimport android.media.VolumeProvider;
271a937b04e63539cb1fab1bde601031d415c7156fJeff Brownimport android.media.routing.MediaRouter;
28f0593bc17b61c872ae2d7705fb598c5e5056e679Gabriel Pealimport android.net.Uri;
2901fe661ae5da3739215d93922412df4b24c859a2RoboErikimport android.os.Bundle;
3001fe661ae5da3739215d93922412df4b24c859a2RoboErikimport android.os.Handler;
3101fe661ae5da3739215d93922412df4b24c859a2RoboErikimport android.os.Looper;
3201fe661ae5da3739215d93922412df4b24c859a2RoboErikimport android.os.Message;
3301fe661ae5da3739215d93922412df4b24c859a2RoboErikimport android.os.RemoteException;
348ae0f34db936a649ddaf9cdd086c224f6514efebRoboErikimport android.os.ResultReceiver;
3501fe661ae5da3739215d93922412df4b24c859a2RoboErikimport android.text.TextUtils;
3601fe661ae5da3739215d93922412df4b24c859a2RoboErikimport android.util.Log;
3701fe661ae5da3739215d93922412df4b24c859a2RoboErikimport android.view.KeyEvent;
3801fe661ae5da3739215d93922412df4b24c859a2RoboErik
398ae0f34db936a649ddaf9cdd086c224f6514efebRoboErikimport java.lang.ref.WeakReference;
4001fe661ae5da3739215d93922412df4b24c859a2RoboErikimport java.util.ArrayList;
41f0593bc17b61c872ae2d7705fb598c5e5056e679Gabriel Pealimport java.util.List;
4201fe661ae5da3739215d93922412df4b24c859a2RoboErik
4301fe661ae5da3739215d93922412df4b24c859a2RoboErik/**
4401fe661ae5da3739215d93922412df4b24c859a2RoboErik * Allows an app to interact with an ongoing media session. Media buttons and
4501fe661ae5da3739215d93922412df4b24c859a2RoboErik * other commands can be sent to the session. A callback may be registered to
4601fe661ae5da3739215d93922412df4b24c859a2RoboErik * receive updates from the session, such as metadata and play state changes.
4701fe661ae5da3739215d93922412df4b24c859a2RoboErik * <p>
4842ea7eecd149161ed192d3029f0d77d1d08a4aa5RoboErik * A MediaController can be created through {@link MediaSessionManager} if you
4901fe661ae5da3739215d93922412df4b24c859a2RoboErik * hold the "android.permission.MEDIA_CONTENT_CONTROL" permission or directly if
50dba34ba35cd2042d9a8fecfda56e2abe7a680badJeff Brown * you have a {@link MediaSession.Token} from the session owner.
5101fe661ae5da3739215d93922412df4b24c859a2RoboErik * <p>
5201fe661ae5da3739215d93922412df4b24c859a2RoboErik * MediaController objects are thread-safe.
5301fe661ae5da3739215d93922412df4b24c859a2RoboErik */
5442ea7eecd149161ed192d3029f0d77d1d08a4aa5RoboErikpublic final class MediaController {
55bf58d9b727f1007c7c620f622ac1d8003b1b211bJeff Brown    private static final String TAG = "MediaController";
5601fe661ae5da3739215d93922412df4b24c859a2RoboErik
578ae0f34db936a649ddaf9cdd086c224f6514efebRoboErik    private static final int MSG_EVENT = 1;
58c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik    private static final int MSG_UPDATE_PLAYBACK_STATE = 2;
59c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik    private static final int MSG_UPDATE_METADATA = 3;
6001a500ed1c6ae3fff66678144ae637aa8cad0eccJeff Brown    private static final int MSG_UPDATE_VOLUME = 4;
61f0593bc17b61c872ae2d7705fb598c5e5056e679Gabriel Peal    private static final int MSG_UPDATE_QUEUE = 5;
62f0593bc17b61c872ae2d7705fb598c5e5056e679Gabriel Peal    private static final int MSG_UPDATE_QUEUE_TITLE = 6;
63f0593bc17b61c872ae2d7705fb598c5e5056e679Gabriel Peal    private static final int MSG_UPDATE_EXTRAS = 7;
6401fe661ae5da3739215d93922412df4b24c859a2RoboErik
6507c7077c54717dbbf2c401ea32d00fa6df6d77c6RoboErik    private final ISessionController mSessionBinder;
6601fe661ae5da3739215d93922412df4b24c859a2RoboErik
678ae0f34db936a649ddaf9cdd086c224f6514efebRoboErik    private final CallbackStub mCbStub = new CallbackStub(this);
688ae0f34db936a649ddaf9cdd086c224f6514efebRoboErik    private final ArrayList<MessageHandler> mCallbacks = new ArrayList<MessageHandler>();
6901fe661ae5da3739215d93922412df4b24c859a2RoboErik    private final Object mLock = new Object();
7001fe661ae5da3739215d93922412df4b24c859a2RoboErik
7101fe661ae5da3739215d93922412df4b24c859a2RoboErik    private boolean mCbRegistered = false;
7273e23e229dd1a2d25687b1c6a63c708665378e41RoboErik    private MediaSessionInfo mInfo;
7301fe661ae5da3739215d93922412df4b24c859a2RoboErik
741a937b04e63539cb1fab1bde601031d415c7156fJeff Brown    private final TransportControls mTransportControls;
758ae0f34db936a649ddaf9cdd086c224f6514efebRoboErik
7601fe661ae5da3739215d93922412df4b24c859a2RoboErik    /**
778b4bffcac996b4083e720310a09d315ca1c4a000RoboErik     * Call for creating a MediaController directly from a binder. Should only
788b4bffcac996b4083e720310a09d315ca1c4a000RoboErik     * be used by framework code.
798b4bffcac996b4083e720310a09d315ca1c4a000RoboErik     *
808ae0f34db936a649ddaf9cdd086c224f6514efebRoboErik     * @hide
8101fe661ae5da3739215d93922412df4b24c859a2RoboErik     */
828b4bffcac996b4083e720310a09d315ca1c4a000RoboErik    public MediaController(ISessionController sessionBinder) {
838b4bffcac996b4083e720310a09d315ca1c4a000RoboErik        if (sessionBinder == null) {
848b4bffcac996b4083e720310a09d315ca1c4a000RoboErik            throw new IllegalArgumentException("Session token cannot be null");
858b4bffcac996b4083e720310a09d315ca1c4a000RoboErik        }
868b4bffcac996b4083e720310a09d315ca1c4a000RoboErik        mSessionBinder = sessionBinder;
878b4bffcac996b4083e720310a09d315ca1c4a000RoboErik        mTransportControls = new TransportControls();
8801fe661ae5da3739215d93922412df4b24c859a2RoboErik    }
8901fe661ae5da3739215d93922412df4b24c859a2RoboErik
9001fe661ae5da3739215d93922412df4b24c859a2RoboErik    /**
918b4bffcac996b4083e720310a09d315ca1c4a000RoboErik     * Create a new MediaController from a session's token.
928ae0f34db936a649ddaf9cdd086c224f6514efebRoboErik     *
938b4bffcac996b4083e720310a09d315ca1c4a000RoboErik     * @param token The token for the session.
9401fe661ae5da3739215d93922412df4b24c859a2RoboErik     */
958b4bffcac996b4083e720310a09d315ca1c4a000RoboErik    public MediaController(@NonNull MediaSession.Token token) {
968b4bffcac996b4083e720310a09d315ca1c4a000RoboErik        this(token.getBinder());
9701fe661ae5da3739215d93922412df4b24c859a2RoboErik    }
9801fe661ae5da3739215d93922412df4b24c859a2RoboErik
9901fe661ae5da3739215d93922412df4b24c859a2RoboErik    /**
1001a937b04e63539cb1fab1bde601031d415c7156fJeff Brown     * Get a {@link TransportControls} instance to send transport actions to
1011a937b04e63539cb1fab1bde601031d415c7156fJeff Brown     * the associated session.
10201fe661ae5da3739215d93922412df4b24c859a2RoboErik     *
1031a937b04e63539cb1fab1bde601031d415c7156fJeff Brown     * @return A transport controls instance.
10401fe661ae5da3739215d93922412df4b24c859a2RoboErik     */
105bf58d9b727f1007c7c620f622ac1d8003b1b211bJeff Brown    public @NonNull TransportControls getTransportControls() {
1061a937b04e63539cb1fab1bde601031d415c7156fJeff Brown        return mTransportControls;
1071a937b04e63539cb1fab1bde601031d415c7156fJeff Brown    }
1081a937b04e63539cb1fab1bde601031d415c7156fJeff Brown
1091a937b04e63539cb1fab1bde601031d415c7156fJeff Brown    /**
1101a937b04e63539cb1fab1bde601031d415c7156fJeff Brown     * Creates a media router delegate through which the destination of the media
1111a937b04e63539cb1fab1bde601031d415c7156fJeff Brown     * router may be observed and controlled.
1121a937b04e63539cb1fab1bde601031d415c7156fJeff Brown     *
1131a937b04e63539cb1fab1bde601031d415c7156fJeff Brown     * @return The media router delegate, or null if the media session does
1141a937b04e63539cb1fab1bde601031d415c7156fJeff Brown     * not support media routing.
1151a937b04e63539cb1fab1bde601031d415c7156fJeff Brown     */
1161a937b04e63539cb1fab1bde601031d415c7156fJeff Brown    public @Nullable MediaRouter.Delegate createMediaRouterDelegate() {
1171a937b04e63539cb1fab1bde601031d415c7156fJeff Brown        return new MediaRouter.Delegate();
11801fe661ae5da3739215d93922412df4b24c859a2RoboErik    }
11901fe661ae5da3739215d93922412df4b24c859a2RoboErik
12001fe661ae5da3739215d93922412df4b24c859a2RoboErik    /**
12179fa4630bbca7c6c251eea99fe8997e4b45beceeRoboErik     * Send the specified media button event to the session. Only media keys can
12279fa4630bbca7c6c251eea99fe8997e4b45beceeRoboErik     * be sent by this method, other keys will be ignored.
12301fe661ae5da3739215d93922412df4b24c859a2RoboErik     *
12479fa4630bbca7c6c251eea99fe8997e4b45beceeRoboErik     * @param keyEvent The media button event to dispatch.
12579fa4630bbca7c6c251eea99fe8997e4b45beceeRoboErik     * @return true if the event was sent to the session, false otherwise.
12601fe661ae5da3739215d93922412df4b24c859a2RoboErik     */
127bf58d9b727f1007c7c620f622ac1d8003b1b211bJeff Brown    public boolean dispatchMediaButtonEvent(@NonNull KeyEvent keyEvent) {
12879fa4630bbca7c6c251eea99fe8997e4b45beceeRoboErik        if (keyEvent == null) {
12979fa4630bbca7c6c251eea99fe8997e4b45beceeRoboErik            throw new IllegalArgumentException("KeyEvent may not be null");
13079fa4630bbca7c6c251eea99fe8997e4b45beceeRoboErik        }
13179fa4630bbca7c6c251eea99fe8997e4b45beceeRoboErik        if (!KeyEvent.isMediaKey(keyEvent.getKeyCode())) {
13279fa4630bbca7c6c251eea99fe8997e4b45beceeRoboErik            return false;
13301fe661ae5da3739215d93922412df4b24c859a2RoboErik        }
13401fe661ae5da3739215d93922412df4b24c859a2RoboErik        try {
13579fa4630bbca7c6c251eea99fe8997e4b45beceeRoboErik            return mSessionBinder.sendMediaButton(keyEvent);
13601fe661ae5da3739215d93922412df4b24c859a2RoboErik        } catch (RemoteException e) {
137c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik            // System is dead. =(
13801fe661ae5da3739215d93922412df4b24c859a2RoboErik        }
13979fa4630bbca7c6c251eea99fe8997e4b45beceeRoboErik        return false;
14001fe661ae5da3739215d93922412df4b24c859a2RoboErik    }
14101fe661ae5da3739215d93922412df4b24c859a2RoboErik
14201fe661ae5da3739215d93922412df4b24c859a2RoboErik    /**
143c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik     * Get the current playback state for this session.
144c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik     *
145c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik     * @return The current PlaybackState or null
146c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik     */
147bf58d9b727f1007c7c620f622ac1d8003b1b211bJeff Brown    public @Nullable PlaybackState getPlaybackState() {
148c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik        try {
149c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik            return mSessionBinder.getPlaybackState();
150c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik        } catch (RemoteException e) {
151c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik            Log.wtf(TAG, "Error calling getPlaybackState.", e);
152c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik            return null;
153c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik        }
154c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik    }
155c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik
156c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik    /**
157c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik     * Get the current metadata for this session.
158c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik     *
159c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik     * @return The current MediaMetadata or null.
160c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik     */
161bf58d9b727f1007c7c620f622ac1d8003b1b211bJeff Brown    public @Nullable MediaMetadata getMetadata() {
162c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik        try {
163c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik            return mSessionBinder.getMetadata();
164c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik        } catch (RemoteException e) {
165c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik            Log.wtf(TAG, "Error calling getMetadata.", e);
166c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik            return null;
167c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik        }
168c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik    }
169c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik
170c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik    /**
171f0593bc17b61c872ae2d7705fb598c5e5056e679Gabriel Peal     * Get the current play queue for this session.
172f0593bc17b61c872ae2d7705fb598c5e5056e679Gabriel Peal     *
173f0593bc17b61c872ae2d7705fb598c5e5056e679Gabriel Peal     * @return The current play queue or null.
174f0593bc17b61c872ae2d7705fb598c5e5056e679Gabriel Peal     */
175f0593bc17b61c872ae2d7705fb598c5e5056e679Gabriel Peal    public @Nullable List<MediaSession.Track> getQueue() {
176f0593bc17b61c872ae2d7705fb598c5e5056e679Gabriel Peal        try {
177f0593bc17b61c872ae2d7705fb598c5e5056e679Gabriel Peal            ParceledListSlice queue = mSessionBinder.getQueue();
178f0593bc17b61c872ae2d7705fb598c5e5056e679Gabriel Peal            if (queue != null) {
179f0593bc17b61c872ae2d7705fb598c5e5056e679Gabriel Peal                return queue.getList();
180f0593bc17b61c872ae2d7705fb598c5e5056e679Gabriel Peal            }
181f0593bc17b61c872ae2d7705fb598c5e5056e679Gabriel Peal        } catch (RemoteException e) {
182f0593bc17b61c872ae2d7705fb598c5e5056e679Gabriel Peal            Log.wtf(TAG, "Error calling getQueue.", e);
183f0593bc17b61c872ae2d7705fb598c5e5056e679Gabriel Peal        }
184f0593bc17b61c872ae2d7705fb598c5e5056e679Gabriel Peal        return null;
185f0593bc17b61c872ae2d7705fb598c5e5056e679Gabriel Peal    }
186f0593bc17b61c872ae2d7705fb598c5e5056e679Gabriel Peal
187f0593bc17b61c872ae2d7705fb598c5e5056e679Gabriel Peal    /**
188c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik     * Get the rating type supported by the session. One of:
189c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik     * <ul>
190c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik     * <li>{@link Rating#RATING_NONE}</li>
191c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik     * <li>{@link Rating#RATING_HEART}</li>
192c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik     * <li>{@link Rating#RATING_THUMB_UP_DOWN}</li>
193c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik     * <li>{@link Rating#RATING_3_STARS}</li>
194c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik     * <li>{@link Rating#RATING_4_STARS}</li>
195c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik     * <li>{@link Rating#RATING_5_STARS}</li>
196c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik     * <li>{@link Rating#RATING_PERCENTAGE}</li>
197c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik     * </ul>
198c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik     *
199c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik     * @return The supported rating type
200c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik     */
201c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik    public int getRatingType() {
202c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik        try {
203c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik            return mSessionBinder.getRatingType();
204c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik        } catch (RemoteException e) {
205c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik            Log.wtf(TAG, "Error calling getRatingType.", e);
206c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik            return Rating.RATING_NONE;
207c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik        }
208c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik    }
209c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik
210c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik    /**
21173e23e229dd1a2d25687b1c6a63c708665378e41RoboErik     * Get the flags for this session.
21273e23e229dd1a2d25687b1c6a63c708665378e41RoboErik     *
21373e23e229dd1a2d25687b1c6a63c708665378e41RoboErik     * @return The current set of flags for the session.
21473e23e229dd1a2d25687b1c6a63c708665378e41RoboErik     * @hide
21573e23e229dd1a2d25687b1c6a63c708665378e41RoboErik     */
21673e23e229dd1a2d25687b1c6a63c708665378e41RoboErik    public long getFlags() {
21773e23e229dd1a2d25687b1c6a63c708665378e41RoboErik        try {
21873e23e229dd1a2d25687b1c6a63c708665378e41RoboErik            return mSessionBinder.getFlags();
21973e23e229dd1a2d25687b1c6a63c708665378e41RoboErik        } catch (RemoteException e) {
22073e23e229dd1a2d25687b1c6a63c708665378e41RoboErik            Log.wtf(TAG, "Error calling getFlags.", e);
22173e23e229dd1a2d25687b1c6a63c708665378e41RoboErik        }
22273e23e229dd1a2d25687b1c6a63c708665378e41RoboErik        return 0;
22373e23e229dd1a2d25687b1c6a63c708665378e41RoboErik    }
22473e23e229dd1a2d25687b1c6a63c708665378e41RoboErik
22573e23e229dd1a2d25687b1c6a63c708665378e41RoboErik    /**
226ef3c9e9b057a5aac2d0d012e8e6385660478e203RoboErik     * Get the current volume info for this session.
227ef3c9e9b057a5aac2d0d012e8e6385660478e203RoboErik     *
228ef3c9e9b057a5aac2d0d012e8e6385660478e203RoboErik     * @return The current volume info or null.
229ef3c9e9b057a5aac2d0d012e8e6385660478e203RoboErik     */
230bf58d9b727f1007c7c620f622ac1d8003b1b211bJeff Brown    public @Nullable VolumeInfo getVolumeInfo() {
231ef3c9e9b057a5aac2d0d012e8e6385660478e203RoboErik        try {
232ef3c9e9b057a5aac2d0d012e8e6385660478e203RoboErik            ParcelableVolumeInfo result = mSessionBinder.getVolumeAttributes();
2339db9bf7034d7dcdf596dc22d521b18975d0dd2b9RoboErik            return new VolumeInfo(result.volumeType, result.audioAttrs, result.controlType,
234ef3c9e9b057a5aac2d0d012e8e6385660478e203RoboErik                    result.maxVolume, result.currentVolume);
235ef3c9e9b057a5aac2d0d012e8e6385660478e203RoboErik
236ef3c9e9b057a5aac2d0d012e8e6385660478e203RoboErik        } catch (RemoteException e) {
237ef3c9e9b057a5aac2d0d012e8e6385660478e203RoboErik            Log.wtf(TAG, "Error calling getVolumeInfo.", e);
238ef3c9e9b057a5aac2d0d012e8e6385660478e203RoboErik        }
239ef3c9e9b057a5aac2d0d012e8e6385660478e203RoboErik        return null;
240ef3c9e9b057a5aac2d0d012e8e6385660478e203RoboErik    }
241ef3c9e9b057a5aac2d0d012e8e6385660478e203RoboErik
242ef3c9e9b057a5aac2d0d012e8e6385660478e203RoboErik    /**
2439db9bf7034d7dcdf596dc22d521b18975d0dd2b9RoboErik     * Set the volume of the output this session is playing on. The command will
2449db9bf7034d7dcdf596dc22d521b18975d0dd2b9RoboErik     * be ignored if it does not support
24519c9518f6a817d53d5234de0020313cab6950b2fRoboErik     * {@link VolumeProvider#VOLUME_CONTROL_ABSOLUTE}. The flags in
24619c9518f6a817d53d5234de0020313cab6950b2fRoboErik     * {@link AudioManager} may be used to affect the handling.
24719c9518f6a817d53d5234de0020313cab6950b2fRoboErik     *
24819c9518f6a817d53d5234de0020313cab6950b2fRoboErik     * @see #getVolumeInfo()
24919c9518f6a817d53d5234de0020313cab6950b2fRoboErik     * @param value The value to set it to, between 0 and the reported max.
25019c9518f6a817d53d5234de0020313cab6950b2fRoboErik     * @param flags Any flags to pass with the command.
25119c9518f6a817d53d5234de0020313cab6950b2fRoboErik     */
25219c9518f6a817d53d5234de0020313cab6950b2fRoboErik    public void setVolumeTo(int value, int flags) {
25319c9518f6a817d53d5234de0020313cab6950b2fRoboErik        try {
25419c9518f6a817d53d5234de0020313cab6950b2fRoboErik            mSessionBinder.setVolumeTo(value, flags);
25519c9518f6a817d53d5234de0020313cab6950b2fRoboErik        } catch (RemoteException e) {
25619c9518f6a817d53d5234de0020313cab6950b2fRoboErik            Log.wtf(TAG, "Error calling setVolumeTo.", e);
25719c9518f6a817d53d5234de0020313cab6950b2fRoboErik        }
25819c9518f6a817d53d5234de0020313cab6950b2fRoboErik    }
25919c9518f6a817d53d5234de0020313cab6950b2fRoboErik
26019c9518f6a817d53d5234de0020313cab6950b2fRoboErik    /**
2619db9bf7034d7dcdf596dc22d521b18975d0dd2b9RoboErik     * Adjust the volume of the output this session is playing on. The direction
2629db9bf7034d7dcdf596dc22d521b18975d0dd2b9RoboErik     * must be one of {@link AudioManager#ADJUST_LOWER},
2631ff5b1648a051e9650614f0c0f1b3f449777db81RoboErik     * {@link AudioManager#ADJUST_RAISE}, or {@link AudioManager#ADJUST_SAME}.
2641ff5b1648a051e9650614f0c0f1b3f449777db81RoboErik     * The command will be ignored if the session does not support
2651ff5b1648a051e9650614f0c0f1b3f449777db81RoboErik     * {@link VolumeProvider#VOLUME_CONTROL_RELATIVE} or
26619c9518f6a817d53d5234de0020313cab6950b2fRoboErik     * {@link VolumeProvider#VOLUME_CONTROL_ABSOLUTE}. The flags in
26719c9518f6a817d53d5234de0020313cab6950b2fRoboErik     * {@link AudioManager} may be used to affect the handling.
26819c9518f6a817d53d5234de0020313cab6950b2fRoboErik     *
26919c9518f6a817d53d5234de0020313cab6950b2fRoboErik     * @see #getVolumeInfo()
2701ff5b1648a051e9650614f0c0f1b3f449777db81RoboErik     * @param direction The direction to adjust the volume in.
27119c9518f6a817d53d5234de0020313cab6950b2fRoboErik     * @param flags Any flags to pass with the command.
27219c9518f6a817d53d5234de0020313cab6950b2fRoboErik     */
2731ff5b1648a051e9650614f0c0f1b3f449777db81RoboErik    public void adjustVolume(int direction, int flags) {
27419c9518f6a817d53d5234de0020313cab6950b2fRoboErik        try {
2751ff5b1648a051e9650614f0c0f1b3f449777db81RoboErik            mSessionBinder.adjustVolume(direction, flags);
27619c9518f6a817d53d5234de0020313cab6950b2fRoboErik        } catch (RemoteException e) {
27719c9518f6a817d53d5234de0020313cab6950b2fRoboErik            Log.wtf(TAG, "Error calling adjustVolumeBy.", e);
27819c9518f6a817d53d5234de0020313cab6950b2fRoboErik        }
27919c9518f6a817d53d5234de0020313cab6950b2fRoboErik    }
28019c9518f6a817d53d5234de0020313cab6950b2fRoboErik
28119c9518f6a817d53d5234de0020313cab6950b2fRoboErik    /**
28201fe661ae5da3739215d93922412df4b24c859a2RoboErik     * Adds a callback to receive updates from the Session. Updates will be
28301fe661ae5da3739215d93922412df4b24c859a2RoboErik     * posted on the caller's thread.
28401fe661ae5da3739215d93922412df4b24c859a2RoboErik     *
285bf58d9b727f1007c7c620f622ac1d8003b1b211bJeff Brown     * @param callback The callback object, must not be null.
28601fe661ae5da3739215d93922412df4b24c859a2RoboErik     */
287bf58d9b727f1007c7c620f622ac1d8003b1b211bJeff Brown    public void addCallback(@NonNull Callback callback) {
288bf58d9b727f1007c7c620f622ac1d8003b1b211bJeff Brown        addCallback(callback, null);
28901fe661ae5da3739215d93922412df4b24c859a2RoboErik    }
29001fe661ae5da3739215d93922412df4b24c859a2RoboErik
29101fe661ae5da3739215d93922412df4b24c859a2RoboErik    /**
29201fe661ae5da3739215d93922412df4b24c859a2RoboErik     * Adds a callback to receive updates from the session. Updates will be
2938ae0f34db936a649ddaf9cdd086c224f6514efebRoboErik     * posted on the specified handler's thread.
29401fe661ae5da3739215d93922412df4b24c859a2RoboErik     *
295bf58d9b727f1007c7c620f622ac1d8003b1b211bJeff Brown     * @param callback The callback object, must not be null.
2968ae0f34db936a649ddaf9cdd086c224f6514efebRoboErik     * @param handler The handler to post updates on. If null the callers thread
297bf58d9b727f1007c7c620f622ac1d8003b1b211bJeff Brown     *            will be used.
29801fe661ae5da3739215d93922412df4b24c859a2RoboErik     */
299bf58d9b727f1007c7c620f622ac1d8003b1b211bJeff Brown    public void addCallback(@NonNull Callback callback, @Nullable Handler handler) {
300bf58d9b727f1007c7c620f622ac1d8003b1b211bJeff Brown        if (callback == null) {
301bf58d9b727f1007c7c620f622ac1d8003b1b211bJeff Brown            throw new IllegalArgumentException("callback must not be null");
302bf58d9b727f1007c7c620f622ac1d8003b1b211bJeff Brown        }
30301fe661ae5da3739215d93922412df4b24c859a2RoboErik        if (handler == null) {
30401fe661ae5da3739215d93922412df4b24c859a2RoboErik            handler = new Handler();
30501fe661ae5da3739215d93922412df4b24c859a2RoboErik        }
30601fe661ae5da3739215d93922412df4b24c859a2RoboErik        synchronized (mLock) {
307bf58d9b727f1007c7c620f622ac1d8003b1b211bJeff Brown            addCallbackLocked(callback, handler);
30801fe661ae5da3739215d93922412df4b24c859a2RoboErik        }
30901fe661ae5da3739215d93922412df4b24c859a2RoboErik    }
31001fe661ae5da3739215d93922412df4b24c859a2RoboErik
31101fe661ae5da3739215d93922412df4b24c859a2RoboErik    /**
31201fe661ae5da3739215d93922412df4b24c859a2RoboErik     * Stop receiving updates on the specified callback. If an update has
31301fe661ae5da3739215d93922412df4b24c859a2RoboErik     * already been posted you may still receive it after calling this method.
31401fe661ae5da3739215d93922412df4b24c859a2RoboErik     *
315bf58d9b727f1007c7c620f622ac1d8003b1b211bJeff Brown     * @param callback The callback to remove.
31601fe661ae5da3739215d93922412df4b24c859a2RoboErik     */
317bf58d9b727f1007c7c620f622ac1d8003b1b211bJeff Brown    public void removeCallback(@NonNull Callback callback) {
318bf58d9b727f1007c7c620f622ac1d8003b1b211bJeff Brown        if (callback == null) {
319bf58d9b727f1007c7c620f622ac1d8003b1b211bJeff Brown            throw new IllegalArgumentException("callback must not be null");
320bf58d9b727f1007c7c620f622ac1d8003b1b211bJeff Brown        }
32101fe661ae5da3739215d93922412df4b24c859a2RoboErik        synchronized (mLock) {
322bf58d9b727f1007c7c620f622ac1d8003b1b211bJeff Brown            removeCallbackLocked(callback);
32301fe661ae5da3739215d93922412df4b24c859a2RoboErik        }
32401fe661ae5da3739215d93922412df4b24c859a2RoboErik    }
32501fe661ae5da3739215d93922412df4b24c859a2RoboErik
3268ae0f34db936a649ddaf9cdd086c224f6514efebRoboErik    /**
3278ae0f34db936a649ddaf9cdd086c224f6514efebRoboErik     * Sends a generic command to the session. It is up to the session creator
3288ae0f34db936a649ddaf9cdd086c224f6514efebRoboErik     * to decide what commands and parameters they will support. As such,
3298ae0f34db936a649ddaf9cdd086c224f6514efebRoboErik     * commands should only be sent to sessions that the controller owns.
3308ae0f34db936a649ddaf9cdd086c224f6514efebRoboErik     *
3318ae0f34db936a649ddaf9cdd086c224f6514efebRoboErik     * @param command The command to send
332f364f944962c4ec66f5e5b33dafe8480f38f6db6Gabriel Peal     * @param args Any parameters to include with the command
3338ae0f34db936a649ddaf9cdd086c224f6514efebRoboErik     * @param cb The callback to receive the result on
3348ae0f34db936a649ddaf9cdd086c224f6514efebRoboErik     */
335f364f944962c4ec66f5e5b33dafe8480f38f6db6Gabriel Peal    public void sendCommand(@NonNull String command, @Nullable Bundle args,
336bf58d9b727f1007c7c620f622ac1d8003b1b211bJeff Brown            @Nullable ResultReceiver cb) {
3378ae0f34db936a649ddaf9cdd086c224f6514efebRoboErik        if (TextUtils.isEmpty(command)) {
3388ae0f34db936a649ddaf9cdd086c224f6514efebRoboErik            throw new IllegalArgumentException("command cannot be null or empty");
3398ae0f34db936a649ddaf9cdd086c224f6514efebRoboErik        }
3408ae0f34db936a649ddaf9cdd086c224f6514efebRoboErik        try {
341f364f944962c4ec66f5e5b33dafe8480f38f6db6Gabriel Peal            mSessionBinder.sendCommand(command, args, cb);
3428ae0f34db936a649ddaf9cdd086c224f6514efebRoboErik        } catch (RemoteException e) {
3438ae0f34db936a649ddaf9cdd086c224f6514efebRoboErik            Log.d(TAG, "Dead object in sendCommand.", e);
3448ae0f34db936a649ddaf9cdd086c224f6514efebRoboErik        }
3458ae0f34db936a649ddaf9cdd086c224f6514efebRoboErik    }
3468ae0f34db936a649ddaf9cdd086c224f6514efebRoboErik
34707c7077c54717dbbf2c401ea32d00fa6df6d77c6RoboErik    /**
348fb442b03840245c7e52cf2a540a77c5fc6c54587RoboErik     * Get the info for the session this controller is connected to.
349fb442b03840245c7e52cf2a540a77c5fc6c54587RoboErik     *
350fb442b03840245c7e52cf2a540a77c5fc6c54587RoboErik     * @return The session info for the connected session.
351fb442b03840245c7e52cf2a540a77c5fc6c54587RoboErik     * @hide
352fb442b03840245c7e52cf2a540a77c5fc6c54587RoboErik     */
353fb442b03840245c7e52cf2a540a77c5fc6c54587RoboErik    public MediaSessionInfo getSessionInfo() {
35473e23e229dd1a2d25687b1c6a63c708665378e41RoboErik        if (mInfo == null) {
35573e23e229dd1a2d25687b1c6a63c708665378e41RoboErik            try {
35673e23e229dd1a2d25687b1c6a63c708665378e41RoboErik                mInfo = mSessionBinder.getSessionInfo();
35773e23e229dd1a2d25687b1c6a63c708665378e41RoboErik            } catch (RemoteException e) {
35873e23e229dd1a2d25687b1c6a63c708665378e41RoboErik                Log.e(TAG, "Error in getSessionInfo.", e);
35973e23e229dd1a2d25687b1c6a63c708665378e41RoboErik            }
360fb442b03840245c7e52cf2a540a77c5fc6c54587RoboErik        }
36173e23e229dd1a2d25687b1c6a63c708665378e41RoboErik        return mInfo;
362fb442b03840245c7e52cf2a540a77c5fc6c54587RoboErik    }
363fb442b03840245c7e52cf2a540a77c5fc6c54587RoboErik
36401fe661ae5da3739215d93922412df4b24c859a2RoboErik    /*
36501fe661ae5da3739215d93922412df4b24c859a2RoboErik     * @hide
36601fe661ae5da3739215d93922412df4b24c859a2RoboErik     */
36707c7077c54717dbbf2c401ea32d00fa6df6d77c6RoboErik    ISessionController getSessionBinder() {
36801fe661ae5da3739215d93922412df4b24c859a2RoboErik        return mSessionBinder;
36901fe661ae5da3739215d93922412df4b24c859a2RoboErik    }
37001fe661ae5da3739215d93922412df4b24c859a2RoboErik
37101fe661ae5da3739215d93922412df4b24c859a2RoboErik    private void addCallbackLocked(Callback cb, Handler handler) {
3728ae0f34db936a649ddaf9cdd086c224f6514efebRoboErik        if (getHandlerForCallbackLocked(cb) != null) {
37301fe661ae5da3739215d93922412df4b24c859a2RoboErik            Log.w(TAG, "Callback is already added, ignoring");
37401fe661ae5da3739215d93922412df4b24c859a2RoboErik            return;
37501fe661ae5da3739215d93922412df4b24c859a2RoboErik        }
3768ae0f34db936a649ddaf9cdd086c224f6514efebRoboErik        MessageHandler holder = new MessageHandler(handler.getLooper(), cb);
3778ae0f34db936a649ddaf9cdd086c224f6514efebRoboErik        mCallbacks.add(holder);
37801fe661ae5da3739215d93922412df4b24c859a2RoboErik
37901fe661ae5da3739215d93922412df4b24c859a2RoboErik        if (!mCbRegistered) {
38001fe661ae5da3739215d93922412df4b24c859a2RoboErik            try {
38101fe661ae5da3739215d93922412df4b24c859a2RoboErik                mSessionBinder.registerCallbackListener(mCbStub);
38201fe661ae5da3739215d93922412df4b24c859a2RoboErik                mCbRegistered = true;
38301fe661ae5da3739215d93922412df4b24c859a2RoboErik            } catch (RemoteException e) {
384d3c8642dae9a1f6db60e2f8e5c7b32cd1b3169dfRoboErik                Log.e(TAG, "Dead object in registerCallback", e);
38501fe661ae5da3739215d93922412df4b24c859a2RoboErik            }
38601fe661ae5da3739215d93922412df4b24c859a2RoboErik        }
38701fe661ae5da3739215d93922412df4b24c859a2RoboErik    }
38801fe661ae5da3739215d93922412df4b24c859a2RoboErik
3898ae0f34db936a649ddaf9cdd086c224f6514efebRoboErik    private boolean removeCallbackLocked(Callback cb) {
390d3c8642dae9a1f6db60e2f8e5c7b32cd1b3169dfRoboErik        boolean success = false;
3918ae0f34db936a649ddaf9cdd086c224f6514efebRoboErik        for (int i = mCallbacks.size() - 1; i >= 0; i--) {
3928ae0f34db936a649ddaf9cdd086c224f6514efebRoboErik            MessageHandler handler = mCallbacks.get(i);
3938ae0f34db936a649ddaf9cdd086c224f6514efebRoboErik            if (cb == handler.mCallback) {
3948ae0f34db936a649ddaf9cdd086c224f6514efebRoboErik                mCallbacks.remove(i);
395d3c8642dae9a1f6db60e2f8e5c7b32cd1b3169dfRoboErik                success = true;
39601fe661ae5da3739215d93922412df4b24c859a2RoboErik            }
39701fe661ae5da3739215d93922412df4b24c859a2RoboErik        }
398d3c8642dae9a1f6db60e2f8e5c7b32cd1b3169dfRoboErik        if (mCbRegistered && mCallbacks.size() == 0) {
399d3c8642dae9a1f6db60e2f8e5c7b32cd1b3169dfRoboErik            try {
400d3c8642dae9a1f6db60e2f8e5c7b32cd1b3169dfRoboErik                mSessionBinder.unregisterCallbackListener(mCbStub);
401d3c8642dae9a1f6db60e2f8e5c7b32cd1b3169dfRoboErik            } catch (RemoteException e) {
402d3c8642dae9a1f6db60e2f8e5c7b32cd1b3169dfRoboErik                Log.e(TAG, "Dead object in removeCallbackLocked");
403d3c8642dae9a1f6db60e2f8e5c7b32cd1b3169dfRoboErik            }
404d3c8642dae9a1f6db60e2f8e5c7b32cd1b3169dfRoboErik            mCbRegistered = false;
405d3c8642dae9a1f6db60e2f8e5c7b32cd1b3169dfRoboErik        }
406d3c8642dae9a1f6db60e2f8e5c7b32cd1b3169dfRoboErik        return success;
40701fe661ae5da3739215d93922412df4b24c859a2RoboErik    }
40801fe661ae5da3739215d93922412df4b24c859a2RoboErik
4098ae0f34db936a649ddaf9cdd086c224f6514efebRoboErik    private MessageHandler getHandlerForCallbackLocked(Callback cb) {
4108ae0f34db936a649ddaf9cdd086c224f6514efebRoboErik        if (cb == null) {
4118ae0f34db936a649ddaf9cdd086c224f6514efebRoboErik            throw new IllegalArgumentException("Callback cannot be null");
41201fe661ae5da3739215d93922412df4b24c859a2RoboErik        }
4138ae0f34db936a649ddaf9cdd086c224f6514efebRoboErik        for (int i = mCallbacks.size() - 1; i >= 0; i--) {
4148ae0f34db936a649ddaf9cdd086c224f6514efebRoboErik            MessageHandler handler = mCallbacks.get(i);
4158ae0f34db936a649ddaf9cdd086c224f6514efebRoboErik            if (cb == handler.mCallback) {
4168ae0f34db936a649ddaf9cdd086c224f6514efebRoboErik                return handler;
4178ae0f34db936a649ddaf9cdd086c224f6514efebRoboErik            }
41801fe661ae5da3739215d93922412df4b24c859a2RoboErik        }
4198ae0f34db936a649ddaf9cdd086c224f6514efebRoboErik        return null;
42001fe661ae5da3739215d93922412df4b24c859a2RoboErik    }
42101fe661ae5da3739215d93922412df4b24c859a2RoboErik
422c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik    private final void postMessage(int what, Object obj, Bundle data) {
4238ae0f34db936a649ddaf9cdd086c224f6514efebRoboErik        synchronized (mLock) {
4248ae0f34db936a649ddaf9cdd086c224f6514efebRoboErik            for (int i = mCallbacks.size() - 1; i >= 0; i--) {
425c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik                mCallbacks.get(i).post(what, obj, data);
4268ae0f34db936a649ddaf9cdd086c224f6514efebRoboErik            }
42701fe661ae5da3739215d93922412df4b24c859a2RoboErik        }
42801fe661ae5da3739215d93922412df4b24c859a2RoboErik    }
42901fe661ae5da3739215d93922412df4b24c859a2RoboErik
43001fe661ae5da3739215d93922412df4b24c859a2RoboErik    /**
4318ae0f34db936a649ddaf9cdd086c224f6514efebRoboErik     * Callback for receiving updates on from the session. A Callback can be
4328ae0f34db936a649ddaf9cdd086c224f6514efebRoboErik     * registered using {@link #addCallback}
43301fe661ae5da3739215d93922412df4b24c859a2RoboErik     */
43401fe661ae5da3739215d93922412df4b24c859a2RoboErik    public static abstract class Callback {
43501fe661ae5da3739215d93922412df4b24c859a2RoboErik        /**
4368ae0f34db936a649ddaf9cdd086c224f6514efebRoboErik         * Override to handle custom events sent by the session owner without a
4378ae0f34db936a649ddaf9cdd086c224f6514efebRoboErik         * specified interface. Controllers should only handle these for
4388ae0f34db936a649ddaf9cdd086c224f6514efebRoboErik         * sessions they own.
43901fe661ae5da3739215d93922412df4b24c859a2RoboErik         *
440bf58d9b727f1007c7c620f622ac1d8003b1b211bJeff Brown         * @param event The event from the session.
441bf58d9b727f1007c7c620f622ac1d8003b1b211bJeff Brown         * @param extras Optional parameters for the event, may be null.
44201fe661ae5da3739215d93922412df4b24c859a2RoboErik         */
443bf58d9b727f1007c7c620f622ac1d8003b1b211bJeff Brown        public void onSessionEvent(@NonNull String event, @Nullable Bundle extras) {
44401fe661ae5da3739215d93922412df4b24c859a2RoboErik        }
44501fe661ae5da3739215d93922412df4b24c859a2RoboErik
44601fe661ae5da3739215d93922412df4b24c859a2RoboErik        /**
447c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik         * Override to handle changes in playback state.
448c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik         *
449c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik         * @param state The new playback state of the session
450c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik         */
451bf58d9b727f1007c7c620f622ac1d8003b1b211bJeff Brown        public void onPlaybackStateChanged(@NonNull PlaybackState state) {
452c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik        }
453c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik
454c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik        /**
455c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik         * Override to handle changes to the current metadata.
456c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik         *
457bf58d9b727f1007c7c620f622ac1d8003b1b211bJeff Brown         * @param metadata The current metadata for the session or null if none.
458c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik         * @see MediaMetadata
459c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik         */
460bf58d9b727f1007c7c620f622ac1d8003b1b211bJeff Brown        public void onMetadataChanged(@Nullable MediaMetadata metadata) {
461c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik        }
46219c9518f6a817d53d5234de0020313cab6950b2fRoboErik
46319c9518f6a817d53d5234de0020313cab6950b2fRoboErik        /**
464f0593bc17b61c872ae2d7705fb598c5e5056e679Gabriel Peal         * Override to handle changes to tracks in the queue.
465f0593bc17b61c872ae2d7705fb598c5e5056e679Gabriel Peal         *
466f0593bc17b61c872ae2d7705fb598c5e5056e679Gabriel Peal         * @param queue A list of tracks in the current play queue. It should include the currently
467f0593bc17b61c872ae2d7705fb598c5e5056e679Gabriel Peal         *              playing track as well as previous and upcoming tracks if applicable.
468f0593bc17b61c872ae2d7705fb598c5e5056e679Gabriel Peal         * @see MediaSession.Track
469f0593bc17b61c872ae2d7705fb598c5e5056e679Gabriel Peal         */
470f0593bc17b61c872ae2d7705fb598c5e5056e679Gabriel Peal        public void onQueueChanged(@Nullable List<MediaSession.Track> queue) {
471f0593bc17b61c872ae2d7705fb598c5e5056e679Gabriel Peal        }
472f0593bc17b61c872ae2d7705fb598c5e5056e679Gabriel Peal
473f0593bc17b61c872ae2d7705fb598c5e5056e679Gabriel Peal        /**
474f0593bc17b61c872ae2d7705fb598c5e5056e679Gabriel Peal         * Override to handle changes to the queue title.
475f0593bc17b61c872ae2d7705fb598c5e5056e679Gabriel Peal         *
476f0593bc17b61c872ae2d7705fb598c5e5056e679Gabriel Peal         * @param title The title that should be displayed along with the play queue such as
477f0593bc17b61c872ae2d7705fb598c5e5056e679Gabriel Peal         *              "Now Playing". May be null if there is no such title.
478f0593bc17b61c872ae2d7705fb598c5e5056e679Gabriel Peal         */
479f0593bc17b61c872ae2d7705fb598c5e5056e679Gabriel Peal        public void onQueueTitleChanged(@Nullable CharSequence title) {
480f0593bc17b61c872ae2d7705fb598c5e5056e679Gabriel Peal        }
481f0593bc17b61c872ae2d7705fb598c5e5056e679Gabriel Peal
482f0593bc17b61c872ae2d7705fb598c5e5056e679Gabriel Peal        /**
483f0593bc17b61c872ae2d7705fb598c5e5056e679Gabriel Peal         * Override to handle changes to the {@link MediaSession} extras.
484f0593bc17b61c872ae2d7705fb598c5e5056e679Gabriel Peal         *
485f0593bc17b61c872ae2d7705fb598c5e5056e679Gabriel Peal         * @param extras The extras that can include other information associated with the
486f0593bc17b61c872ae2d7705fb598c5e5056e679Gabriel Peal         *               {@link MediaSession}.
487f0593bc17b61c872ae2d7705fb598c5e5056e679Gabriel Peal         */
488f0593bc17b61c872ae2d7705fb598c5e5056e679Gabriel Peal        public void onExtrasChanged(@Nullable Bundle extras) {
489f0593bc17b61c872ae2d7705fb598c5e5056e679Gabriel Peal        }
490f0593bc17b61c872ae2d7705fb598c5e5056e679Gabriel Peal
491f0593bc17b61c872ae2d7705fb598c5e5056e679Gabriel Peal        /**
49219c9518f6a817d53d5234de0020313cab6950b2fRoboErik         * Override to handle changes to the volume info.
49319c9518f6a817d53d5234de0020313cab6950b2fRoboErik         *
49419c9518f6a817d53d5234de0020313cab6950b2fRoboErik         * @param info The current volume info for this session.
49519c9518f6a817d53d5234de0020313cab6950b2fRoboErik         */
49619c9518f6a817d53d5234de0020313cab6950b2fRoboErik        public void onVolumeInfoChanged(VolumeInfo info) {
49719c9518f6a817d53d5234de0020313cab6950b2fRoboErik        }
498c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik    }
499c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik
500c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik    /**
501c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik     * Interface for controlling media playback on a session. This allows an app
502c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik     * to send media transport commands to the session.
503c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik     */
504c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik    public final class TransportControls {
505c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik        private static final String TAG = "TransportController";
506c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik
507c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik        private TransportControls() {
508c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik        }
509c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik
510c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik        /**
511c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik         * Request that the player start its playback at its current position.
512c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik         */
513c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik        public void play() {
514c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik            try {
515c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik                mSessionBinder.play();
516c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik            } catch (RemoteException e) {
517c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik                Log.wtf(TAG, "Error calling play.", e);
518c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik            }
519c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik        }
520c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik
521c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik        /**
522f0593bc17b61c872ae2d7705fb598c5e5056e679Gabriel Peal         * Request that the player start playback for a specific {@link Uri}.
523f0593bc17b61c872ae2d7705fb598c5e5056e679Gabriel Peal         *
524f0593bc17b61c872ae2d7705fb598c5e5056e679Gabriel Peal         * @param uri The uri of the requested media.
525f0593bc17b61c872ae2d7705fb598c5e5056e679Gabriel Peal         * @param extras Optional extras that can include extra information about the media item
526f0593bc17b61c872ae2d7705fb598c5e5056e679Gabriel Peal         *               to be played.
527f0593bc17b61c872ae2d7705fb598c5e5056e679Gabriel Peal         */
528f0593bc17b61c872ae2d7705fb598c5e5056e679Gabriel Peal        public void playUri(Uri uri, Bundle extras) {
529f0593bc17b61c872ae2d7705fb598c5e5056e679Gabriel Peal            if (uri == null) {
530f0593bc17b61c872ae2d7705fb598c5e5056e679Gabriel Peal                throw new IllegalArgumentException("You must specify a non-null Uri for playUri.");
531f0593bc17b61c872ae2d7705fb598c5e5056e679Gabriel Peal            }
532f0593bc17b61c872ae2d7705fb598c5e5056e679Gabriel Peal            try {
533f0593bc17b61c872ae2d7705fb598c5e5056e679Gabriel Peal                mSessionBinder.playUri(uri, extras);
534f0593bc17b61c872ae2d7705fb598c5e5056e679Gabriel Peal            } catch (RemoteException e) {
535f0593bc17b61c872ae2d7705fb598c5e5056e679Gabriel Peal                Log.wtf(TAG, "Error calling play(" + uri + ").", e);
536f0593bc17b61c872ae2d7705fb598c5e5056e679Gabriel Peal            }
537f0593bc17b61c872ae2d7705fb598c5e5056e679Gabriel Peal        }
538f0593bc17b61c872ae2d7705fb598c5e5056e679Gabriel Peal
539f0593bc17b61c872ae2d7705fb598c5e5056e679Gabriel Peal        /**
540f0593bc17b61c872ae2d7705fb598c5e5056e679Gabriel Peal         * Request that the player start playback for a specific search query.
541f0593bc17b61c872ae2d7705fb598c5e5056e679Gabriel Peal         *
542f0593bc17b61c872ae2d7705fb598c5e5056e679Gabriel Peal         * @param query The search query.
543f0593bc17b61c872ae2d7705fb598c5e5056e679Gabriel Peal         * @param extras Optional extras that can include extra information about the query.
544f0593bc17b61c872ae2d7705fb598c5e5056e679Gabriel Peal         */
545f0593bc17b61c872ae2d7705fb598c5e5056e679Gabriel Peal        public void playFromSearch(String query, Bundle extras) {
546f0593bc17b61c872ae2d7705fb598c5e5056e679Gabriel Peal            if (TextUtils.isEmpty(query)) {
547f0593bc17b61c872ae2d7705fb598c5e5056e679Gabriel Peal                throw new IllegalArgumentException(
548f0593bc17b61c872ae2d7705fb598c5e5056e679Gabriel Peal                        "You must specify a non-empty search query for playFromSearch.");
549f0593bc17b61c872ae2d7705fb598c5e5056e679Gabriel Peal            }
550f0593bc17b61c872ae2d7705fb598c5e5056e679Gabriel Peal            try {
551f0593bc17b61c872ae2d7705fb598c5e5056e679Gabriel Peal                mSessionBinder.playFromSearch(query, extras);
552f0593bc17b61c872ae2d7705fb598c5e5056e679Gabriel Peal            } catch (RemoteException e) {
553f0593bc17b61c872ae2d7705fb598c5e5056e679Gabriel Peal                Log.wtf(TAG, "Error calling play(" + query + ").", e);
554f0593bc17b61c872ae2d7705fb598c5e5056e679Gabriel Peal            }
555f0593bc17b61c872ae2d7705fb598c5e5056e679Gabriel Peal        }
556f0593bc17b61c872ae2d7705fb598c5e5056e679Gabriel Peal
557f0593bc17b61c872ae2d7705fb598c5e5056e679Gabriel Peal        /**
558f0593bc17b61c872ae2d7705fb598c5e5056e679Gabriel Peal         * Play a track with a specific id in the play queue.
559f0593bc17b61c872ae2d7705fb598c5e5056e679Gabriel Peal         * If you specify an id that is not in the play queue, the behavior is undefined.
560f0593bc17b61c872ae2d7705fb598c5e5056e679Gabriel Peal         */
561f0593bc17b61c872ae2d7705fb598c5e5056e679Gabriel Peal        public void skipToTrack(long id) {
562f0593bc17b61c872ae2d7705fb598c5e5056e679Gabriel Peal            try {
563f0593bc17b61c872ae2d7705fb598c5e5056e679Gabriel Peal                mSessionBinder.skipToTrack(id);
564f0593bc17b61c872ae2d7705fb598c5e5056e679Gabriel Peal            } catch (RemoteException e) {
565f0593bc17b61c872ae2d7705fb598c5e5056e679Gabriel Peal                Log.wtf(TAG, "Error calling skipToTrack(" + id + ").", e);
566f0593bc17b61c872ae2d7705fb598c5e5056e679Gabriel Peal            }
567f0593bc17b61c872ae2d7705fb598c5e5056e679Gabriel Peal        }
568f0593bc17b61c872ae2d7705fb598c5e5056e679Gabriel Peal
569f0593bc17b61c872ae2d7705fb598c5e5056e679Gabriel Peal        /**
570c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik         * Request that the player pause its playback and stay at its current
571c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik         * position.
572c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik         */
573c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik        public void pause() {
574c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik            try {
575c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik                mSessionBinder.pause();
576c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik            } catch (RemoteException e) {
577c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik                Log.wtf(TAG, "Error calling pause.", e);
578c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik            }
579c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik        }
580c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik
581c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik        /**
582c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik         * Request that the player stop its playback; it may clear its state in
583c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik         * whatever way is appropriate.
584c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik         */
585c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik        public void stop() {
586c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik            try {
587c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik                mSessionBinder.stop();
588c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik            } catch (RemoteException e) {
589c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik                Log.wtf(TAG, "Error calling stop.", e);
590c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik            }
591c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik        }
592c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik
593c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik        /**
594c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik         * Move to a new location in the media stream.
595c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik         *
596c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik         * @param pos Position to move to, in milliseconds.
597c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik         */
598c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik        public void seekTo(long pos) {
599c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik            try {
600c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik                mSessionBinder.seekTo(pos);
601c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik            } catch (RemoteException e) {
602c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik                Log.wtf(TAG, "Error calling seekTo.", e);
603c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik            }
604c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik        }
605c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik
606c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik        /**
607c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik         * Start fast forwarding. If playback is already fast forwarding this
608c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik         * may increase the rate.
609c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik         */
610c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik        public void fastForward() {
611c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik            try {
612c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik                mSessionBinder.fastForward();
613c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik            } catch (RemoteException e) {
614c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik                Log.wtf(TAG, "Error calling fastForward.", e);
615c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik            }
616c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik        }
617c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik
618c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik        /**
619c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik         * Skip to the next item.
620c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik         */
621c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik        public void skipToNext() {
622c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik            try {
623c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik                mSessionBinder.next();
624c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik            } catch (RemoteException e) {
625c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik                Log.wtf(TAG, "Error calling next.", e);
626c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik            }
627c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik        }
628c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik
629c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik        /**
630c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik         * Start rewinding. If playback is already rewinding this may increase
631c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik         * the rate.
632c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik         */
633c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik        public void rewind() {
634c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik            try {
635c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik                mSessionBinder.rewind();
636c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik            } catch (RemoteException e) {
637c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik                Log.wtf(TAG, "Error calling rewind.", e);
638c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik            }
639c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik        }
640c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik
641c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik        /**
642c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik         * Skip to the previous item.
643c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik         */
644c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik        public void skipToPrevious() {
645c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik            try {
646c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik                mSessionBinder.previous();
647c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik            } catch (RemoteException e) {
648c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik                Log.wtf(TAG, "Error calling previous.", e);
649c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik            }
650c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik        }
651c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik
652c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik        /**
653c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik         * Rate the current content. This will cause the rating to be set for
654c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik         * the current user. The Rating type must match the type returned by
655c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik         * {@link #getRatingType()}.
656c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik         *
657c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik         * @param rating The rating to set for the current content
658c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik         */
659c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik        public void setRating(Rating rating) {
660c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik            try {
661c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik                mSessionBinder.rate(rating);
662c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik            } catch (RemoteException e) {
663c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik                Log.wtf(TAG, "Error calling rate.", e);
664c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik            }
665c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik        }
666f364f944962c4ec66f5e5b33dafe8480f38f6db6Gabriel Peal
667f364f944962c4ec66f5e5b33dafe8480f38f6db6Gabriel Peal        /**
668f364f944962c4ec66f5e5b33dafe8480f38f6db6Gabriel Peal         * Send a custom action back for the {@link MediaSession} to perform.
669f364f944962c4ec66f5e5b33dafe8480f38f6db6Gabriel Peal         *
670f364f944962c4ec66f5e5b33dafe8480f38f6db6Gabriel Peal         * @param customAction The action to perform.
671f364f944962c4ec66f5e5b33dafe8480f38f6db6Gabriel Peal         * @param args Optional arguments to supply to the {@link MediaSession} for this
672f364f944962c4ec66f5e5b33dafe8480f38f6db6Gabriel Peal         *             custom action.
673f364f944962c4ec66f5e5b33dafe8480f38f6db6Gabriel Peal         */
674f364f944962c4ec66f5e5b33dafe8480f38f6db6Gabriel Peal        public void sendCustomAction(@NonNull PlaybackState.CustomAction customAction,
675f364f944962c4ec66f5e5b33dafe8480f38f6db6Gabriel Peal                    @Nullable Bundle args) {
676f364f944962c4ec66f5e5b33dafe8480f38f6db6Gabriel Peal            if (customAction == null) {
677f364f944962c4ec66f5e5b33dafe8480f38f6db6Gabriel Peal                throw new IllegalArgumentException("CustomAction cannot be null.");
678f364f944962c4ec66f5e5b33dafe8480f38f6db6Gabriel Peal            }
679f364f944962c4ec66f5e5b33dafe8480f38f6db6Gabriel Peal            sendCustomAction(customAction.getAction(), args);
680f364f944962c4ec66f5e5b33dafe8480f38f6db6Gabriel Peal        }
681f364f944962c4ec66f5e5b33dafe8480f38f6db6Gabriel Peal
682f364f944962c4ec66f5e5b33dafe8480f38f6db6Gabriel Peal        /**
683f364f944962c4ec66f5e5b33dafe8480f38f6db6Gabriel Peal         * Send the id and args from a custom action back for the {@link MediaSession} to perform.
684f364f944962c4ec66f5e5b33dafe8480f38f6db6Gabriel Peal         *
685f364f944962c4ec66f5e5b33dafe8480f38f6db6Gabriel Peal         * @see #sendCustomAction(PlaybackState.CustomAction action, Bundle args)
686f364f944962c4ec66f5e5b33dafe8480f38f6db6Gabriel Peal         * @param action The action identifier of the {@link PlaybackState.CustomAction} as
687f364f944962c4ec66f5e5b33dafe8480f38f6db6Gabriel Peal         *               specified by the {@link MediaSession}.
688f364f944962c4ec66f5e5b33dafe8480f38f6db6Gabriel Peal         * @param args Optional arguments to supply to the {@link MediaSession} for this
689f364f944962c4ec66f5e5b33dafe8480f38f6db6Gabriel Peal         *             custom action.
690f364f944962c4ec66f5e5b33dafe8480f38f6db6Gabriel Peal         */
691f364f944962c4ec66f5e5b33dafe8480f38f6db6Gabriel Peal        public void sendCustomAction(@NonNull String action, @Nullable Bundle args) {
692f364f944962c4ec66f5e5b33dafe8480f38f6db6Gabriel Peal            if (TextUtils.isEmpty(action)) {
693f364f944962c4ec66f5e5b33dafe8480f38f6db6Gabriel Peal                throw new IllegalArgumentException("CustomAction cannot be null.");
694f364f944962c4ec66f5e5b33dafe8480f38f6db6Gabriel Peal            }
695f364f944962c4ec66f5e5b33dafe8480f38f6db6Gabriel Peal            try {
696f364f944962c4ec66f5e5b33dafe8480f38f6db6Gabriel Peal                mSessionBinder.sendCustomAction(action, args);
697f364f944962c4ec66f5e5b33dafe8480f38f6db6Gabriel Peal            } catch (RemoteException e) {
698f364f944962c4ec66f5e5b33dafe8480f38f6db6Gabriel Peal                Log.d(TAG, "Dead object in sendCustomAction.", e);
699f364f944962c4ec66f5e5b33dafe8480f38f6db6Gabriel Peal            }
700f364f944962c4ec66f5e5b33dafe8480f38f6db6Gabriel Peal        }
7018ae0f34db936a649ddaf9cdd086c224f6514efebRoboErik    }
70201fe661ae5da3739215d93922412df4b24c859a2RoboErik
703ef3c9e9b057a5aac2d0d012e8e6385660478e203RoboErik    /**
704ef3c9e9b057a5aac2d0d012e8e6385660478e203RoboErik     * Holds information about the way volume is handled for this session.
705ef3c9e9b057a5aac2d0d012e8e6385660478e203RoboErik     */
706ef3c9e9b057a5aac2d0d012e8e6385660478e203RoboErik    public static final class VolumeInfo {
707ef3c9e9b057a5aac2d0d012e8e6385660478e203RoboErik        private final int mVolumeType;
708ef3c9e9b057a5aac2d0d012e8e6385660478e203RoboErik        private final int mVolumeControl;
709ef3c9e9b057a5aac2d0d012e8e6385660478e203RoboErik        private final int mMaxVolume;
710ef3c9e9b057a5aac2d0d012e8e6385660478e203RoboErik        private final int mCurrentVolume;
7119db9bf7034d7dcdf596dc22d521b18975d0dd2b9RoboErik        private final AudioAttributes mAudioAttrs;
712ef3c9e9b057a5aac2d0d012e8e6385660478e203RoboErik
713ef3c9e9b057a5aac2d0d012e8e6385660478e203RoboErik        /**
714ef3c9e9b057a5aac2d0d012e8e6385660478e203RoboErik         * @hide
715ef3c9e9b057a5aac2d0d012e8e6385660478e203RoboErik         */
7169db9bf7034d7dcdf596dc22d521b18975d0dd2b9RoboErik        public VolumeInfo(int type, AudioAttributes attrs, int control, int max, int current) {
717ef3c9e9b057a5aac2d0d012e8e6385660478e203RoboErik            mVolumeType = type;
7189db9bf7034d7dcdf596dc22d521b18975d0dd2b9RoboErik            mAudioAttrs = attrs;
719ef3c9e9b057a5aac2d0d012e8e6385660478e203RoboErik            mVolumeControl = control;
720ef3c9e9b057a5aac2d0d012e8e6385660478e203RoboErik            mMaxVolume = max;
721ef3c9e9b057a5aac2d0d012e8e6385660478e203RoboErik            mCurrentVolume = current;
722ef3c9e9b057a5aac2d0d012e8e6385660478e203RoboErik        }
723ef3c9e9b057a5aac2d0d012e8e6385660478e203RoboErik
724ef3c9e9b057a5aac2d0d012e8e6385660478e203RoboErik        /**
725ef3c9e9b057a5aac2d0d012e8e6385660478e203RoboErik         * Get the type of volume handling, either local or remote. One of:
726ef3c9e9b057a5aac2d0d012e8e6385660478e203RoboErik         * <ul>
72719c9518f6a817d53d5234de0020313cab6950b2fRoboErik         * <li>{@link MediaSession#PLAYBACK_TYPE_LOCAL}</li>
72819c9518f6a817d53d5234de0020313cab6950b2fRoboErik         * <li>{@link MediaSession#PLAYBACK_TYPE_REMOTE}</li>
729ef3c9e9b057a5aac2d0d012e8e6385660478e203RoboErik         * </ul>
730ef3c9e9b057a5aac2d0d012e8e6385660478e203RoboErik         *
731ef3c9e9b057a5aac2d0d012e8e6385660478e203RoboErik         * @return The type of volume handling this session is using.
732ef3c9e9b057a5aac2d0d012e8e6385660478e203RoboErik         */
733ef3c9e9b057a5aac2d0d012e8e6385660478e203RoboErik        public int getVolumeType() {
734ef3c9e9b057a5aac2d0d012e8e6385660478e203RoboErik            return mVolumeType;
735ef3c9e9b057a5aac2d0d012e8e6385660478e203RoboErik        }
736ef3c9e9b057a5aac2d0d012e8e6385660478e203RoboErik
737ef3c9e9b057a5aac2d0d012e8e6385660478e203RoboErik        /**
7389db9bf7034d7dcdf596dc22d521b18975d0dd2b9RoboErik         * Get the audio attributes for this session. The attributes will affect
7399db9bf7034d7dcdf596dc22d521b18975d0dd2b9RoboErik         * volume handling for the session. When the volume type is
7409db9bf7034d7dcdf596dc22d521b18975d0dd2b9RoboErik         * {@link MediaSession#PLAYBACK_TYPE_REMOTE} these may be ignored by the
7419db9bf7034d7dcdf596dc22d521b18975d0dd2b9RoboErik         * remote volume handler.
742ef3c9e9b057a5aac2d0d012e8e6385660478e203RoboErik         *
7439db9bf7034d7dcdf596dc22d521b18975d0dd2b9RoboErik         * @return The attributes for this session.
744ef3c9e9b057a5aac2d0d012e8e6385660478e203RoboErik         */
7459db9bf7034d7dcdf596dc22d521b18975d0dd2b9RoboErik        public AudioAttributes getAudioAttributes() {
7469db9bf7034d7dcdf596dc22d521b18975d0dd2b9RoboErik            return mAudioAttrs;
747ef3c9e9b057a5aac2d0d012e8e6385660478e203RoboErik        }
748ef3c9e9b057a5aac2d0d012e8e6385660478e203RoboErik
749ef3c9e9b057a5aac2d0d012e8e6385660478e203RoboErik        /**
750ef3c9e9b057a5aac2d0d012e8e6385660478e203RoboErik         * Get the type of volume control that can be used. One of:
751ef3c9e9b057a5aac2d0d012e8e6385660478e203RoboErik         * <ul>
752ef3c9e9b057a5aac2d0d012e8e6385660478e203RoboErik         * <li>{@link VolumeProvider#VOLUME_CONTROL_ABSOLUTE}</li>
753ef3c9e9b057a5aac2d0d012e8e6385660478e203RoboErik         * <li>{@link VolumeProvider#VOLUME_CONTROL_RELATIVE}</li>
754ef3c9e9b057a5aac2d0d012e8e6385660478e203RoboErik         * <li>{@link VolumeProvider#VOLUME_CONTROL_FIXED}</li>
755ef3c9e9b057a5aac2d0d012e8e6385660478e203RoboErik         * </ul>
756ef3c9e9b057a5aac2d0d012e8e6385660478e203RoboErik         *
757ef3c9e9b057a5aac2d0d012e8e6385660478e203RoboErik         * @return The type of volume control that may be used with this
758ef3c9e9b057a5aac2d0d012e8e6385660478e203RoboErik         *         session.
759ef3c9e9b057a5aac2d0d012e8e6385660478e203RoboErik         */
760ef3c9e9b057a5aac2d0d012e8e6385660478e203RoboErik        public int getVolumeControl() {
761ef3c9e9b057a5aac2d0d012e8e6385660478e203RoboErik            return mVolumeControl;
762ef3c9e9b057a5aac2d0d012e8e6385660478e203RoboErik        }
763ef3c9e9b057a5aac2d0d012e8e6385660478e203RoboErik
764ef3c9e9b057a5aac2d0d012e8e6385660478e203RoboErik        /**
765ef3c9e9b057a5aac2d0d012e8e6385660478e203RoboErik         * Get the maximum volume that may be set for this session.
766ef3c9e9b057a5aac2d0d012e8e6385660478e203RoboErik         *
767ef3c9e9b057a5aac2d0d012e8e6385660478e203RoboErik         * @return The maximum allowed volume where this session is playing.
768ef3c9e9b057a5aac2d0d012e8e6385660478e203RoboErik         */
769ef3c9e9b057a5aac2d0d012e8e6385660478e203RoboErik        public int getMaxVolume() {
770ef3c9e9b057a5aac2d0d012e8e6385660478e203RoboErik            return mMaxVolume;
771ef3c9e9b057a5aac2d0d012e8e6385660478e203RoboErik        }
772ef3c9e9b057a5aac2d0d012e8e6385660478e203RoboErik
773ef3c9e9b057a5aac2d0d012e8e6385660478e203RoboErik        /**
774ef3c9e9b057a5aac2d0d012e8e6385660478e203RoboErik         * Get the current volume for this session.
775ef3c9e9b057a5aac2d0d012e8e6385660478e203RoboErik         *
776ef3c9e9b057a5aac2d0d012e8e6385660478e203RoboErik         * @return The current volume where this session is playing.
777ef3c9e9b057a5aac2d0d012e8e6385660478e203RoboErik         */
778ef3c9e9b057a5aac2d0d012e8e6385660478e203RoboErik        public int getCurrentVolume() {
779ef3c9e9b057a5aac2d0d012e8e6385660478e203RoboErik            return mCurrentVolume;
780ef3c9e9b057a5aac2d0d012e8e6385660478e203RoboErik        }
781ef3c9e9b057a5aac2d0d012e8e6385660478e203RoboErik    }
782ef3c9e9b057a5aac2d0d012e8e6385660478e203RoboErik
78307c7077c54717dbbf2c401ea32d00fa6df6d77c6RoboErik    private final static class CallbackStub extends ISessionControllerCallback.Stub {
78442ea7eecd149161ed192d3029f0d77d1d08a4aa5RoboErik        private final WeakReference<MediaController> mController;
78501fe661ae5da3739215d93922412df4b24c859a2RoboErik
78642ea7eecd149161ed192d3029f0d77d1d08a4aa5RoboErik        public CallbackStub(MediaController controller) {
78742ea7eecd149161ed192d3029f0d77d1d08a4aa5RoboErik            mController = new WeakReference<MediaController>(controller);
78801fe661ae5da3739215d93922412df4b24c859a2RoboErik        }
78901fe661ae5da3739215d93922412df4b24c859a2RoboErik
79001fe661ae5da3739215d93922412df4b24c859a2RoboErik        @Override
7918ae0f34db936a649ddaf9cdd086c224f6514efebRoboErik        public void onEvent(String event, Bundle extras) {
79242ea7eecd149161ed192d3029f0d77d1d08a4aa5RoboErik            MediaController controller = mController.get();
7938ae0f34db936a649ddaf9cdd086c224f6514efebRoboErik            if (controller != null) {
794c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik                controller.postMessage(MSG_EVENT, event, extras);
79501fe661ae5da3739215d93922412df4b24c859a2RoboErik            }
79601fe661ae5da3739215d93922412df4b24c859a2RoboErik        }
79701fe661ae5da3739215d93922412df4b24c859a2RoboErik
79801fe661ae5da3739215d93922412df4b24c859a2RoboErik        @Override
7998ae0f34db936a649ddaf9cdd086c224f6514efebRoboErik        public void onPlaybackStateChanged(PlaybackState state) {
80042ea7eecd149161ed192d3029f0d77d1d08a4aa5RoboErik            MediaController controller = mController.get();
8018ae0f34db936a649ddaf9cdd086c224f6514efebRoboErik            if (controller != null) {
802c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik                controller.postMessage(MSG_UPDATE_PLAYBACK_STATE, state, null);
80301fe661ae5da3739215d93922412df4b24c859a2RoboErik            }
80401fe661ae5da3739215d93922412df4b24c859a2RoboErik        }
80501fe661ae5da3739215d93922412df4b24c859a2RoboErik
80601fe661ae5da3739215d93922412df4b24c859a2RoboErik        @Override
8078ae0f34db936a649ddaf9cdd086c224f6514efebRoboErik        public void onMetadataChanged(MediaMetadata metadata) {
80842ea7eecd149161ed192d3029f0d77d1d08a4aa5RoboErik            MediaController controller = mController.get();
8098ae0f34db936a649ddaf9cdd086c224f6514efebRoboErik            if (controller != null) {
810c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik                controller.postMessage(MSG_UPDATE_METADATA, metadata, null);
81101fe661ae5da3739215d93922412df4b24c859a2RoboErik            }
81201fe661ae5da3739215d93922412df4b24c859a2RoboErik        }
81301fe661ae5da3739215d93922412df4b24c859a2RoboErik
81419c9518f6a817d53d5234de0020313cab6950b2fRoboErik        @Override
815f0593bc17b61c872ae2d7705fb598c5e5056e679Gabriel Peal        public void onQueueChanged(ParceledListSlice parceledQueue) {
816f0593bc17b61c872ae2d7705fb598c5e5056e679Gabriel Peal            List<MediaSession.Track> queue = parceledQueue.getList();
817f0593bc17b61c872ae2d7705fb598c5e5056e679Gabriel Peal            MediaController controller = mController.get();
818f0593bc17b61c872ae2d7705fb598c5e5056e679Gabriel Peal            if (controller != null) {
819f0593bc17b61c872ae2d7705fb598c5e5056e679Gabriel Peal                controller.postMessage(MSG_UPDATE_QUEUE, queue, null);
820f0593bc17b61c872ae2d7705fb598c5e5056e679Gabriel Peal            }
821f0593bc17b61c872ae2d7705fb598c5e5056e679Gabriel Peal        }
822f0593bc17b61c872ae2d7705fb598c5e5056e679Gabriel Peal
823f0593bc17b61c872ae2d7705fb598c5e5056e679Gabriel Peal        @Override
824f0593bc17b61c872ae2d7705fb598c5e5056e679Gabriel Peal        public void onQueueTitleChanged(CharSequence title) {
825f0593bc17b61c872ae2d7705fb598c5e5056e679Gabriel Peal            MediaController controller = mController.get();
826f0593bc17b61c872ae2d7705fb598c5e5056e679Gabriel Peal            if (controller != null) {
827f0593bc17b61c872ae2d7705fb598c5e5056e679Gabriel Peal                controller.postMessage(MSG_UPDATE_QUEUE_TITLE, title, null);
828f0593bc17b61c872ae2d7705fb598c5e5056e679Gabriel Peal            }
829f0593bc17b61c872ae2d7705fb598c5e5056e679Gabriel Peal        }
830f0593bc17b61c872ae2d7705fb598c5e5056e679Gabriel Peal
831f0593bc17b61c872ae2d7705fb598c5e5056e679Gabriel Peal        @Override
832f0593bc17b61c872ae2d7705fb598c5e5056e679Gabriel Peal        public void onExtrasChanged(Bundle extras) {
833f0593bc17b61c872ae2d7705fb598c5e5056e679Gabriel Peal            MediaController controller = mController.get();
834f0593bc17b61c872ae2d7705fb598c5e5056e679Gabriel Peal            if (controller != null) {
835f0593bc17b61c872ae2d7705fb598c5e5056e679Gabriel Peal                controller.postMessage(MSG_UPDATE_EXTRAS, extras, null);
836f0593bc17b61c872ae2d7705fb598c5e5056e679Gabriel Peal            }
837f0593bc17b61c872ae2d7705fb598c5e5056e679Gabriel Peal        }
838f0593bc17b61c872ae2d7705fb598c5e5056e679Gabriel Peal
839f0593bc17b61c872ae2d7705fb598c5e5056e679Gabriel Peal        @Override
84019c9518f6a817d53d5234de0020313cab6950b2fRoboErik        public void onVolumeInfoChanged(ParcelableVolumeInfo pvi) {
84119c9518f6a817d53d5234de0020313cab6950b2fRoboErik            MediaController controller = mController.get();
84219c9518f6a817d53d5234de0020313cab6950b2fRoboErik            if (controller != null) {
8439db9bf7034d7dcdf596dc22d521b18975d0dd2b9RoboErik                VolumeInfo info = new VolumeInfo(pvi.volumeType, pvi.audioAttrs, pvi.controlType,
84419c9518f6a817d53d5234de0020313cab6950b2fRoboErik                        pvi.maxVolume, pvi.currentVolume);
84519c9518f6a817d53d5234de0020313cab6950b2fRoboErik                controller.postMessage(MSG_UPDATE_VOLUME, info, null);
84619c9518f6a817d53d5234de0020313cab6950b2fRoboErik            }
84719c9518f6a817d53d5234de0020313cab6950b2fRoboErik        }
84819c9518f6a817d53d5234de0020313cab6950b2fRoboErik
84901fe661ae5da3739215d93922412df4b24c859a2RoboErik    }
85001fe661ae5da3739215d93922412df4b24c859a2RoboErik
85101fe661ae5da3739215d93922412df4b24c859a2RoboErik    private final static class MessageHandler extends Handler {
85242ea7eecd149161ed192d3029f0d77d1d08a4aa5RoboErik        private final MediaController.Callback mCallback;
85301fe661ae5da3739215d93922412df4b24c859a2RoboErik
85442ea7eecd149161ed192d3029f0d77d1d08a4aa5RoboErik        public MessageHandler(Looper looper, MediaController.Callback cb) {
8558ae0f34db936a649ddaf9cdd086c224f6514efebRoboErik            super(looper, null, true);
8568ae0f34db936a649ddaf9cdd086c224f6514efebRoboErik            mCallback = cb;
85701fe661ae5da3739215d93922412df4b24c859a2RoboErik        }
85801fe661ae5da3739215d93922412df4b24c859a2RoboErik
85901fe661ae5da3739215d93922412df4b24c859a2RoboErik        @Override
86001fe661ae5da3739215d93922412df4b24c859a2RoboErik        public void handleMessage(Message msg) {
86101fe661ae5da3739215d93922412df4b24c859a2RoboErik            switch (msg.what) {
8628ae0f34db936a649ddaf9cdd086c224f6514efebRoboErik                case MSG_EVENT:
86379fa4630bbca7c6c251eea99fe8997e4b45beceeRoboErik                    mCallback.onSessionEvent((String) msg.obj, msg.getData());
86401fe661ae5da3739215d93922412df4b24c859a2RoboErik                    break;
865c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik                case MSG_UPDATE_PLAYBACK_STATE:
866c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik                    mCallback.onPlaybackStateChanged((PlaybackState) msg.obj);
867c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik                    break;
868c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik                case MSG_UPDATE_METADATA:
869c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik                    mCallback.onMetadataChanged((MediaMetadata) msg.obj);
870c47fa84b0a6bda48c38ba8822481ce613bafd019RoboErik                    break;
871f0593bc17b61c872ae2d7705fb598c5e5056e679Gabriel Peal                case MSG_UPDATE_QUEUE:
872f0593bc17b61c872ae2d7705fb598c5e5056e679Gabriel Peal                    mCallback.onQueueChanged((List<MediaSession.Track>) msg.obj);
873f0593bc17b61c872ae2d7705fb598c5e5056e679Gabriel Peal                    break;
874f0593bc17b61c872ae2d7705fb598c5e5056e679Gabriel Peal                case MSG_UPDATE_QUEUE_TITLE:
875f0593bc17b61c872ae2d7705fb598c5e5056e679Gabriel Peal                    mCallback.onQueueTitleChanged((CharSequence) msg.obj);
876f0593bc17b61c872ae2d7705fb598c5e5056e679Gabriel Peal                    break;
877f0593bc17b61c872ae2d7705fb598c5e5056e679Gabriel Peal                case MSG_UPDATE_EXTRAS:
878f0593bc17b61c872ae2d7705fb598c5e5056e679Gabriel Peal                    mCallback.onExtrasChanged((Bundle) msg.obj);
879f0593bc17b61c872ae2d7705fb598c5e5056e679Gabriel Peal                    break;
88019c9518f6a817d53d5234de0020313cab6950b2fRoboErik                case MSG_UPDATE_VOLUME:
88119c9518f6a817d53d5234de0020313cab6950b2fRoboErik                    mCallback.onVolumeInfoChanged((VolumeInfo) msg.obj);
88219c9518f6a817d53d5234de0020313cab6950b2fRoboErik                    break;
88301fe661ae5da3739215d93922412df4b24c859a2RoboErik            }
88401fe661ae5da3739215d93922412df4b24c859a2RoboErik        }
8858ae0f34db936a649ddaf9cdd086c224f6514efebRoboErik
8868ae0f34db936a649ddaf9cdd086c224f6514efebRoboErik        public void post(int what, Object obj, Bundle data) {
8878ae0f34db936a649ddaf9cdd086c224f6514efebRoboErik            obtainMessage(what, obj).sendToTarget();
8888ae0f34db936a649ddaf9cdd086c224f6514efebRoboErik        }
88901fe661ae5da3739215d93922412df4b24c859a2RoboErik    }
89001fe661ae5da3739215d93922412df4b24c859a2RoboErik
89101fe661ae5da3739215d93922412df4b24c859a2RoboErik}
892