1a97f1edf7624785c41ec0bfec6fd12c2388d9234Jeff Brown/*
2a97f1edf7624785c41ec0bfec6fd12c2388d9234Jeff Brown * Copyright (C) 2013 The Android Open Source Project
3a97f1edf7624785c41ec0bfec6fd12c2388d9234Jeff Brown *
4a97f1edf7624785c41ec0bfec6fd12c2388d9234Jeff Brown * Licensed under the Apache License, Version 2.0 (the "License");
5a97f1edf7624785c41ec0bfec6fd12c2388d9234Jeff Brown * you may not use this file except in compliance with the License.
6a97f1edf7624785c41ec0bfec6fd12c2388d9234Jeff Brown * You may obtain a copy of the License at
7a97f1edf7624785c41ec0bfec6fd12c2388d9234Jeff Brown *
8a97f1edf7624785c41ec0bfec6fd12c2388d9234Jeff Brown *      http://www.apache.org/licenses/LICENSE-2.0
9a97f1edf7624785c41ec0bfec6fd12c2388d9234Jeff Brown *
10a97f1edf7624785c41ec0bfec6fd12c2388d9234Jeff Brown * Unless required by applicable law or agreed to in writing, software
11a97f1edf7624785c41ec0bfec6fd12c2388d9234Jeff Brown * distributed under the License is distributed on an "AS IS" BASIS,
12a97f1edf7624785c41ec0bfec6fd12c2388d9234Jeff Brown * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13a97f1edf7624785c41ec0bfec6fd12c2388d9234Jeff Brown * See the License for the specific language governing permissions and
14a97f1edf7624785c41ec0bfec6fd12c2388d9234Jeff Brown * limitations under the License.
15a97f1edf7624785c41ec0bfec6fd12c2388d9234Jeff Brown */
16a97f1edf7624785c41ec0bfec6fd12c2388d9234Jeff Brownpackage android.support.v7.media;
17a97f1edf7624785c41ec0bfec6fd12c2388d9234Jeff Brown
18a97f1edf7624785c41ec0bfec6fd12c2388d9234Jeff Brownimport android.app.PendingIntent;
19a97f1edf7624785c41ec0bfec6fd12c2388d9234Jeff Brownimport android.content.BroadcastReceiver;
20a97f1edf7624785c41ec0bfec6fd12c2388d9234Jeff Brownimport android.content.Context;
21a97f1edf7624785c41ec0bfec6fd12c2388d9234Jeff Brownimport android.content.Intent;
22a97f1edf7624785c41ec0bfec6fd12c2388d9234Jeff Brownimport android.content.IntentFilter;
23a97f1edf7624785c41ec0bfec6fd12c2388d9234Jeff Brownimport android.net.Uri;
24a97f1edf7624785c41ec0bfec6fd12c2388d9234Jeff Brownimport android.os.Bundle;
25a97f1edf7624785c41ec0bfec6fd12c2388d9234Jeff Brownimport android.util.Log;
26a97f1edf7624785c41ec0bfec6fd12c2388d9234Jeff Brown
27a97f1edf7624785c41ec0bfec6fd12c2388d9234Jeff Brown/**
28a97f1edf7624785c41ec0bfec6fd12c2388d9234Jeff Brown * A helper class for playing media on remote routes using the remote playback protocol
29a97f1edf7624785c41ec0bfec6fd12c2388d9234Jeff Brown * defined by {@link MediaControlIntent}.
30a97f1edf7624785c41ec0bfec6fd12c2388d9234Jeff Brown * <p>
31a97f1edf7624785c41ec0bfec6fd12c2388d9234Jeff Brown * The client maintains session state and offers a simplified interface for issuing
32a97f1edf7624785c41ec0bfec6fd12c2388d9234Jeff Brown * remote playback media control intents to a single route.
33a97f1edf7624785c41ec0bfec6fd12c2388d9234Jeff Brown * </p>
34a97f1edf7624785c41ec0bfec6fd12c2388d9234Jeff Brown */
35a97f1edf7624785c41ec0bfec6fd12c2388d9234Jeff Brownpublic class RemotePlaybackClient {
36a97f1edf7624785c41ec0bfec6fd12c2388d9234Jeff Brown    private static final String TAG = "RemotePlaybackClient";
37a97f1edf7624785c41ec0bfec6fd12c2388d9234Jeff Brown    private static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG);
38a97f1edf7624785c41ec0bfec6fd12c2388d9234Jeff Brown
39a97f1edf7624785c41ec0bfec6fd12c2388d9234Jeff Brown    private final Context mContext;
40a97f1edf7624785c41ec0bfec6fd12c2388d9234Jeff Brown    private final MediaRouter.RouteInfo mRoute;
41a97f1edf7624785c41ec0bfec6fd12c2388d9234Jeff Brown    private final StatusReceiver mStatusReceiver;
42a97f1edf7624785c41ec0bfec6fd12c2388d9234Jeff Brown    private final PendingIntent mItemStatusPendingIntent;
43a97f1edf7624785c41ec0bfec6fd12c2388d9234Jeff Brown    private final PendingIntent mSessionStatusPendingIntent;
44a97f1edf7624785c41ec0bfec6fd12c2388d9234Jeff Brown
45a97f1edf7624785c41ec0bfec6fd12c2388d9234Jeff Brown    private boolean mRouteSupportsRemotePlayback;
46a97f1edf7624785c41ec0bfec6fd12c2388d9234Jeff Brown    private boolean mRouteSupportsQueuing;
47a97f1edf7624785c41ec0bfec6fd12c2388d9234Jeff Brown    private boolean mRouteSupportsSessionManagement;
48a97f1edf7624785c41ec0bfec6fd12c2388d9234Jeff Brown
49a97f1edf7624785c41ec0bfec6fd12c2388d9234Jeff Brown    private String mSessionId;
50a97f1edf7624785c41ec0bfec6fd12c2388d9234Jeff Brown    private StatusCallback mStatusCallback;
51a97f1edf7624785c41ec0bfec6fd12c2388d9234Jeff Brown
52a97f1edf7624785c41ec0bfec6fd12c2388d9234Jeff Brown    /**
53a97f1edf7624785c41ec0bfec6fd12c2388d9234Jeff Brown     * Creates a remote playback client for a route.
54a97f1edf7624785c41ec0bfec6fd12c2388d9234Jeff Brown     *
55a97f1edf7624785c41ec0bfec6fd12c2388d9234Jeff Brown     * @param route The media route.
56a97f1edf7624785c41ec0bfec6fd12c2388d9234Jeff Brown     */
57a97f1edf7624785c41ec0bfec6fd12c2388d9234Jeff Brown    public RemotePlaybackClient(Context context, MediaRouter.RouteInfo route) {
58a97f1edf7624785c41ec0bfec6fd12c2388d9234Jeff Brown        if (context == null) {
59a97f1edf7624785c41ec0bfec6fd12c2388d9234Jeff Brown            throw new IllegalArgumentException("context must not be null");
60a97f1edf7624785c41ec0bfec6fd12c2388d9234Jeff Brown        }
61a97f1edf7624785c41ec0bfec6fd12c2388d9234Jeff Brown        if (route == null) {
62a97f1edf7624785c41ec0bfec6fd12c2388d9234Jeff Brown            throw new IllegalArgumentException("route must not be null");
63a97f1edf7624785c41ec0bfec6fd12c2388d9234Jeff Brown        }
64a97f1edf7624785c41ec0bfec6fd12c2388d9234Jeff Brown
65a97f1edf7624785c41ec0bfec6fd12c2388d9234Jeff Brown        mContext = context;
66a97f1edf7624785c41ec0bfec6fd12c2388d9234Jeff Brown        mRoute = route;
67a97f1edf7624785c41ec0bfec6fd12c2388d9234Jeff Brown
68a97f1edf7624785c41ec0bfec6fd12c2388d9234Jeff Brown        IntentFilter statusFilter = new IntentFilter();
69a97f1edf7624785c41ec0bfec6fd12c2388d9234Jeff Brown        statusFilter.addAction(StatusReceiver.ACTION_ITEM_STATUS_CHANGED);
70a97f1edf7624785c41ec0bfec6fd12c2388d9234Jeff Brown        statusFilter.addAction(StatusReceiver.ACTION_SESSION_STATUS_CHANGED);
71a97f1edf7624785c41ec0bfec6fd12c2388d9234Jeff Brown        mStatusReceiver = new StatusReceiver();
72a97f1edf7624785c41ec0bfec6fd12c2388d9234Jeff Brown        context.registerReceiver(mStatusReceiver, statusFilter);
73a97f1edf7624785c41ec0bfec6fd12c2388d9234Jeff Brown
74a97f1edf7624785c41ec0bfec6fd12c2388d9234Jeff Brown        Intent itemStatusIntent = new Intent(StatusReceiver.ACTION_ITEM_STATUS_CHANGED);
75a97f1edf7624785c41ec0bfec6fd12c2388d9234Jeff Brown        itemStatusIntent.setPackage(context.getPackageName());
76a97f1edf7624785c41ec0bfec6fd12c2388d9234Jeff Brown        mItemStatusPendingIntent = PendingIntent.getBroadcast(
77a97f1edf7624785c41ec0bfec6fd12c2388d9234Jeff Brown                context, 0, itemStatusIntent, 0);
78a97f1edf7624785c41ec0bfec6fd12c2388d9234Jeff Brown
79a97f1edf7624785c41ec0bfec6fd12c2388d9234Jeff Brown        Intent sessionStatusIntent = new Intent(StatusReceiver.ACTION_SESSION_STATUS_CHANGED);
80a97f1edf7624785c41ec0bfec6fd12c2388d9234Jeff Brown        sessionStatusIntent.setPackage(context.getPackageName());
81a97f1edf7624785c41ec0bfec6fd12c2388d9234Jeff Brown        mSessionStatusPendingIntent = PendingIntent.getBroadcast(
82a97f1edf7624785c41ec0bfec6fd12c2388d9234Jeff Brown                context, 0, sessionStatusIntent, 0);
83a97f1edf7624785c41ec0bfec6fd12c2388d9234Jeff Brown
84a97f1edf7624785c41ec0bfec6fd12c2388d9234Jeff Brown        detectFeatures();
85a97f1edf7624785c41ec0bfec6fd12c2388d9234Jeff Brown    }
86a97f1edf7624785c41ec0bfec6fd12c2388d9234Jeff Brown
87a97f1edf7624785c41ec0bfec6fd12c2388d9234Jeff Brown    /**
88a97f1edf7624785c41ec0bfec6fd12c2388d9234Jeff Brown     * Releases resources owned by the client.
89a97f1edf7624785c41ec0bfec6fd12c2388d9234Jeff Brown     */
90a97f1edf7624785c41ec0bfec6fd12c2388d9234Jeff Brown    public void release() {
91a97f1edf7624785c41ec0bfec6fd12c2388d9234Jeff Brown        mContext.unregisterReceiver(mStatusReceiver);
92a97f1edf7624785c41ec0bfec6fd12c2388d9234Jeff Brown    }
93a97f1edf7624785c41ec0bfec6fd12c2388d9234Jeff Brown
94a97f1edf7624785c41ec0bfec6fd12c2388d9234Jeff Brown    /**
95a97f1edf7624785c41ec0bfec6fd12c2388d9234Jeff Brown     * Returns true if the route supports remote playback.
96a97f1edf7624785c41ec0bfec6fd12c2388d9234Jeff Brown     * <p>
97a97f1edf7624785c41ec0bfec6fd12c2388d9234Jeff Brown     * If the route does not support remote playback, then none of the functionality
98a97f1edf7624785c41ec0bfec6fd12c2388d9234Jeff Brown     * offered by the client will be available.
99a97f1edf7624785c41ec0bfec6fd12c2388d9234Jeff Brown     * </p><p>
100a97f1edf7624785c41ec0bfec6fd12c2388d9234Jeff Brown     * This method returns true if the route supports all of the following
101a97f1edf7624785c41ec0bfec6fd12c2388d9234Jeff Brown     * actions: {@link MediaControlIntent#ACTION_PLAY play},
102a97f1edf7624785c41ec0bfec6fd12c2388d9234Jeff Brown     * {@link MediaControlIntent#ACTION_SEEK seek},
103a97f1edf7624785c41ec0bfec6fd12c2388d9234Jeff Brown     * {@link MediaControlIntent#ACTION_GET_STATUS get status},
104a97f1edf7624785c41ec0bfec6fd12c2388d9234Jeff Brown     * {@link MediaControlIntent#ACTION_PAUSE pause},
105a97f1edf7624785c41ec0bfec6fd12c2388d9234Jeff Brown     * {@link MediaControlIntent#ACTION_RESUME resume},
106a97f1edf7624785c41ec0bfec6fd12c2388d9234Jeff Brown     * {@link MediaControlIntent#ACTION_STOP stop}.
107a97f1edf7624785c41ec0bfec6fd12c2388d9234Jeff Brown     * </p>
108a97f1edf7624785c41ec0bfec6fd12c2388d9234Jeff Brown     *
109a97f1edf7624785c41ec0bfec6fd12c2388d9234Jeff Brown     * @return True if remote playback is supported.
110a97f1edf7624785c41ec0bfec6fd12c2388d9234Jeff Brown     */
111a97f1edf7624785c41ec0bfec6fd12c2388d9234Jeff Brown    public boolean isRemotePlaybackSupported() {
112a97f1edf7624785c41ec0bfec6fd12c2388d9234Jeff Brown        return mRouteSupportsRemotePlayback;
113a97f1edf7624785c41ec0bfec6fd12c2388d9234Jeff Brown    }
114a97f1edf7624785c41ec0bfec6fd12c2388d9234Jeff Brown
115a97f1edf7624785c41ec0bfec6fd12c2388d9234Jeff Brown    /**
116a97f1edf7624785c41ec0bfec6fd12c2388d9234Jeff Brown     * Returns true if the route supports queuing features.
117a97f1edf7624785c41ec0bfec6fd12c2388d9234Jeff Brown     * <p>
118a97f1edf7624785c41ec0bfec6fd12c2388d9234Jeff Brown     * If the route does not support queuing, then at most one media item can be played
119a97f1edf7624785c41ec0bfec6fd12c2388d9234Jeff Brown     * at a time and the {@link #enqueue} method will not be available.
120a97f1edf7624785c41ec0bfec6fd12c2388d9234Jeff Brown     * </p><p>
121a97f1edf7624785c41ec0bfec6fd12c2388d9234Jeff Brown     * This method returns true if the route supports all of the basic remote playback
122a97f1edf7624785c41ec0bfec6fd12c2388d9234Jeff Brown     * actions and all of the following actions:
123a97f1edf7624785c41ec0bfec6fd12c2388d9234Jeff Brown     * {@link MediaControlIntent#ACTION_ENQUEUE enqueue},
124a97f1edf7624785c41ec0bfec6fd12c2388d9234Jeff Brown     * {@link MediaControlIntent#ACTION_REMOVE remove}.
125a97f1edf7624785c41ec0bfec6fd12c2388d9234Jeff Brown     * </p>
126a97f1edf7624785c41ec0bfec6fd12c2388d9234Jeff Brown     *
127a97f1edf7624785c41ec0bfec6fd12c2388d9234Jeff Brown     * @return True if queuing is supported.  Implies {@link #isRemotePlaybackSupported}
128a97f1edf7624785c41ec0bfec6fd12c2388d9234Jeff Brown     * is also true.
129a97f1edf7624785c41ec0bfec6fd12c2388d9234Jeff Brown     *
130a97f1edf7624785c41ec0bfec6fd12c2388d9234Jeff Brown     * @see #isRemotePlaybackSupported
131a97f1edf7624785c41ec0bfec6fd12c2388d9234Jeff Brown     */
132a97f1edf7624785c41ec0bfec6fd12c2388d9234Jeff Brown    public boolean isQueuingSupported() {
133a97f1edf7624785c41ec0bfec6fd12c2388d9234Jeff Brown        return mRouteSupportsQueuing;
134a97f1edf7624785c41ec0bfec6fd12c2388d9234Jeff Brown    }
135a97f1edf7624785c41ec0bfec6fd12c2388d9234Jeff Brown
136a97f1edf7624785c41ec0bfec6fd12c2388d9234Jeff Brown    /**
137a97f1edf7624785c41ec0bfec6fd12c2388d9234Jeff Brown     * Returns true if the route supports session management features.
138a97f1edf7624785c41ec0bfec6fd12c2388d9234Jeff Brown     * <p>
139a97f1edf7624785c41ec0bfec6fd12c2388d9234Jeff Brown     * If the route does not support session management, then the session will
140a97f1edf7624785c41ec0bfec6fd12c2388d9234Jeff Brown     * not be created until the first media item is played.
141a97f1edf7624785c41ec0bfec6fd12c2388d9234Jeff Brown     * </p><p>
142a97f1edf7624785c41ec0bfec6fd12c2388d9234Jeff Brown     * This method returns true if the route supports all of the basic remote playback
143a97f1edf7624785c41ec0bfec6fd12c2388d9234Jeff Brown     * actions and all of the following actions:
144a97f1edf7624785c41ec0bfec6fd12c2388d9234Jeff Brown     * {@link MediaControlIntent#ACTION_START_SESSION start session},
145a97f1edf7624785c41ec0bfec6fd12c2388d9234Jeff Brown     * {@link MediaControlIntent#ACTION_GET_SESSION_STATUS get session status},
146a97f1edf7624785c41ec0bfec6fd12c2388d9234Jeff Brown     * {@link MediaControlIntent#ACTION_END_SESSION end session}.
147a97f1edf7624785c41ec0bfec6fd12c2388d9234Jeff Brown     * </p>
148a97f1edf7624785c41ec0bfec6fd12c2388d9234Jeff Brown     *
149a97f1edf7624785c41ec0bfec6fd12c2388d9234Jeff Brown     * @return True if session management is supported.
150a97f1edf7624785c41ec0bfec6fd12c2388d9234Jeff Brown     * Implies {@link #isRemotePlaybackSupported} is also true.
151a97f1edf7624785c41ec0bfec6fd12c2388d9234Jeff Brown     *
152a97f1edf7624785c41ec0bfec6fd12c2388d9234Jeff Brown     * @see #isRemotePlaybackSupported
153a97f1edf7624785c41ec0bfec6fd12c2388d9234Jeff Brown     */
154a97f1edf7624785c41ec0bfec6fd12c2388d9234Jeff Brown    public boolean isSessionManagementSupported() {
155a97f1edf7624785c41ec0bfec6fd12c2388d9234Jeff Brown        return mRouteSupportsSessionManagement;
156a97f1edf7624785c41ec0bfec6fd12c2388d9234Jeff Brown    }
157a97f1edf7624785c41ec0bfec6fd12c2388d9234Jeff Brown
158a97f1edf7624785c41ec0bfec6fd12c2388d9234Jeff Brown    /**
159a97f1edf7624785c41ec0bfec6fd12c2388d9234Jeff Brown     * Gets the current session id if there is one.
160a97f1edf7624785c41ec0bfec6fd12c2388d9234Jeff Brown     *
161a97f1edf7624785c41ec0bfec6fd12c2388d9234Jeff Brown     * @return The current session id, or null if none.
162a97f1edf7624785c41ec0bfec6fd12c2388d9234Jeff Brown     */
163a97f1edf7624785c41ec0bfec6fd12c2388d9234Jeff Brown    public String getSessionId() {
164a97f1edf7624785c41ec0bfec6fd12c2388d9234Jeff Brown        return mSessionId;
165a97f1edf7624785c41ec0bfec6fd12c2388d9234Jeff Brown    }
166a97f1edf7624785c41ec0bfec6fd12c2388d9234Jeff Brown
167a97f1edf7624785c41ec0bfec6fd12c2388d9234Jeff Brown    /**
168a97f1edf7624785c41ec0bfec6fd12c2388d9234Jeff Brown     * Sets the current session id.
169a97f1edf7624785c41ec0bfec6fd12c2388d9234Jeff Brown     * <p>
170a97f1edf7624785c41ec0bfec6fd12c2388d9234Jeff Brown     * It is usually not necessary to set the session id explicitly since
171a97f1edf7624785c41ec0bfec6fd12c2388d9234Jeff Brown     * it is created as a side-effect of other requests such as
172a97f1edf7624785c41ec0bfec6fd12c2388d9234Jeff Brown     * {@link #play}, {@link #enqueue}, and {@link #startSession}.
173a97f1edf7624785c41ec0bfec6fd12c2388d9234Jeff Brown     * </p>
174a97f1edf7624785c41ec0bfec6fd12c2388d9234Jeff Brown     *
175a97f1edf7624785c41ec0bfec6fd12c2388d9234Jeff Brown     * @param sessionId The new session id, or null if none.
176a97f1edf7624785c41ec0bfec6fd12c2388d9234Jeff Brown     */
177a97f1edf7624785c41ec0bfec6fd12c2388d9234Jeff Brown    public void setSessionId(String sessionId) {
178a97f1edf7624785c41ec0bfec6fd12c2388d9234Jeff Brown        if (mSessionId != sessionId
179a97f1edf7624785c41ec0bfec6fd12c2388d9234Jeff Brown                && (mSessionId == null || !mSessionId.equals(sessionId))) {
180a97f1edf7624785c41ec0bfec6fd12c2388d9234Jeff Brown            if (DEBUG) {
181a97f1edf7624785c41ec0bfec6fd12c2388d9234Jeff Brown                Log.d(TAG, "Session id is now: " + sessionId);
182a97f1edf7624785c41ec0bfec6fd12c2388d9234Jeff Brown            }
183a97f1edf7624785c41ec0bfec6fd12c2388d9234Jeff Brown            mSessionId = sessionId;
184a97f1edf7624785c41ec0bfec6fd12c2388d9234Jeff Brown            if (mStatusCallback != null) {
185a97f1edf7624785c41ec0bfec6fd12c2388d9234Jeff Brown                mStatusCallback.onSessionChanged(sessionId);
186a97f1edf7624785c41ec0bfec6fd12c2388d9234Jeff Brown            }
187a97f1edf7624785c41ec0bfec6fd12c2388d9234Jeff Brown        }
188a97f1edf7624785c41ec0bfec6fd12c2388d9234Jeff Brown    }
189a97f1edf7624785c41ec0bfec6fd12c2388d9234Jeff Brown
190a97f1edf7624785c41ec0bfec6fd12c2388d9234Jeff Brown    /**
191a97f1edf7624785c41ec0bfec6fd12c2388d9234Jeff Brown     * Returns true if the client currently has a session.
192a97f1edf7624785c41ec0bfec6fd12c2388d9234Jeff Brown     * <p>
193a97f1edf7624785c41ec0bfec6fd12c2388d9234Jeff Brown     * Equivalent to checking whether {@link #getSessionId} returns a non-null result.
194a97f1edf7624785c41ec0bfec6fd12c2388d9234Jeff Brown     * </p>
195a97f1edf7624785c41ec0bfec6fd12c2388d9234Jeff Brown     *
196a97f1edf7624785c41ec0bfec6fd12c2388d9234Jeff Brown     * @return True if there is a current session.
197a97f1edf7624785c41ec0bfec6fd12c2388d9234Jeff Brown     */
198a97f1edf7624785c41ec0bfec6fd12c2388d9234Jeff Brown    public boolean hasSession() {
199a97f1edf7624785c41ec0bfec6fd12c2388d9234Jeff Brown        return mSessionId != null;
200a97f1edf7624785c41ec0bfec6fd12c2388d9234Jeff Brown    }
201a97f1edf7624785c41ec0bfec6fd12c2388d9234Jeff Brown
202a97f1edf7624785c41ec0bfec6fd12c2388d9234Jeff Brown    /**
203a97f1edf7624785c41ec0bfec6fd12c2388d9234Jeff Brown     * Sets a callback that should receive status updates when the state of
204a97f1edf7624785c41ec0bfec6fd12c2388d9234Jeff Brown     * media sessions or media items created by this instance of the remote
205a97f1edf7624785c41ec0bfec6fd12c2388d9234Jeff Brown     * playback client changes.
206a97f1edf7624785c41ec0bfec6fd12c2388d9234Jeff Brown     * <p>
207a97f1edf7624785c41ec0bfec6fd12c2388d9234Jeff Brown     * The callback should be set before the session is created or any play
208a97f1edf7624785c41ec0bfec6fd12c2388d9234Jeff Brown     * commands are issued.
209a97f1edf7624785c41ec0bfec6fd12c2388d9234Jeff Brown     * </p>
210a97f1edf7624785c41ec0bfec6fd12c2388d9234Jeff Brown     *
211a97f1edf7624785c41ec0bfec6fd12c2388d9234Jeff Brown     * @param callback The callback to set.  May be null to remove the previous callback.
212a97f1edf7624785c41ec0bfec6fd12c2388d9234Jeff Brown     */
213a97f1edf7624785c41ec0bfec6fd12c2388d9234Jeff Brown    public void setStatusCallback(StatusCallback callback) {
214a97f1edf7624785c41ec0bfec6fd12c2388d9234Jeff Brown        mStatusCallback = callback;
215a97f1edf7624785c41ec0bfec6fd12c2388d9234Jeff Brown    }
216a97f1edf7624785c41ec0bfec6fd12c2388d9234Jeff Brown
217a97f1edf7624785c41ec0bfec6fd12c2388d9234Jeff Brown    /**
218a97f1edf7624785c41ec0bfec6fd12c2388d9234Jeff Brown     * Sends a request to play a media item.
219a97f1edf7624785c41ec0bfec6fd12c2388d9234Jeff Brown     * <p>
220a97f1edf7624785c41ec0bfec6fd12c2388d9234Jeff Brown     * Clears the queue and starts playing the new item immediately.  If the queue
221a97f1edf7624785c41ec0bfec6fd12c2388d9234Jeff Brown     * was previously paused, then it is resumed as a side-effect of this request.
222a97f1edf7624785c41ec0bfec6fd12c2388d9234Jeff Brown     * </p><p>
223a97f1edf7624785c41ec0bfec6fd12c2388d9234Jeff Brown     * The request is issued in the current session.  If no session is available, then
224a97f1edf7624785c41ec0bfec6fd12c2388d9234Jeff Brown     * one is created implicitly.
225a97f1edf7624785c41ec0bfec6fd12c2388d9234Jeff Brown     * </p><p>
226a97f1edf7624785c41ec0bfec6fd12c2388d9234Jeff Brown     * Please refer to {@link MediaControlIntent#ACTION_PLAY ACTION_PLAY} for
227a97f1edf7624785c41ec0bfec6fd12c2388d9234Jeff Brown     * more information about the semantics of this request.
228a97f1edf7624785c41ec0bfec6fd12c2388d9234Jeff Brown     * </p>
229a97f1edf7624785c41ec0bfec6fd12c2388d9234Jeff Brown     *
230a97f1edf7624785c41ec0bfec6fd12c2388d9234Jeff Brown     * @param contentUri The content Uri to play.
231a97f1edf7624785c41ec0bfec6fd12c2388d9234Jeff Brown     * @param mimeType The mime type of the content, or null if unknown.
232a97f1edf7624785c41ec0bfec6fd12c2388d9234Jeff Brown     * @param positionMillis The initial content position for the item in milliseconds,
233a97f1edf7624785c41ec0bfec6fd12c2388d9234Jeff Brown     * or <code>0</code> to start at the beginning.
234a97f1edf7624785c41ec0bfec6fd12c2388d9234Jeff Brown     * @param metadata The media item metadata bundle, or null if none.
235a97f1edf7624785c41ec0bfec6fd12c2388d9234Jeff Brown     * @param extras A bundle of extra arguments to be added to the
236a97f1edf7624785c41ec0bfec6fd12c2388d9234Jeff Brown     * {@link MediaControlIntent#ACTION_PLAY} intent, or null if none.
237a97f1edf7624785c41ec0bfec6fd12c2388d9234Jeff Brown     * @param callback A callback to invoke when the request has been
238a97f1edf7624785c41ec0bfec6fd12c2388d9234Jeff Brown     * processed, or null if none.
239a97f1edf7624785c41ec0bfec6fd12c2388d9234Jeff Brown     *
240a97f1edf7624785c41ec0bfec6fd12c2388d9234Jeff Brown     * @throws UnsupportedOperationException if the route does not support remote playback.
241a97f1edf7624785c41ec0bfec6fd12c2388d9234Jeff Brown     *
242a97f1edf7624785c41ec0bfec6fd12c2388d9234Jeff Brown     * @see MediaControlIntent#ACTION_PLAY
243a97f1edf7624785c41ec0bfec6fd12c2388d9234Jeff Brown     * @see #isRemotePlaybackSupported
244a97f1edf7624785c41ec0bfec6fd12c2388d9234Jeff Brown     */
245a97f1edf7624785c41ec0bfec6fd12c2388d9234Jeff Brown    public void play(Uri contentUri, String mimeType, Bundle metadata,
246a97f1edf7624785c41ec0bfec6fd12c2388d9234Jeff Brown            long positionMillis, Bundle extras, ItemActionCallback callback) {
247a97f1edf7624785c41ec0bfec6fd12c2388d9234Jeff Brown        playOrEnqueue(contentUri, mimeType, metadata, positionMillis,
248a97f1edf7624785c41ec0bfec6fd12c2388d9234Jeff Brown                extras, callback, MediaControlIntent.ACTION_PLAY);
249a97f1edf7624785c41ec0bfec6fd12c2388d9234Jeff Brown    }
250a97f1edf7624785c41ec0bfec6fd12c2388d9234Jeff Brown
251a97f1edf7624785c41ec0bfec6fd12c2388d9234Jeff Brown    /**
252a97f1edf7624785c41ec0bfec6fd12c2388d9234Jeff Brown     * Sends a request to enqueue a media item.
253a97f1edf7624785c41ec0bfec6fd12c2388d9234Jeff Brown     * <p>
254a97f1edf7624785c41ec0bfec6fd12c2388d9234Jeff Brown     * Enqueues a new item to play.  If the queue was previously paused, then will
255a97f1edf7624785c41ec0bfec6fd12c2388d9234Jeff Brown     * remain paused.
256a97f1edf7624785c41ec0bfec6fd12c2388d9234Jeff Brown     * </p><p>
257a97f1edf7624785c41ec0bfec6fd12c2388d9234Jeff Brown     * The request is issued in the current session.  If no session is available, then
258a97f1edf7624785c41ec0bfec6fd12c2388d9234Jeff Brown     * one is created implicitly.
259a97f1edf7624785c41ec0bfec6fd12c2388d9234Jeff Brown     * </p><p>
260a97f1edf7624785c41ec0bfec6fd12c2388d9234Jeff Brown     * Please refer to {@link MediaControlIntent#ACTION_ENQUEUE ACTION_ENQUEUE} for
261a97f1edf7624785c41ec0bfec6fd12c2388d9234Jeff Brown     * more information about the semantics of this request.
262a97f1edf7624785c41ec0bfec6fd12c2388d9234Jeff Brown     * </p>
263a97f1edf7624785c41ec0bfec6fd12c2388d9234Jeff Brown     *
264a97f1edf7624785c41ec0bfec6fd12c2388d9234Jeff Brown     * @param contentUri The content Uri to enqueue.
265a97f1edf7624785c41ec0bfec6fd12c2388d9234Jeff Brown     * @param mimeType The mime type of the content, or null if unknown.
266a97f1edf7624785c41ec0bfec6fd12c2388d9234Jeff Brown     * @param positionMillis The initial content position for the item in milliseconds,
267a97f1edf7624785c41ec0bfec6fd12c2388d9234Jeff Brown     * or <code>0</code> to start at the beginning.
268a97f1edf7624785c41ec0bfec6fd12c2388d9234Jeff Brown     * @param metadata The media item metadata bundle, or null if none.
269a97f1edf7624785c41ec0bfec6fd12c2388d9234Jeff Brown     * @param extras A bundle of extra arguments to be added to the
270a97f1edf7624785c41ec0bfec6fd12c2388d9234Jeff Brown     * {@link MediaControlIntent#ACTION_ENQUEUE} intent, or null if none.
271a97f1edf7624785c41ec0bfec6fd12c2388d9234Jeff Brown     * @param callback A callback to invoke when the request has been
272a97f1edf7624785c41ec0bfec6fd12c2388d9234Jeff Brown     * processed, or null if none.
273a97f1edf7624785c41ec0bfec6fd12c2388d9234Jeff Brown     *
274a97f1edf7624785c41ec0bfec6fd12c2388d9234Jeff Brown     * @throws UnsupportedOperationException if the route does not support queuing.
275a97f1edf7624785c41ec0bfec6fd12c2388d9234Jeff Brown     *
276a97f1edf7624785c41ec0bfec6fd12c2388d9234Jeff Brown     * @see MediaControlIntent#ACTION_ENQUEUE
277a97f1edf7624785c41ec0bfec6fd12c2388d9234Jeff Brown     * @see #isRemotePlaybackSupported
278a97f1edf7624785c41ec0bfec6fd12c2388d9234Jeff Brown     * @see #isQueuingSupported
279a97f1edf7624785c41ec0bfec6fd12c2388d9234Jeff Brown     */
280a97f1edf7624785c41ec0bfec6fd12c2388d9234Jeff Brown    public void enqueue(Uri contentUri, String mimeType, Bundle metadata,
281a97f1edf7624785c41ec0bfec6fd12c2388d9234Jeff Brown            long positionMillis, Bundle extras, ItemActionCallback callback) {
282a97f1edf7624785c41ec0bfec6fd12c2388d9234Jeff Brown        playOrEnqueue(contentUri, mimeType, metadata, positionMillis,
283a97f1edf7624785c41ec0bfec6fd12c2388d9234Jeff Brown                extras, callback, MediaControlIntent.ACTION_ENQUEUE);
284a97f1edf7624785c41ec0bfec6fd12c2388d9234Jeff Brown    }
285a97f1edf7624785c41ec0bfec6fd12c2388d9234Jeff Brown
286a97f1edf7624785c41ec0bfec6fd12c2388d9234Jeff Brown    private void playOrEnqueue(Uri contentUri, String mimeType, Bundle metadata,
287a97f1edf7624785c41ec0bfec6fd12c2388d9234Jeff Brown            long positionMillis, Bundle extras,
288a97f1edf7624785c41ec0bfec6fd12c2388d9234Jeff Brown            final ItemActionCallback callback, String action) {
289a97f1edf7624785c41ec0bfec6fd12c2388d9234Jeff Brown        if (contentUri == null) {
290a97f1edf7624785c41ec0bfec6fd12c2388d9234Jeff Brown            throw new IllegalArgumentException("contentUri must not be null");
291a97f1edf7624785c41ec0bfec6fd12c2388d9234Jeff Brown        }
292a97f1edf7624785c41ec0bfec6fd12c2388d9234Jeff Brown        throwIfRemotePlaybackNotSupported();
293a97f1edf7624785c41ec0bfec6fd12c2388d9234Jeff Brown        if (action.equals(MediaControlIntent.ACTION_ENQUEUE)) {
294a97f1edf7624785c41ec0bfec6fd12c2388d9234Jeff Brown            throwIfQueuingNotSupported();
295a97f1edf7624785c41ec0bfec6fd12c2388d9234Jeff Brown        }
296a97f1edf7624785c41ec0bfec6fd12c2388d9234Jeff Brown
297a97f1edf7624785c41ec0bfec6fd12c2388d9234Jeff Brown        Intent intent = new Intent(action);
298a97f1edf7624785c41ec0bfec6fd12c2388d9234Jeff Brown        intent.setDataAndType(contentUri, mimeType);
299a97f1edf7624785c41ec0bfec6fd12c2388d9234Jeff Brown        intent.putExtra(MediaControlIntent.EXTRA_ITEM_STATUS_UPDATE_RECEIVER,
300a97f1edf7624785c41ec0bfec6fd12c2388d9234Jeff Brown                mItemStatusPendingIntent);
301a97f1edf7624785c41ec0bfec6fd12c2388d9234Jeff Brown        if (metadata != null) {
302a97f1edf7624785c41ec0bfec6fd12c2388d9234Jeff Brown            intent.putExtra(MediaControlIntent.EXTRA_ITEM_METADATA, metadata);
303a97f1edf7624785c41ec0bfec6fd12c2388d9234Jeff Brown        }
304a97f1edf7624785c41ec0bfec6fd12c2388d9234Jeff Brown        if (positionMillis != 0) {
305a97f1edf7624785c41ec0bfec6fd12c2388d9234Jeff Brown            intent.putExtra(MediaControlIntent.EXTRA_ITEM_CONTENT_POSITION, positionMillis);
306a97f1edf7624785c41ec0bfec6fd12c2388d9234Jeff Brown        }
307a97f1edf7624785c41ec0bfec6fd12c2388d9234Jeff Brown        performItemAction(intent, mSessionId, null, extras, callback);
308a97f1edf7624785c41ec0bfec6fd12c2388d9234Jeff Brown    }
309a97f1edf7624785c41ec0bfec6fd12c2388d9234Jeff Brown
310a97f1edf7624785c41ec0bfec6fd12c2388d9234Jeff Brown    /**
311a97f1edf7624785c41ec0bfec6fd12c2388d9234Jeff Brown     * Sends a request to seek to a new position in a media item.
312a97f1edf7624785c41ec0bfec6fd12c2388d9234Jeff Brown     * <p>
313a97f1edf7624785c41ec0bfec6fd12c2388d9234Jeff Brown     * Seeks to a new position.  If the queue was previously paused then it
314a97f1edf7624785c41ec0bfec6fd12c2388d9234Jeff Brown     * remains paused but the item's new position is still remembered.
315a97f1edf7624785c41ec0bfec6fd12c2388d9234Jeff Brown     * </p><p>
316a97f1edf7624785c41ec0bfec6fd12c2388d9234Jeff Brown     * The request is issued in the current session.
317a97f1edf7624785c41ec0bfec6fd12c2388d9234Jeff Brown     * </p><p>
318a97f1edf7624785c41ec0bfec6fd12c2388d9234Jeff Brown     * Please refer to {@link MediaControlIntent#ACTION_SEEK ACTION_SEEK} for
319a97f1edf7624785c41ec0bfec6fd12c2388d9234Jeff Brown     * more information about the semantics of this request.
320a97f1edf7624785c41ec0bfec6fd12c2388d9234Jeff Brown     * </p>
321a97f1edf7624785c41ec0bfec6fd12c2388d9234Jeff Brown     *
322a97f1edf7624785c41ec0bfec6fd12c2388d9234Jeff Brown     * @param itemId The item id.
323a97f1edf7624785c41ec0bfec6fd12c2388d9234Jeff Brown     * @param positionMillis The new content position for the item in milliseconds,
324a97f1edf7624785c41ec0bfec6fd12c2388d9234Jeff Brown     * or <code>0</code> to start at the beginning.
325a97f1edf7624785c41ec0bfec6fd12c2388d9234Jeff Brown     * @param extras A bundle of extra arguments to be added to the
326a97f1edf7624785c41ec0bfec6fd12c2388d9234Jeff Brown     * {@link MediaControlIntent#ACTION_SEEK} intent, or null if none.
327a97f1edf7624785c41ec0bfec6fd12c2388d9234Jeff Brown     * @param callback A callback to invoke when the request has been
328a97f1edf7624785c41ec0bfec6fd12c2388d9234Jeff Brown     * processed, or null if none.
329a97f1edf7624785c41ec0bfec6fd12c2388d9234Jeff Brown     *
330a97f1edf7624785c41ec0bfec6fd12c2388d9234Jeff Brown     * @throws IllegalStateException if there is no current session.
331a97f1edf7624785c41ec0bfec6fd12c2388d9234Jeff Brown     *
332a97f1edf7624785c41ec0bfec6fd12c2388d9234Jeff Brown     * @see MediaControlIntent#ACTION_SEEK
333a97f1edf7624785c41ec0bfec6fd12c2388d9234Jeff Brown     * @see #isRemotePlaybackSupported
334a97f1edf7624785c41ec0bfec6fd12c2388d9234Jeff Brown     */
335a97f1edf7624785c41ec0bfec6fd12c2388d9234Jeff Brown    public void seek(String itemId, long positionMillis, Bundle extras,
336a97f1edf7624785c41ec0bfec6fd12c2388d9234Jeff Brown            ItemActionCallback callback) {
337a97f1edf7624785c41ec0bfec6fd12c2388d9234Jeff Brown        if (itemId == null) {
338a97f1edf7624785c41ec0bfec6fd12c2388d9234Jeff Brown            throw new IllegalArgumentException("itemId must not be null");
339a97f1edf7624785c41ec0bfec6fd12c2388d9234Jeff Brown        }
340a97f1edf7624785c41ec0bfec6fd12c2388d9234Jeff Brown        throwIfNoCurrentSession();
341a97f1edf7624785c41ec0bfec6fd12c2388d9234Jeff Brown
342a97f1edf7624785c41ec0bfec6fd12c2388d9234Jeff Brown        Intent intent = new Intent(MediaControlIntent.ACTION_SEEK);
343a97f1edf7624785c41ec0bfec6fd12c2388d9234Jeff Brown        intent.putExtra(MediaControlIntent.EXTRA_ITEM_CONTENT_POSITION, positionMillis);
344a97f1edf7624785c41ec0bfec6fd12c2388d9234Jeff Brown        performItemAction(intent, mSessionId, itemId, extras, callback);
345a97f1edf7624785c41ec0bfec6fd12c2388d9234Jeff Brown    }
346a97f1edf7624785c41ec0bfec6fd12c2388d9234Jeff Brown
347a97f1edf7624785c41ec0bfec6fd12c2388d9234Jeff Brown    /**
348a97f1edf7624785c41ec0bfec6fd12c2388d9234Jeff Brown     * Sends a request to get the status of a media item.
349a97f1edf7624785c41ec0bfec6fd12c2388d9234Jeff Brown     * <p>
350a97f1edf7624785c41ec0bfec6fd12c2388d9234Jeff Brown     * The request is issued in the current session.
351a97f1edf7624785c41ec0bfec6fd12c2388d9234Jeff Brown     * </p><p>
352a97f1edf7624785c41ec0bfec6fd12c2388d9234Jeff Brown     * Please refer to {@link MediaControlIntent#ACTION_GET_STATUS ACTION_GET_STATUS} for
353a97f1edf7624785c41ec0bfec6fd12c2388d9234Jeff Brown     * more information about the semantics of this request.
354a97f1edf7624785c41ec0bfec6fd12c2388d9234Jeff Brown     * </p>
355a97f1edf7624785c41ec0bfec6fd12c2388d9234Jeff Brown     *
356a97f1edf7624785c41ec0bfec6fd12c2388d9234Jeff Brown     * @param itemId The item id.
357a97f1edf7624785c41ec0bfec6fd12c2388d9234Jeff Brown     * @param extras A bundle of extra arguments to be added to the
358a97f1edf7624785c41ec0bfec6fd12c2388d9234Jeff Brown     * {@link MediaControlIntent#ACTION_GET_STATUS} intent, or null if none.
359a97f1edf7624785c41ec0bfec6fd12c2388d9234Jeff Brown     * @param callback A callback to invoke when the request has been
360a97f1edf7624785c41ec0bfec6fd12c2388d9234Jeff Brown     * processed, or null if none.
361a97f1edf7624785c41ec0bfec6fd12c2388d9234Jeff Brown     *
362a97f1edf7624785c41ec0bfec6fd12c2388d9234Jeff Brown     * @throws IllegalStateException if there is no current session.
363a97f1edf7624785c41ec0bfec6fd12c2388d9234Jeff Brown     *
364a97f1edf7624785c41ec0bfec6fd12c2388d9234Jeff Brown     * @see MediaControlIntent#ACTION_GET_STATUS
365a97f1edf7624785c41ec0bfec6fd12c2388d9234Jeff Brown     * @see #isRemotePlaybackSupported
366a97f1edf7624785c41ec0bfec6fd12c2388d9234Jeff Brown     */
367a97f1edf7624785c41ec0bfec6fd12c2388d9234Jeff Brown    public void getStatus(String itemId, Bundle extras, ItemActionCallback callback) {
368a97f1edf7624785c41ec0bfec6fd12c2388d9234Jeff Brown        if (itemId == null) {
369a97f1edf7624785c41ec0bfec6fd12c2388d9234Jeff Brown            throw new IllegalArgumentException("itemId must not be null");
370a97f1edf7624785c41ec0bfec6fd12c2388d9234Jeff Brown        }
371a97f1edf7624785c41ec0bfec6fd12c2388d9234Jeff Brown        throwIfNoCurrentSession();
372a97f1edf7624785c41ec0bfec6fd12c2388d9234Jeff Brown
373a97f1edf7624785c41ec0bfec6fd12c2388d9234Jeff Brown        Intent intent = new Intent(MediaControlIntent.ACTION_GET_STATUS);
374a97f1edf7624785c41ec0bfec6fd12c2388d9234Jeff Brown        performItemAction(intent, mSessionId, itemId, extras, callback);
375a97f1edf7624785c41ec0bfec6fd12c2388d9234Jeff Brown    }
376a97f1edf7624785c41ec0bfec6fd12c2388d9234Jeff Brown
377a97f1edf7624785c41ec0bfec6fd12c2388d9234Jeff Brown    /**
378a97f1edf7624785c41ec0bfec6fd12c2388d9234Jeff Brown     * Sends a request to remove a media item from the queue.
379a97f1edf7624785c41ec0bfec6fd12c2388d9234Jeff Brown     * <p>
380a97f1edf7624785c41ec0bfec6fd12c2388d9234Jeff Brown     * The request is issued in the current session.
381a97f1edf7624785c41ec0bfec6fd12c2388d9234Jeff Brown     * </p><p>
382a97f1edf7624785c41ec0bfec6fd12c2388d9234Jeff Brown     * Please refer to {@link MediaControlIntent#ACTION_REMOVE ACTION_REMOVE} for
383a97f1edf7624785c41ec0bfec6fd12c2388d9234Jeff Brown     * more information about the semantics of this request.
384a97f1edf7624785c41ec0bfec6fd12c2388d9234Jeff Brown     * </p>
385a97f1edf7624785c41ec0bfec6fd12c2388d9234Jeff Brown     *
386a97f1edf7624785c41ec0bfec6fd12c2388d9234Jeff Brown     * @param itemId The item id.
387a97f1edf7624785c41ec0bfec6fd12c2388d9234Jeff Brown     * @param extras A bundle of extra arguments to be added to the
388a97f1edf7624785c41ec0bfec6fd12c2388d9234Jeff Brown     * {@link MediaControlIntent#ACTION_REMOVE} intent, or null if none.
389a97f1edf7624785c41ec0bfec6fd12c2388d9234Jeff Brown     * @param callback A callback to invoke when the request has been
390a97f1edf7624785c41ec0bfec6fd12c2388d9234Jeff Brown     * processed, or null if none.
391a97f1edf7624785c41ec0bfec6fd12c2388d9234Jeff Brown     *
392a97f1edf7624785c41ec0bfec6fd12c2388d9234Jeff Brown     * @throws IllegalStateException if there is no current session.
393a97f1edf7624785c41ec0bfec6fd12c2388d9234Jeff Brown     * @throws UnsupportedOperationException if the route does not support queuing.
394a97f1edf7624785c41ec0bfec6fd12c2388d9234Jeff Brown     *
395a97f1edf7624785c41ec0bfec6fd12c2388d9234Jeff Brown     * @see MediaControlIntent#ACTION_REMOVE
396a97f1edf7624785c41ec0bfec6fd12c2388d9234Jeff Brown     * @see #isRemotePlaybackSupported
397a97f1edf7624785c41ec0bfec6fd12c2388d9234Jeff Brown     * @see #isQueuingSupported
398a97f1edf7624785c41ec0bfec6fd12c2388d9234Jeff Brown     */
399a97f1edf7624785c41ec0bfec6fd12c2388d9234Jeff Brown    public void remove(String itemId, Bundle extras, ItemActionCallback callback) {
400a97f1edf7624785c41ec0bfec6fd12c2388d9234Jeff Brown        if (itemId == null) {
401a97f1edf7624785c41ec0bfec6fd12c2388d9234Jeff Brown            throw new IllegalArgumentException("itemId must not be null");
402a97f1edf7624785c41ec0bfec6fd12c2388d9234Jeff Brown        }
403a97f1edf7624785c41ec0bfec6fd12c2388d9234Jeff Brown        throwIfQueuingNotSupported();
404a97f1edf7624785c41ec0bfec6fd12c2388d9234Jeff Brown        throwIfNoCurrentSession();
405a97f1edf7624785c41ec0bfec6fd12c2388d9234Jeff Brown
406a97f1edf7624785c41ec0bfec6fd12c2388d9234Jeff Brown        Intent intent = new Intent(MediaControlIntent.ACTION_REMOVE);
407a97f1edf7624785c41ec0bfec6fd12c2388d9234Jeff Brown        performItemAction(intent, mSessionId, itemId, extras, callback);
408a97f1edf7624785c41ec0bfec6fd12c2388d9234Jeff Brown    }
409a97f1edf7624785c41ec0bfec6fd12c2388d9234Jeff Brown
410a97f1edf7624785c41ec0bfec6fd12c2388d9234Jeff Brown    /**
411a97f1edf7624785c41ec0bfec6fd12c2388d9234Jeff Brown     * Sends a request to pause media playback.
412a97f1edf7624785c41ec0bfec6fd12c2388d9234Jeff Brown     * <p>
413a97f1edf7624785c41ec0bfec6fd12c2388d9234Jeff Brown     * The request is issued in the current session.  If playback is already paused
414a97f1edf7624785c41ec0bfec6fd12c2388d9234Jeff Brown     * then the request has no effect.
415a97f1edf7624785c41ec0bfec6fd12c2388d9234Jeff Brown     * </p><p>
416a97f1edf7624785c41ec0bfec6fd12c2388d9234Jeff Brown     * Please refer to {@link MediaControlIntent#ACTION_PAUSE ACTION_PAUSE} for
417a97f1edf7624785c41ec0bfec6fd12c2388d9234Jeff Brown     * more information about the semantics of this request.
418a97f1edf7624785c41ec0bfec6fd12c2388d9234Jeff Brown     * </p>
419a97f1edf7624785c41ec0bfec6fd12c2388d9234Jeff Brown     *
420a97f1edf7624785c41ec0bfec6fd12c2388d9234Jeff Brown     * @param extras A bundle of extra arguments to be added to the
421a97f1edf7624785c41ec0bfec6fd12c2388d9234Jeff Brown     * {@link MediaControlIntent#ACTION_PAUSE} intent, or null if none.
422a97f1edf7624785c41ec0bfec6fd12c2388d9234Jeff Brown     * @param callback A callback to invoke when the request has been
423a97f1edf7624785c41ec0bfec6fd12c2388d9234Jeff Brown     * processed, or null if none.
424a97f1edf7624785c41ec0bfec6fd12c2388d9234Jeff Brown     *
425a97f1edf7624785c41ec0bfec6fd12c2388d9234Jeff Brown     * @throws IllegalStateException if there is no current session.
426a97f1edf7624785c41ec0bfec6fd12c2388d9234Jeff Brown     *
427a97f1edf7624785c41ec0bfec6fd12c2388d9234Jeff Brown     * @see MediaControlIntent#ACTION_PAUSE
428a97f1edf7624785c41ec0bfec6fd12c2388d9234Jeff Brown     * @see #isRemotePlaybackSupported
429a97f1edf7624785c41ec0bfec6fd12c2388d9234Jeff Brown     */
430a97f1edf7624785c41ec0bfec6fd12c2388d9234Jeff Brown    public void pause(Bundle extras, SessionActionCallback callback) {
431a97f1edf7624785c41ec0bfec6fd12c2388d9234Jeff Brown        throwIfNoCurrentSession();
432a97f1edf7624785c41ec0bfec6fd12c2388d9234Jeff Brown
433a97f1edf7624785c41ec0bfec6fd12c2388d9234Jeff Brown        Intent intent = new Intent(MediaControlIntent.ACTION_PAUSE);
434a97f1edf7624785c41ec0bfec6fd12c2388d9234Jeff Brown        performSessionAction(intent, mSessionId, extras, callback);
435a97f1edf7624785c41ec0bfec6fd12c2388d9234Jeff Brown    }
436a97f1edf7624785c41ec0bfec6fd12c2388d9234Jeff Brown
437a97f1edf7624785c41ec0bfec6fd12c2388d9234Jeff Brown    /**
438a97f1edf7624785c41ec0bfec6fd12c2388d9234Jeff Brown     * Sends a request to resume (unpause) media playback.
439a97f1edf7624785c41ec0bfec6fd12c2388d9234Jeff Brown     * <p>
440a97f1edf7624785c41ec0bfec6fd12c2388d9234Jeff Brown     * The request is issued in the current session.  If playback is not paused
441a97f1edf7624785c41ec0bfec6fd12c2388d9234Jeff Brown     * then the request has no effect.
442a97f1edf7624785c41ec0bfec6fd12c2388d9234Jeff Brown     * </p><p>
443a97f1edf7624785c41ec0bfec6fd12c2388d9234Jeff Brown     * Please refer to {@link MediaControlIntent#ACTION_RESUME ACTION_RESUME} for
444a97f1edf7624785c41ec0bfec6fd12c2388d9234Jeff Brown     * more information about the semantics of this request.
445a97f1edf7624785c41ec0bfec6fd12c2388d9234Jeff Brown     * </p>
446a97f1edf7624785c41ec0bfec6fd12c2388d9234Jeff Brown     *
447a97f1edf7624785c41ec0bfec6fd12c2388d9234Jeff Brown     * @param extras A bundle of extra arguments to be added to the
448a97f1edf7624785c41ec0bfec6fd12c2388d9234Jeff Brown     * {@link MediaControlIntent#ACTION_RESUME} intent, or null if none.
449a97f1edf7624785c41ec0bfec6fd12c2388d9234Jeff Brown     * @param callback A callback to invoke when the request has been
450a97f1edf7624785c41ec0bfec6fd12c2388d9234Jeff Brown     * processed, or null if none.
451a97f1edf7624785c41ec0bfec6fd12c2388d9234Jeff Brown     *
452a97f1edf7624785c41ec0bfec6fd12c2388d9234Jeff Brown     * @throws IllegalStateException if there is no current session.
453a97f1edf7624785c41ec0bfec6fd12c2388d9234Jeff Brown     *
454a97f1edf7624785c41ec0bfec6fd12c2388d9234Jeff Brown     * @see MediaControlIntent#ACTION_RESUME
455a97f1edf7624785c41ec0bfec6fd12c2388d9234Jeff Brown     * @see #isRemotePlaybackSupported
456a97f1edf7624785c41ec0bfec6fd12c2388d9234Jeff Brown     */
457a97f1edf7624785c41ec0bfec6fd12c2388d9234Jeff Brown    public void resume(Bundle extras, SessionActionCallback callback) {
458a97f1edf7624785c41ec0bfec6fd12c2388d9234Jeff Brown        throwIfNoCurrentSession();
459a97f1edf7624785c41ec0bfec6fd12c2388d9234Jeff Brown
460a97f1edf7624785c41ec0bfec6fd12c2388d9234Jeff Brown        Intent intent = new Intent(MediaControlIntent.ACTION_RESUME);
461a97f1edf7624785c41ec0bfec6fd12c2388d9234Jeff Brown        performSessionAction(intent, mSessionId, extras, callback);
462a97f1edf7624785c41ec0bfec6fd12c2388d9234Jeff Brown    }
463a97f1edf7624785c41ec0bfec6fd12c2388d9234Jeff Brown
464a97f1edf7624785c41ec0bfec6fd12c2388d9234Jeff Brown    /**
465a97f1edf7624785c41ec0bfec6fd12c2388d9234Jeff Brown     * Sends a request to stop media playback and clear the media playback queue.
466a97f1edf7624785c41ec0bfec6fd12c2388d9234Jeff Brown     * <p>
467a97f1edf7624785c41ec0bfec6fd12c2388d9234Jeff Brown     * The request is issued in the current session.  If the queue is already
468a97f1edf7624785c41ec0bfec6fd12c2388d9234Jeff Brown     * empty then the request has no effect.
469a97f1edf7624785c41ec0bfec6fd12c2388d9234Jeff Brown     * </p><p>
470a97f1edf7624785c41ec0bfec6fd12c2388d9234Jeff Brown     * Please refer to {@link MediaControlIntent#ACTION_STOP ACTION_STOP} for
471a97f1edf7624785c41ec0bfec6fd12c2388d9234Jeff Brown     * more information about the semantics of this request.
472a97f1edf7624785c41ec0bfec6fd12c2388d9234Jeff Brown     * </p>
473a97f1edf7624785c41ec0bfec6fd12c2388d9234Jeff Brown     *
474a97f1edf7624785c41ec0bfec6fd12c2388d9234Jeff Brown     * @param extras A bundle of extra arguments to be added to the
475a97f1edf7624785c41ec0bfec6fd12c2388d9234Jeff Brown     * {@link MediaControlIntent#ACTION_STOP} intent, or null if none.
476a97f1edf7624785c41ec0bfec6fd12c2388d9234Jeff Brown     * @param callback A callback to invoke when the request has been
477a97f1edf7624785c41ec0bfec6fd12c2388d9234Jeff Brown     * processed, or null if none.
478a97f1edf7624785c41ec0bfec6fd12c2388d9234Jeff Brown     *
479a97f1edf7624785c41ec0bfec6fd12c2388d9234Jeff Brown     * @throws IllegalStateException if there is no current session.
480a97f1edf7624785c41ec0bfec6fd12c2388d9234Jeff Brown     *
481a97f1edf7624785c41ec0bfec6fd12c2388d9234Jeff Brown     * @see MediaControlIntent#ACTION_STOP
482a97f1edf7624785c41ec0bfec6fd12c2388d9234Jeff Brown     * @see #isRemotePlaybackSupported
483a97f1edf7624785c41ec0bfec6fd12c2388d9234Jeff Brown     */
484a97f1edf7624785c41ec0bfec6fd12c2388d9234Jeff Brown    public void stop(Bundle extras, SessionActionCallback callback) {
485a97f1edf7624785c41ec0bfec6fd12c2388d9234Jeff Brown        throwIfNoCurrentSession();
486a97f1edf7624785c41ec0bfec6fd12c2388d9234Jeff Brown
487a97f1edf7624785c41ec0bfec6fd12c2388d9234Jeff Brown        Intent intent = new Intent(MediaControlIntent.ACTION_STOP);
488a97f1edf7624785c41ec0bfec6fd12c2388d9234Jeff Brown        performSessionAction(intent, mSessionId, extras, callback);
489a97f1edf7624785c41ec0bfec6fd12c2388d9234Jeff Brown    }
490a97f1edf7624785c41ec0bfec6fd12c2388d9234Jeff Brown
491a97f1edf7624785c41ec0bfec6fd12c2388d9234Jeff Brown    /**
492a97f1edf7624785c41ec0bfec6fd12c2388d9234Jeff Brown     * Sends a request to start a new media playback session.
493a97f1edf7624785c41ec0bfec6fd12c2388d9234Jeff Brown     * <p>
494a97f1edf7624785c41ec0bfec6fd12c2388d9234Jeff Brown     * The application must wait for the callback to indicate that this request
495a97f1edf7624785c41ec0bfec6fd12c2388d9234Jeff Brown     * is complete before issuing other requests that affect the session.  If this
496a97f1edf7624785c41ec0bfec6fd12c2388d9234Jeff Brown     * request is successful then the previous session will be invalidated.
497a97f1edf7624785c41ec0bfec6fd12c2388d9234Jeff Brown     * </p><p>
498a97f1edf7624785c41ec0bfec6fd12c2388d9234Jeff Brown     * Please refer to {@link MediaControlIntent#ACTION_START_SESSION ACTION_START_SESSION}
499a97f1edf7624785c41ec0bfec6fd12c2388d9234Jeff Brown     * for more information about the semantics of this request.
500a97f1edf7624785c41ec0bfec6fd12c2388d9234Jeff Brown     * </p>
501a97f1edf7624785c41ec0bfec6fd12c2388d9234Jeff Brown     *
502a97f1edf7624785c41ec0bfec6fd12c2388d9234Jeff Brown     * @param extras A bundle of extra arguments to be added to the
503a97f1edf7624785c41ec0bfec6fd12c2388d9234Jeff Brown     * {@link MediaControlIntent#ACTION_START_SESSION} intent, or null if none.
504a97f1edf7624785c41ec0bfec6fd12c2388d9234Jeff Brown     * @param callback A callback to invoke when the request has been
505a97f1edf7624785c41ec0bfec6fd12c2388d9234Jeff Brown     * processed, or null if none.
506a97f1edf7624785c41ec0bfec6fd12c2388d9234Jeff Brown     *
507a97f1edf7624785c41ec0bfec6fd12c2388d9234Jeff Brown     * @throws UnsupportedOperationException if the route does not support session management.
508a97f1edf7624785c41ec0bfec6fd12c2388d9234Jeff Brown     *
509a97f1edf7624785c41ec0bfec6fd12c2388d9234Jeff Brown     * @see MediaControlIntent#ACTION_START_SESSION
510a97f1edf7624785c41ec0bfec6fd12c2388d9234Jeff Brown     * @see #isRemotePlaybackSupported
511a97f1edf7624785c41ec0bfec6fd12c2388d9234Jeff Brown     * @see #isSessionManagementSupported
512a97f1edf7624785c41ec0bfec6fd12c2388d9234Jeff Brown     */
513a97f1edf7624785c41ec0bfec6fd12c2388d9234Jeff Brown    public void startSession(Bundle extras, SessionActionCallback callback) {
514a97f1edf7624785c41ec0bfec6fd12c2388d9234Jeff Brown        throwIfSessionManagementNotSupported();
515a97f1edf7624785c41ec0bfec6fd12c2388d9234Jeff Brown
516a97f1edf7624785c41ec0bfec6fd12c2388d9234Jeff Brown        Intent intent = new Intent(MediaControlIntent.ACTION_START_SESSION);
517a97f1edf7624785c41ec0bfec6fd12c2388d9234Jeff Brown        intent.putExtra(MediaControlIntent.EXTRA_SESSION_STATUS_UPDATE_RECEIVER,
518a97f1edf7624785c41ec0bfec6fd12c2388d9234Jeff Brown                mSessionStatusPendingIntent);
519a97f1edf7624785c41ec0bfec6fd12c2388d9234Jeff Brown        performSessionAction(intent, null, extras, callback);
520a97f1edf7624785c41ec0bfec6fd12c2388d9234Jeff Brown    }
521a97f1edf7624785c41ec0bfec6fd12c2388d9234Jeff Brown
522a97f1edf7624785c41ec0bfec6fd12c2388d9234Jeff Brown    /**
523a97f1edf7624785c41ec0bfec6fd12c2388d9234Jeff Brown     * Sends a request to get the status of the media playback session.
524a97f1edf7624785c41ec0bfec6fd12c2388d9234Jeff Brown     * <p>
525a97f1edf7624785c41ec0bfec6fd12c2388d9234Jeff Brown     * The request is issued in the current session.
526a97f1edf7624785c41ec0bfec6fd12c2388d9234Jeff Brown     * </p><p>
527a97f1edf7624785c41ec0bfec6fd12c2388d9234Jeff Brown     * Please refer to {@link MediaControlIntent#ACTION_GET_SESSION_STATUS
528a97f1edf7624785c41ec0bfec6fd12c2388d9234Jeff Brown     * ACTION_GET_SESSION_STATUS} for more information about the semantics of this request.
529a97f1edf7624785c41ec0bfec6fd12c2388d9234Jeff Brown     * </p>
530a97f1edf7624785c41ec0bfec6fd12c2388d9234Jeff Brown     *
531a97f1edf7624785c41ec0bfec6fd12c2388d9234Jeff Brown     * @param extras A bundle of extra arguments to be added to the
532a97f1edf7624785c41ec0bfec6fd12c2388d9234Jeff Brown     * {@link MediaControlIntent#ACTION_GET_SESSION_STATUS} intent, or null if none.
533a97f1edf7624785c41ec0bfec6fd12c2388d9234Jeff Brown     * @param callback A callback to invoke when the request has been
534a97f1edf7624785c41ec0bfec6fd12c2388d9234Jeff Brown     * processed, or null if none.
535a97f1edf7624785c41ec0bfec6fd12c2388d9234Jeff Brown     *
536a97f1edf7624785c41ec0bfec6fd12c2388d9234Jeff Brown     * @throws IllegalStateException if there is no current session.
537a97f1edf7624785c41ec0bfec6fd12c2388d9234Jeff Brown     * @throws UnsupportedOperationException if the route does not support session management.
538a97f1edf7624785c41ec0bfec6fd12c2388d9234Jeff Brown     *
539a97f1edf7624785c41ec0bfec6fd12c2388d9234Jeff Brown     * @see MediaControlIntent#ACTION_GET_SESSION_STATUS
540a97f1edf7624785c41ec0bfec6fd12c2388d9234Jeff Brown     * @see #isRemotePlaybackSupported
541a97f1edf7624785c41ec0bfec6fd12c2388d9234Jeff Brown     * @see #isSessionManagementSupported
542a97f1edf7624785c41ec0bfec6fd12c2388d9234Jeff Brown     */
543a97f1edf7624785c41ec0bfec6fd12c2388d9234Jeff Brown    public void getSessionStatus(Bundle extras, SessionActionCallback callback) {
544a97f1edf7624785c41ec0bfec6fd12c2388d9234Jeff Brown        throwIfSessionManagementNotSupported();
545a97f1edf7624785c41ec0bfec6fd12c2388d9234Jeff Brown        throwIfNoCurrentSession();
546a97f1edf7624785c41ec0bfec6fd12c2388d9234Jeff Brown
547a97f1edf7624785c41ec0bfec6fd12c2388d9234Jeff Brown        Intent intent = new Intent(MediaControlIntent.ACTION_GET_SESSION_STATUS);
548a97f1edf7624785c41ec0bfec6fd12c2388d9234Jeff Brown        performSessionAction(intent, mSessionId, extras, callback);
549a97f1edf7624785c41ec0bfec6fd12c2388d9234Jeff Brown    }
550a97f1edf7624785c41ec0bfec6fd12c2388d9234Jeff Brown
551a97f1edf7624785c41ec0bfec6fd12c2388d9234Jeff Brown    /**
552a97f1edf7624785c41ec0bfec6fd12c2388d9234Jeff Brown     * Sends a request to end the media playback session.
553a97f1edf7624785c41ec0bfec6fd12c2388d9234Jeff Brown     * <p>
554a97f1edf7624785c41ec0bfec6fd12c2388d9234Jeff Brown     * The request is issued in the current session.  If this request is successful,
555a97f1edf7624785c41ec0bfec6fd12c2388d9234Jeff Brown     * the {@link #getSessionId session id property} will be set to null after
556a97f1edf7624785c41ec0bfec6fd12c2388d9234Jeff Brown     * the callback is invoked.
557a97f1edf7624785c41ec0bfec6fd12c2388d9234Jeff Brown     * </p><p>
558a97f1edf7624785c41ec0bfec6fd12c2388d9234Jeff Brown     * Please refer to {@link MediaControlIntent#ACTION_END_SESSION ACTION_END_SESSION}
559a97f1edf7624785c41ec0bfec6fd12c2388d9234Jeff Brown     * for more information about the semantics of this request.
560a97f1edf7624785c41ec0bfec6fd12c2388d9234Jeff Brown     * </p>
561a97f1edf7624785c41ec0bfec6fd12c2388d9234Jeff Brown     *
562a97f1edf7624785c41ec0bfec6fd12c2388d9234Jeff Brown     * @param extras A bundle of extra arguments to be added to the
563a97f1edf7624785c41ec0bfec6fd12c2388d9234Jeff Brown     * {@link MediaControlIntent#ACTION_END_SESSION} intent, or null if none.
564a97f1edf7624785c41ec0bfec6fd12c2388d9234Jeff Brown     * @param callback A callback to invoke when the request has been
565a97f1edf7624785c41ec0bfec6fd12c2388d9234Jeff Brown     * processed, or null if none.
566a97f1edf7624785c41ec0bfec6fd12c2388d9234Jeff Brown     *
567a97f1edf7624785c41ec0bfec6fd12c2388d9234Jeff Brown     * @throws IllegalStateException if there is no current session.
568a97f1edf7624785c41ec0bfec6fd12c2388d9234Jeff Brown     * @throws UnsupportedOperationException if the route does not support session management.
569a97f1edf7624785c41ec0bfec6fd12c2388d9234Jeff Brown     *
570a97f1edf7624785c41ec0bfec6fd12c2388d9234Jeff Brown     * @see MediaControlIntent#ACTION_END_SESSION
571a97f1edf7624785c41ec0bfec6fd12c2388d9234Jeff Brown     * @see #isRemotePlaybackSupported
572a97f1edf7624785c41ec0bfec6fd12c2388d9234Jeff Brown     * @see #isSessionManagementSupported
573a97f1edf7624785c41ec0bfec6fd12c2388d9234Jeff Brown     */
574a97f1edf7624785c41ec0bfec6fd12c2388d9234Jeff Brown    public void endSession(Bundle extras, SessionActionCallback callback) {
575a97f1edf7624785c41ec0bfec6fd12c2388d9234Jeff Brown        throwIfSessionManagementNotSupported();
576a97f1edf7624785c41ec0bfec6fd12c2388d9234Jeff Brown        throwIfNoCurrentSession();
577a97f1edf7624785c41ec0bfec6fd12c2388d9234Jeff Brown
578a97f1edf7624785c41ec0bfec6fd12c2388d9234Jeff Brown        Intent intent = new Intent(MediaControlIntent.ACTION_END_SESSION);
579a97f1edf7624785c41ec0bfec6fd12c2388d9234Jeff Brown        performSessionAction(intent, mSessionId, extras, callback);
580a97f1edf7624785c41ec0bfec6fd12c2388d9234Jeff Brown    }
581a97f1edf7624785c41ec0bfec6fd12c2388d9234Jeff Brown
582a97f1edf7624785c41ec0bfec6fd12c2388d9234Jeff Brown    private void performItemAction(final Intent intent,
583a97f1edf7624785c41ec0bfec6fd12c2388d9234Jeff Brown            final String sessionId, final String itemId,
584a97f1edf7624785c41ec0bfec6fd12c2388d9234Jeff Brown            Bundle extras, final ItemActionCallback callback) {
585a97f1edf7624785c41ec0bfec6fd12c2388d9234Jeff Brown        intent.addCategory(MediaControlIntent.CATEGORY_REMOTE_PLAYBACK);
586a97f1edf7624785c41ec0bfec6fd12c2388d9234Jeff Brown        if (sessionId != null) {
587a97f1edf7624785c41ec0bfec6fd12c2388d9234Jeff Brown            intent.putExtra(MediaControlIntent.EXTRA_SESSION_ID, sessionId);
588a97f1edf7624785c41ec0bfec6fd12c2388d9234Jeff Brown        }
589a97f1edf7624785c41ec0bfec6fd12c2388d9234Jeff Brown        if (itemId != null) {
590a97f1edf7624785c41ec0bfec6fd12c2388d9234Jeff Brown            intent.putExtra(MediaControlIntent.EXTRA_ITEM_ID, itemId);
591a97f1edf7624785c41ec0bfec6fd12c2388d9234Jeff Brown        }
592a97f1edf7624785c41ec0bfec6fd12c2388d9234Jeff Brown        if (extras != null) {
593a97f1edf7624785c41ec0bfec6fd12c2388d9234Jeff Brown            intent.putExtras(extras);
594a97f1edf7624785c41ec0bfec6fd12c2388d9234Jeff Brown        }
595a97f1edf7624785c41ec0bfec6fd12c2388d9234Jeff Brown        logRequest(intent);
596a97f1edf7624785c41ec0bfec6fd12c2388d9234Jeff Brown        mRoute.sendControlRequest(intent, new MediaRouter.ControlRequestCallback() {
597a97f1edf7624785c41ec0bfec6fd12c2388d9234Jeff Brown            @Override
598a97f1edf7624785c41ec0bfec6fd12c2388d9234Jeff Brown            public void onResult(Bundle data) {
599a97f1edf7624785c41ec0bfec6fd12c2388d9234Jeff Brown                if (data != null) {
600a97f1edf7624785c41ec0bfec6fd12c2388d9234Jeff Brown                    String sessionIdResult = inferMissingResult(sessionId,
601a97f1edf7624785c41ec0bfec6fd12c2388d9234Jeff Brown                            data.getString(MediaControlIntent.EXTRA_SESSION_ID));
602a97f1edf7624785c41ec0bfec6fd12c2388d9234Jeff Brown                    MediaSessionStatus sessionStatus = MediaSessionStatus.fromBundle(
603a97f1edf7624785c41ec0bfec6fd12c2388d9234Jeff Brown                            data.getBundle(MediaControlIntent.EXTRA_SESSION_STATUS));
604a97f1edf7624785c41ec0bfec6fd12c2388d9234Jeff Brown                    String itemIdResult = inferMissingResult(itemId,
605a97f1edf7624785c41ec0bfec6fd12c2388d9234Jeff Brown                            data.getString(MediaControlIntent.EXTRA_ITEM_ID));
606a97f1edf7624785c41ec0bfec6fd12c2388d9234Jeff Brown                    MediaItemStatus itemStatus = MediaItemStatus.fromBundle(
607a97f1edf7624785c41ec0bfec6fd12c2388d9234Jeff Brown                            data.getBundle(MediaControlIntent.EXTRA_ITEM_STATUS));
608a97f1edf7624785c41ec0bfec6fd12c2388d9234Jeff Brown                    adoptSession(sessionIdResult);
609a97f1edf7624785c41ec0bfec6fd12c2388d9234Jeff Brown                    if (sessionIdResult != null && itemIdResult != null && itemStatus != null) {
610a97f1edf7624785c41ec0bfec6fd12c2388d9234Jeff Brown                        if (DEBUG) {
611a97f1edf7624785c41ec0bfec6fd12c2388d9234Jeff Brown                            Log.d(TAG, "Received result from " + intent.getAction()
612a97f1edf7624785c41ec0bfec6fd12c2388d9234Jeff Brown                                    + ": data=" + bundleToString(data)
613a97f1edf7624785c41ec0bfec6fd12c2388d9234Jeff Brown                                    + ", sessionId=" + sessionIdResult
614a97f1edf7624785c41ec0bfec6fd12c2388d9234Jeff Brown                                    + ", sessionStatus=" + sessionStatus
615a97f1edf7624785c41ec0bfec6fd12c2388d9234Jeff Brown                                    + ", itemId=" + itemIdResult
616a97f1edf7624785c41ec0bfec6fd12c2388d9234Jeff Brown                                    + ", itemStatus=" + itemStatus);
617a97f1edf7624785c41ec0bfec6fd12c2388d9234Jeff Brown                        }
618a97f1edf7624785c41ec0bfec6fd12c2388d9234Jeff Brown                        callback.onResult(data, sessionIdResult, sessionStatus,
619a97f1edf7624785c41ec0bfec6fd12c2388d9234Jeff Brown                                itemIdResult, itemStatus);
620a97f1edf7624785c41ec0bfec6fd12c2388d9234Jeff Brown                        return;
621a97f1edf7624785c41ec0bfec6fd12c2388d9234Jeff Brown                    }
622a97f1edf7624785c41ec0bfec6fd12c2388d9234Jeff Brown                }
623a97f1edf7624785c41ec0bfec6fd12c2388d9234Jeff Brown                handleInvalidResult(intent, callback, data);
624a97f1edf7624785c41ec0bfec6fd12c2388d9234Jeff Brown            }
625a97f1edf7624785c41ec0bfec6fd12c2388d9234Jeff Brown
626a97f1edf7624785c41ec0bfec6fd12c2388d9234Jeff Brown            @Override
627a97f1edf7624785c41ec0bfec6fd12c2388d9234Jeff Brown            public void onError(String error, Bundle data) {
628a97f1edf7624785c41ec0bfec6fd12c2388d9234Jeff Brown                handleError(intent, callback, error, data);
629a97f1edf7624785c41ec0bfec6fd12c2388d9234Jeff Brown            }
630a97f1edf7624785c41ec0bfec6fd12c2388d9234Jeff Brown        });
631a97f1edf7624785c41ec0bfec6fd12c2388d9234Jeff Brown    }
632a97f1edf7624785c41ec0bfec6fd12c2388d9234Jeff Brown
633a97f1edf7624785c41ec0bfec6fd12c2388d9234Jeff Brown    private void performSessionAction(final Intent intent, final String sessionId,
634a97f1edf7624785c41ec0bfec6fd12c2388d9234Jeff Brown            Bundle extras, final SessionActionCallback callback) {
635a97f1edf7624785c41ec0bfec6fd12c2388d9234Jeff Brown        intent.addCategory(MediaControlIntent.CATEGORY_REMOTE_PLAYBACK);
636a97f1edf7624785c41ec0bfec6fd12c2388d9234Jeff Brown        if (sessionId != null) {
637a97f1edf7624785c41ec0bfec6fd12c2388d9234Jeff Brown            intent.putExtra(MediaControlIntent.EXTRA_SESSION_ID, sessionId);
638a97f1edf7624785c41ec0bfec6fd12c2388d9234Jeff Brown        }
639a97f1edf7624785c41ec0bfec6fd12c2388d9234Jeff Brown        if (extras != null) {
640a97f1edf7624785c41ec0bfec6fd12c2388d9234Jeff Brown            intent.putExtras(extras);
641a97f1edf7624785c41ec0bfec6fd12c2388d9234Jeff Brown        }
642a97f1edf7624785c41ec0bfec6fd12c2388d9234Jeff Brown        logRequest(intent);
643a97f1edf7624785c41ec0bfec6fd12c2388d9234Jeff Brown        mRoute.sendControlRequest(intent, new MediaRouter.ControlRequestCallback() {
644a97f1edf7624785c41ec0bfec6fd12c2388d9234Jeff Brown            @Override
645a97f1edf7624785c41ec0bfec6fd12c2388d9234Jeff Brown            public void onResult(Bundle data) {
646a97f1edf7624785c41ec0bfec6fd12c2388d9234Jeff Brown                if (data != null) {
647a97f1edf7624785c41ec0bfec6fd12c2388d9234Jeff Brown                    String sessionIdResult = inferMissingResult(sessionId,
648a97f1edf7624785c41ec0bfec6fd12c2388d9234Jeff Brown                            data.getString(MediaControlIntent.EXTRA_SESSION_ID));
649a97f1edf7624785c41ec0bfec6fd12c2388d9234Jeff Brown                    MediaSessionStatus sessionStatus = MediaSessionStatus.fromBundle(
650a97f1edf7624785c41ec0bfec6fd12c2388d9234Jeff Brown                            data.getBundle(MediaControlIntent.EXTRA_SESSION_STATUS));
651a97f1edf7624785c41ec0bfec6fd12c2388d9234Jeff Brown                    adoptSession(sessionIdResult);
652a97f1edf7624785c41ec0bfec6fd12c2388d9234Jeff Brown                    if (sessionIdResult != null) {
653a97f1edf7624785c41ec0bfec6fd12c2388d9234Jeff Brown                        if (DEBUG) {
654a97f1edf7624785c41ec0bfec6fd12c2388d9234Jeff Brown                            Log.d(TAG, "Received result from " + intent.getAction()
655a97f1edf7624785c41ec0bfec6fd12c2388d9234Jeff Brown                                    + ": data=" + bundleToString(data)
656a97f1edf7624785c41ec0bfec6fd12c2388d9234Jeff Brown                                    + ", sessionId=" + sessionIdResult
657a97f1edf7624785c41ec0bfec6fd12c2388d9234Jeff Brown                                    + ", sessionStatus=" + sessionStatus);
658a97f1edf7624785c41ec0bfec6fd12c2388d9234Jeff Brown                        }
659a97f1edf7624785c41ec0bfec6fd12c2388d9234Jeff Brown                        try {
660a97f1edf7624785c41ec0bfec6fd12c2388d9234Jeff Brown                            callback.onResult(data, sessionIdResult, sessionStatus);
661a97f1edf7624785c41ec0bfec6fd12c2388d9234Jeff Brown                        } finally {
662a97f1edf7624785c41ec0bfec6fd12c2388d9234Jeff Brown                            if (intent.getAction().equals(MediaControlIntent.ACTION_END_SESSION)
663a97f1edf7624785c41ec0bfec6fd12c2388d9234Jeff Brown                                    && sessionIdResult.equals(mSessionId)) {
664a97f1edf7624785c41ec0bfec6fd12c2388d9234Jeff Brown                                setSessionId(null);
665a97f1edf7624785c41ec0bfec6fd12c2388d9234Jeff Brown                            }
666a97f1edf7624785c41ec0bfec6fd12c2388d9234Jeff Brown                        }
667a97f1edf7624785c41ec0bfec6fd12c2388d9234Jeff Brown                        return;
668a97f1edf7624785c41ec0bfec6fd12c2388d9234Jeff Brown                    }
669a97f1edf7624785c41ec0bfec6fd12c2388d9234Jeff Brown                }
670a97f1edf7624785c41ec0bfec6fd12c2388d9234Jeff Brown                handleInvalidResult(intent, callback, data);
671a97f1edf7624785c41ec0bfec6fd12c2388d9234Jeff Brown            }
672a97f1edf7624785c41ec0bfec6fd12c2388d9234Jeff Brown
673a97f1edf7624785c41ec0bfec6fd12c2388d9234Jeff Brown            @Override
674a97f1edf7624785c41ec0bfec6fd12c2388d9234Jeff Brown            public void onError(String error, Bundle data) {
675a97f1edf7624785c41ec0bfec6fd12c2388d9234Jeff Brown                handleError(intent, callback, error, data);
676a97f1edf7624785c41ec0bfec6fd12c2388d9234Jeff Brown            }
677a97f1edf7624785c41ec0bfec6fd12c2388d9234Jeff Brown        });
678a97f1edf7624785c41ec0bfec6fd12c2388d9234Jeff Brown    }
679a97f1edf7624785c41ec0bfec6fd12c2388d9234Jeff Brown
680a97f1edf7624785c41ec0bfec6fd12c2388d9234Jeff Brown    private void adoptSession(String sessionId) {
681a97f1edf7624785c41ec0bfec6fd12c2388d9234Jeff Brown        if (sessionId != null) {
682a97f1edf7624785c41ec0bfec6fd12c2388d9234Jeff Brown            setSessionId(sessionId);
683a97f1edf7624785c41ec0bfec6fd12c2388d9234Jeff Brown        }
684a97f1edf7624785c41ec0bfec6fd12c2388d9234Jeff Brown    }
685a97f1edf7624785c41ec0bfec6fd12c2388d9234Jeff Brown
686a97f1edf7624785c41ec0bfec6fd12c2388d9234Jeff Brown    private void handleInvalidResult(Intent intent, ActionCallback callback,
687a97f1edf7624785c41ec0bfec6fd12c2388d9234Jeff Brown            Bundle data) {
688a97f1edf7624785c41ec0bfec6fd12c2388d9234Jeff Brown        Log.w(TAG, "Received invalid result data from " + intent.getAction()
689a97f1edf7624785c41ec0bfec6fd12c2388d9234Jeff Brown                + ": data=" + bundleToString(data));
690a97f1edf7624785c41ec0bfec6fd12c2388d9234Jeff Brown        callback.onError(null, MediaControlIntent.ERROR_UNKNOWN, data);
691a97f1edf7624785c41ec0bfec6fd12c2388d9234Jeff Brown    }
692a97f1edf7624785c41ec0bfec6fd12c2388d9234Jeff Brown
693a97f1edf7624785c41ec0bfec6fd12c2388d9234Jeff Brown    private void handleError(Intent intent, ActionCallback callback,
694a97f1edf7624785c41ec0bfec6fd12c2388d9234Jeff Brown            String error, Bundle data) {
695a97f1edf7624785c41ec0bfec6fd12c2388d9234Jeff Brown        final int code;
696a97f1edf7624785c41ec0bfec6fd12c2388d9234Jeff Brown        if (data != null) {
697a97f1edf7624785c41ec0bfec6fd12c2388d9234Jeff Brown            code = data.getInt(MediaControlIntent.EXTRA_ERROR_CODE,
698a97f1edf7624785c41ec0bfec6fd12c2388d9234Jeff Brown                    MediaControlIntent.ERROR_UNKNOWN);
699a97f1edf7624785c41ec0bfec6fd12c2388d9234Jeff Brown        } else {
700a97f1edf7624785c41ec0bfec6fd12c2388d9234Jeff Brown            code = MediaControlIntent.ERROR_UNKNOWN;
701a97f1edf7624785c41ec0bfec6fd12c2388d9234Jeff Brown        }
702a97f1edf7624785c41ec0bfec6fd12c2388d9234Jeff Brown        if (DEBUG) {
703a97f1edf7624785c41ec0bfec6fd12c2388d9234Jeff Brown            Log.w(TAG, "Received error from " + intent.getAction()
704a97f1edf7624785c41ec0bfec6fd12c2388d9234Jeff Brown                    + ": error=" + error
705a97f1edf7624785c41ec0bfec6fd12c2388d9234Jeff Brown                    + ", code=" + code
706a97f1edf7624785c41ec0bfec6fd12c2388d9234Jeff Brown                    + ", data=" + bundleToString(data));
707a97f1edf7624785c41ec0bfec6fd12c2388d9234Jeff Brown        }
708a97f1edf7624785c41ec0bfec6fd12c2388d9234Jeff Brown        callback.onError(error, code, data);
709a97f1edf7624785c41ec0bfec6fd12c2388d9234Jeff Brown    }
710a97f1edf7624785c41ec0bfec6fd12c2388d9234Jeff Brown
711a97f1edf7624785c41ec0bfec6fd12c2388d9234Jeff Brown    private void detectFeatures() {
712a97f1edf7624785c41ec0bfec6fd12c2388d9234Jeff Brown        mRouteSupportsRemotePlayback = routeSupportsAction(MediaControlIntent.ACTION_PLAY)
713a97f1edf7624785c41ec0bfec6fd12c2388d9234Jeff Brown                && routeSupportsAction(MediaControlIntent.ACTION_SEEK)
714a97f1edf7624785c41ec0bfec6fd12c2388d9234Jeff Brown                && routeSupportsAction(MediaControlIntent.ACTION_GET_STATUS)
715a97f1edf7624785c41ec0bfec6fd12c2388d9234Jeff Brown                && routeSupportsAction(MediaControlIntent.ACTION_PAUSE)
716a97f1edf7624785c41ec0bfec6fd12c2388d9234Jeff Brown                && routeSupportsAction(MediaControlIntent.ACTION_RESUME)
717a97f1edf7624785c41ec0bfec6fd12c2388d9234Jeff Brown                && routeSupportsAction(MediaControlIntent.ACTION_STOP);
718a97f1edf7624785c41ec0bfec6fd12c2388d9234Jeff Brown        mRouteSupportsQueuing = mRouteSupportsRemotePlayback
719a97f1edf7624785c41ec0bfec6fd12c2388d9234Jeff Brown                && routeSupportsAction(MediaControlIntent.ACTION_ENQUEUE)
720a97f1edf7624785c41ec0bfec6fd12c2388d9234Jeff Brown                && routeSupportsAction(MediaControlIntent.ACTION_REMOVE);
721a97f1edf7624785c41ec0bfec6fd12c2388d9234Jeff Brown        mRouteSupportsSessionManagement = mRouteSupportsRemotePlayback
722a97f1edf7624785c41ec0bfec6fd12c2388d9234Jeff Brown                && routeSupportsAction(MediaControlIntent.ACTION_START_SESSION)
723a97f1edf7624785c41ec0bfec6fd12c2388d9234Jeff Brown                && routeSupportsAction(MediaControlIntent.ACTION_GET_SESSION_STATUS)
724a97f1edf7624785c41ec0bfec6fd12c2388d9234Jeff Brown                && routeSupportsAction(MediaControlIntent.ACTION_END_SESSION);
725a97f1edf7624785c41ec0bfec6fd12c2388d9234Jeff Brown    }
726a97f1edf7624785c41ec0bfec6fd12c2388d9234Jeff Brown
727a97f1edf7624785c41ec0bfec6fd12c2388d9234Jeff Brown    private boolean routeSupportsAction(String action) {
728a97f1edf7624785c41ec0bfec6fd12c2388d9234Jeff Brown        return mRoute.supportsControlAction(MediaControlIntent.CATEGORY_REMOTE_PLAYBACK, action);
729a97f1edf7624785c41ec0bfec6fd12c2388d9234Jeff Brown    }
730a97f1edf7624785c41ec0bfec6fd12c2388d9234Jeff Brown
731a97f1edf7624785c41ec0bfec6fd12c2388d9234Jeff Brown    private void throwIfRemotePlaybackNotSupported() {
732a97f1edf7624785c41ec0bfec6fd12c2388d9234Jeff Brown        if (!mRouteSupportsRemotePlayback) {
733a97f1edf7624785c41ec0bfec6fd12c2388d9234Jeff Brown            throw new UnsupportedOperationException("The route does not support remote playback.");
734a97f1edf7624785c41ec0bfec6fd12c2388d9234Jeff Brown        }
735a97f1edf7624785c41ec0bfec6fd12c2388d9234Jeff Brown    }
736a97f1edf7624785c41ec0bfec6fd12c2388d9234Jeff Brown
737a97f1edf7624785c41ec0bfec6fd12c2388d9234Jeff Brown    private void throwIfQueuingNotSupported() {
738a97f1edf7624785c41ec0bfec6fd12c2388d9234Jeff Brown        if (!mRouteSupportsQueuing) {
739a97f1edf7624785c41ec0bfec6fd12c2388d9234Jeff Brown            throw new UnsupportedOperationException("The route does not support queuing.");
740a97f1edf7624785c41ec0bfec6fd12c2388d9234Jeff Brown        }
741a97f1edf7624785c41ec0bfec6fd12c2388d9234Jeff Brown    }
742a97f1edf7624785c41ec0bfec6fd12c2388d9234Jeff Brown
743a97f1edf7624785c41ec0bfec6fd12c2388d9234Jeff Brown    private void throwIfSessionManagementNotSupported() {
744a97f1edf7624785c41ec0bfec6fd12c2388d9234Jeff Brown        if (!mRouteSupportsSessionManagement) {
745a97f1edf7624785c41ec0bfec6fd12c2388d9234Jeff Brown            throw new UnsupportedOperationException("The route does not support "
746a97f1edf7624785c41ec0bfec6fd12c2388d9234Jeff Brown                    + "session management.");
747a97f1edf7624785c41ec0bfec6fd12c2388d9234Jeff Brown        }
748a97f1edf7624785c41ec0bfec6fd12c2388d9234Jeff Brown    }
749a97f1edf7624785c41ec0bfec6fd12c2388d9234Jeff Brown
750a97f1edf7624785c41ec0bfec6fd12c2388d9234Jeff Brown    private void throwIfNoCurrentSession() {
751a97f1edf7624785c41ec0bfec6fd12c2388d9234Jeff Brown        if (mSessionId == null) {
752a97f1edf7624785c41ec0bfec6fd12c2388d9234Jeff Brown            throw new IllegalStateException("There is no current session.");
753a97f1edf7624785c41ec0bfec6fd12c2388d9234Jeff Brown        }
754a97f1edf7624785c41ec0bfec6fd12c2388d9234Jeff Brown    }
755a97f1edf7624785c41ec0bfec6fd12c2388d9234Jeff Brown
756a97f1edf7624785c41ec0bfec6fd12c2388d9234Jeff Brown    private static String inferMissingResult(String request, String result) {
757a97f1edf7624785c41ec0bfec6fd12c2388d9234Jeff Brown        if (result == null) {
758a97f1edf7624785c41ec0bfec6fd12c2388d9234Jeff Brown            // Result is missing.
759a97f1edf7624785c41ec0bfec6fd12c2388d9234Jeff Brown            return request;
760a97f1edf7624785c41ec0bfec6fd12c2388d9234Jeff Brown        }
761a97f1edf7624785c41ec0bfec6fd12c2388d9234Jeff Brown        if (request == null || request.equals(result)) {
762a97f1edf7624785c41ec0bfec6fd12c2388d9234Jeff Brown            // Request didn't specify a value or result matches request.
763a97f1edf7624785c41ec0bfec6fd12c2388d9234Jeff Brown            return result;
764a97f1edf7624785c41ec0bfec6fd12c2388d9234Jeff Brown        }
765a97f1edf7624785c41ec0bfec6fd12c2388d9234Jeff Brown        // Result conflicts with request.
766a97f1edf7624785c41ec0bfec6fd12c2388d9234Jeff Brown        return null;
767a97f1edf7624785c41ec0bfec6fd12c2388d9234Jeff Brown    }
768a97f1edf7624785c41ec0bfec6fd12c2388d9234Jeff Brown
769a97f1edf7624785c41ec0bfec6fd12c2388d9234Jeff Brown    private static void logRequest(Intent intent) {
770a97f1edf7624785c41ec0bfec6fd12c2388d9234Jeff Brown        if (DEBUG) {
771a97f1edf7624785c41ec0bfec6fd12c2388d9234Jeff Brown            Log.d(TAG, "Sending request: " + intent);
772a97f1edf7624785c41ec0bfec6fd12c2388d9234Jeff Brown        }
773a97f1edf7624785c41ec0bfec6fd12c2388d9234Jeff Brown    }
774a97f1edf7624785c41ec0bfec6fd12c2388d9234Jeff Brown
775a97f1edf7624785c41ec0bfec6fd12c2388d9234Jeff Brown    private static String bundleToString(Bundle bundle) {
776a97f1edf7624785c41ec0bfec6fd12c2388d9234Jeff Brown        if (bundle != null) {
777a97f1edf7624785c41ec0bfec6fd12c2388d9234Jeff Brown            bundle.size(); // force bundle to be unparcelled
778a97f1edf7624785c41ec0bfec6fd12c2388d9234Jeff Brown            return bundle.toString();
779a97f1edf7624785c41ec0bfec6fd12c2388d9234Jeff Brown        }
780a97f1edf7624785c41ec0bfec6fd12c2388d9234Jeff Brown        return "null";
781a97f1edf7624785c41ec0bfec6fd12c2388d9234Jeff Brown    }
782a97f1edf7624785c41ec0bfec6fd12c2388d9234Jeff Brown
783a97f1edf7624785c41ec0bfec6fd12c2388d9234Jeff Brown    private final class StatusReceiver extends BroadcastReceiver {
784a97f1edf7624785c41ec0bfec6fd12c2388d9234Jeff Brown        public static final String ACTION_ITEM_STATUS_CHANGED =
785a97f1edf7624785c41ec0bfec6fd12c2388d9234Jeff Brown                "android.support.v7.media.actions.ACTION_ITEM_STATUS_CHANGED";
786a97f1edf7624785c41ec0bfec6fd12c2388d9234Jeff Brown        public static final String ACTION_SESSION_STATUS_CHANGED =
787a97f1edf7624785c41ec0bfec6fd12c2388d9234Jeff Brown                "android.support.v7.media.actions.ACTION_SESSION_STATUS_CHANGED";
788a97f1edf7624785c41ec0bfec6fd12c2388d9234Jeff Brown
789a97f1edf7624785c41ec0bfec6fd12c2388d9234Jeff Brown        @Override
790a97f1edf7624785c41ec0bfec6fd12c2388d9234Jeff Brown        public void onReceive(Context context, Intent intent) {
791a97f1edf7624785c41ec0bfec6fd12c2388d9234Jeff Brown            String sessionId = intent.getStringExtra(MediaControlIntent.EXTRA_SESSION_ID);
792a97f1edf7624785c41ec0bfec6fd12c2388d9234Jeff Brown            if (sessionId == null || !sessionId.equals(mSessionId)) {
793a97f1edf7624785c41ec0bfec6fd12c2388d9234Jeff Brown                Log.w(TAG, "Discarding spurious status callback "
794a97f1edf7624785c41ec0bfec6fd12c2388d9234Jeff Brown                        + "with missing or invalid session id: sessionId=" + sessionId);
795a97f1edf7624785c41ec0bfec6fd12c2388d9234Jeff Brown                return;
796a97f1edf7624785c41ec0bfec6fd12c2388d9234Jeff Brown            }
797a97f1edf7624785c41ec0bfec6fd12c2388d9234Jeff Brown
798a97f1edf7624785c41ec0bfec6fd12c2388d9234Jeff Brown            MediaSessionStatus sessionStatus = MediaSessionStatus.fromBundle(
799a97f1edf7624785c41ec0bfec6fd12c2388d9234Jeff Brown                    intent.getBundleExtra(MediaControlIntent.EXTRA_SESSION_STATUS));
800a97f1edf7624785c41ec0bfec6fd12c2388d9234Jeff Brown            String action = intent.getAction();
801a97f1edf7624785c41ec0bfec6fd12c2388d9234Jeff Brown            if (action.equals(ACTION_ITEM_STATUS_CHANGED)) {
802a97f1edf7624785c41ec0bfec6fd12c2388d9234Jeff Brown                String itemId = intent.getStringExtra(MediaControlIntent.EXTRA_ITEM_ID);
803a97f1edf7624785c41ec0bfec6fd12c2388d9234Jeff Brown                if (itemId == null) {
804a97f1edf7624785c41ec0bfec6fd12c2388d9234Jeff Brown                    Log.w(TAG, "Discarding spurious status callback with missing item id.");
805a97f1edf7624785c41ec0bfec6fd12c2388d9234Jeff Brown                    return;
806a97f1edf7624785c41ec0bfec6fd12c2388d9234Jeff Brown                }
807a97f1edf7624785c41ec0bfec6fd12c2388d9234Jeff Brown
808a97f1edf7624785c41ec0bfec6fd12c2388d9234Jeff Brown                MediaItemStatus itemStatus = MediaItemStatus.fromBundle(
809a97f1edf7624785c41ec0bfec6fd12c2388d9234Jeff Brown                        intent.getBundleExtra(MediaControlIntent.EXTRA_ITEM_STATUS));
810a97f1edf7624785c41ec0bfec6fd12c2388d9234Jeff Brown                if (itemStatus == null) {
811a97f1edf7624785c41ec0bfec6fd12c2388d9234Jeff Brown                    Log.w(TAG, "Discarding spurious status callback with missing item status.");
812a97f1edf7624785c41ec0bfec6fd12c2388d9234Jeff Brown                    return;
813a97f1edf7624785c41ec0bfec6fd12c2388d9234Jeff Brown                }
814a97f1edf7624785c41ec0bfec6fd12c2388d9234Jeff Brown
815a97f1edf7624785c41ec0bfec6fd12c2388d9234Jeff Brown                if (DEBUG) {
816a97f1edf7624785c41ec0bfec6fd12c2388d9234Jeff Brown                    Log.d(TAG, "Received item status callback: sessionId=" + sessionId
817a97f1edf7624785c41ec0bfec6fd12c2388d9234Jeff Brown                            + ", sessionStatus=" + sessionStatus
818a97f1edf7624785c41ec0bfec6fd12c2388d9234Jeff Brown                            + ", itemId=" + itemId
819a97f1edf7624785c41ec0bfec6fd12c2388d9234Jeff Brown                            + ", itemStatus=" + itemStatus);
820a97f1edf7624785c41ec0bfec6fd12c2388d9234Jeff Brown                }
821a97f1edf7624785c41ec0bfec6fd12c2388d9234Jeff Brown
822a97f1edf7624785c41ec0bfec6fd12c2388d9234Jeff Brown                if (mStatusCallback != null) {
823a97f1edf7624785c41ec0bfec6fd12c2388d9234Jeff Brown                    mStatusCallback.onItemStatusChanged(intent.getExtras(),
824a97f1edf7624785c41ec0bfec6fd12c2388d9234Jeff Brown                            sessionId, sessionStatus, itemId, itemStatus);
825a97f1edf7624785c41ec0bfec6fd12c2388d9234Jeff Brown                }
826a97f1edf7624785c41ec0bfec6fd12c2388d9234Jeff Brown            } else if (action.equals(ACTION_SESSION_STATUS_CHANGED)) {
827a97f1edf7624785c41ec0bfec6fd12c2388d9234Jeff Brown                if (sessionStatus == null) {
828a97f1edf7624785c41ec0bfec6fd12c2388d9234Jeff Brown                    Log.w(TAG, "Discarding spurious media status callback with "
829a97f1edf7624785c41ec0bfec6fd12c2388d9234Jeff Brown                            +"missing session status.");
830a97f1edf7624785c41ec0bfec6fd12c2388d9234Jeff Brown                    return;
831a97f1edf7624785c41ec0bfec6fd12c2388d9234Jeff Brown                }
832a97f1edf7624785c41ec0bfec6fd12c2388d9234Jeff Brown
833a97f1edf7624785c41ec0bfec6fd12c2388d9234Jeff Brown                if (DEBUG) {
834a97f1edf7624785c41ec0bfec6fd12c2388d9234Jeff Brown                    Log.d(TAG, "Received session status callback: sessionId=" + sessionId
835a97f1edf7624785c41ec0bfec6fd12c2388d9234Jeff Brown                            + ", sessionStatus=" + sessionStatus);
836a97f1edf7624785c41ec0bfec6fd12c2388d9234Jeff Brown                }
837a97f1edf7624785c41ec0bfec6fd12c2388d9234Jeff Brown
838a97f1edf7624785c41ec0bfec6fd12c2388d9234Jeff Brown                if (mStatusCallback != null) {
839a97f1edf7624785c41ec0bfec6fd12c2388d9234Jeff Brown                    mStatusCallback.onSessionStatusChanged(intent.getExtras(),
840a97f1edf7624785c41ec0bfec6fd12c2388d9234Jeff Brown                            sessionId, sessionStatus);
841a97f1edf7624785c41ec0bfec6fd12c2388d9234Jeff Brown                }
842a97f1edf7624785c41ec0bfec6fd12c2388d9234Jeff Brown            }
843a97f1edf7624785c41ec0bfec6fd12c2388d9234Jeff Brown        }
844a97f1edf7624785c41ec0bfec6fd12c2388d9234Jeff Brown    }
845a97f1edf7624785c41ec0bfec6fd12c2388d9234Jeff Brown
846a97f1edf7624785c41ec0bfec6fd12c2388d9234Jeff Brown    /**
847a97f1edf7624785c41ec0bfec6fd12c2388d9234Jeff Brown     * A callback that will receive media status updates.
848a97f1edf7624785c41ec0bfec6fd12c2388d9234Jeff Brown     */
849a97f1edf7624785c41ec0bfec6fd12c2388d9234Jeff Brown    public static abstract class StatusCallback {
850a97f1edf7624785c41ec0bfec6fd12c2388d9234Jeff Brown        /**
851a97f1edf7624785c41ec0bfec6fd12c2388d9234Jeff Brown         * Called when the status of a media item changes.
852a97f1edf7624785c41ec0bfec6fd12c2388d9234Jeff Brown         *
853a97f1edf7624785c41ec0bfec6fd12c2388d9234Jeff Brown         * @param data The result data bundle.
854a97f1edf7624785c41ec0bfec6fd12c2388d9234Jeff Brown         * @param sessionId The session id.
855a97f1edf7624785c41ec0bfec6fd12c2388d9234Jeff Brown         * @param sessionStatus The session status, or null if unknown.
856a97f1edf7624785c41ec0bfec6fd12c2388d9234Jeff Brown         * @param itemId The item id.
857a97f1edf7624785c41ec0bfec6fd12c2388d9234Jeff Brown         * @param itemStatus The item status.
858a97f1edf7624785c41ec0bfec6fd12c2388d9234Jeff Brown         */
859a97f1edf7624785c41ec0bfec6fd12c2388d9234Jeff Brown        public void onItemStatusChanged(Bundle data,
860a97f1edf7624785c41ec0bfec6fd12c2388d9234Jeff Brown                String sessionId, MediaSessionStatus sessionStatus,
861a97f1edf7624785c41ec0bfec6fd12c2388d9234Jeff Brown                String itemId, MediaItemStatus itemStatus) {
862a97f1edf7624785c41ec0bfec6fd12c2388d9234Jeff Brown        }
863a97f1edf7624785c41ec0bfec6fd12c2388d9234Jeff Brown
864a97f1edf7624785c41ec0bfec6fd12c2388d9234Jeff Brown        /**
865a97f1edf7624785c41ec0bfec6fd12c2388d9234Jeff Brown         * Called when the status of a media session changes.
866a97f1edf7624785c41ec0bfec6fd12c2388d9234Jeff Brown         *
867a97f1edf7624785c41ec0bfec6fd12c2388d9234Jeff Brown         * @param data The result data bundle.
868a97f1edf7624785c41ec0bfec6fd12c2388d9234Jeff Brown         * @param sessionId The session id.
869a97f1edf7624785c41ec0bfec6fd12c2388d9234Jeff Brown         * @param sessionStatus The session status, or null if unknown.
870a97f1edf7624785c41ec0bfec6fd12c2388d9234Jeff Brown         */
871a97f1edf7624785c41ec0bfec6fd12c2388d9234Jeff Brown        public void onSessionStatusChanged(Bundle data,
872a97f1edf7624785c41ec0bfec6fd12c2388d9234Jeff Brown                String sessionId, MediaSessionStatus sessionStatus) {
873a97f1edf7624785c41ec0bfec6fd12c2388d9234Jeff Brown        }
874a97f1edf7624785c41ec0bfec6fd12c2388d9234Jeff Brown
875a97f1edf7624785c41ec0bfec6fd12c2388d9234Jeff Brown        /**
876a97f1edf7624785c41ec0bfec6fd12c2388d9234Jeff Brown         * Called when the session of the remote playback client changes.
877a97f1edf7624785c41ec0bfec6fd12c2388d9234Jeff Brown         *
878a97f1edf7624785c41ec0bfec6fd12c2388d9234Jeff Brown         * @param sessionId The new session id.
879a97f1edf7624785c41ec0bfec6fd12c2388d9234Jeff Brown         */
880a97f1edf7624785c41ec0bfec6fd12c2388d9234Jeff Brown        public void onSessionChanged(String sessionId) {
881a97f1edf7624785c41ec0bfec6fd12c2388d9234Jeff Brown        }
882a97f1edf7624785c41ec0bfec6fd12c2388d9234Jeff Brown    }
883a97f1edf7624785c41ec0bfec6fd12c2388d9234Jeff Brown
884a97f1edf7624785c41ec0bfec6fd12c2388d9234Jeff Brown    /**
885a97f1edf7624785c41ec0bfec6fd12c2388d9234Jeff Brown     * Base callback type for remote playback requests.
886a97f1edf7624785c41ec0bfec6fd12c2388d9234Jeff Brown     */
887a97f1edf7624785c41ec0bfec6fd12c2388d9234Jeff Brown    public static abstract class ActionCallback {
888a97f1edf7624785c41ec0bfec6fd12c2388d9234Jeff Brown        /**
889a97f1edf7624785c41ec0bfec6fd12c2388d9234Jeff Brown         * Called when a media control request fails.
890a97f1edf7624785c41ec0bfec6fd12c2388d9234Jeff Brown         *
891a97f1edf7624785c41ec0bfec6fd12c2388d9234Jeff Brown         * @param error A localized error message which may be shown to the user, or null
892a97f1edf7624785c41ec0bfec6fd12c2388d9234Jeff Brown         * if the cause of the error is unclear.
893a97f1edf7624785c41ec0bfec6fd12c2388d9234Jeff Brown         * @param code The error code, or {@link MediaControlIntent#ERROR_UNKNOWN} if unknown.
894a97f1edf7624785c41ec0bfec6fd12c2388d9234Jeff Brown         * @param data The error data bundle, or null if none.
895a97f1edf7624785c41ec0bfec6fd12c2388d9234Jeff Brown         */
896a97f1edf7624785c41ec0bfec6fd12c2388d9234Jeff Brown        public void onError(String error, int code, Bundle data) {
897a97f1edf7624785c41ec0bfec6fd12c2388d9234Jeff Brown        }
898a97f1edf7624785c41ec0bfec6fd12c2388d9234Jeff Brown    }
899a97f1edf7624785c41ec0bfec6fd12c2388d9234Jeff Brown
900a97f1edf7624785c41ec0bfec6fd12c2388d9234Jeff Brown    /**
901a97f1edf7624785c41ec0bfec6fd12c2388d9234Jeff Brown     * Callback for remote playback requests that operate on items.
902a97f1edf7624785c41ec0bfec6fd12c2388d9234Jeff Brown     */
903a97f1edf7624785c41ec0bfec6fd12c2388d9234Jeff Brown    public static abstract class ItemActionCallback extends ActionCallback {
904a97f1edf7624785c41ec0bfec6fd12c2388d9234Jeff Brown        /**
905a97f1edf7624785c41ec0bfec6fd12c2388d9234Jeff Brown         * Called when the request succeeds.
906a97f1edf7624785c41ec0bfec6fd12c2388d9234Jeff Brown         *
907a97f1edf7624785c41ec0bfec6fd12c2388d9234Jeff Brown         * @param data The result data bundle.
908a97f1edf7624785c41ec0bfec6fd12c2388d9234Jeff Brown         * @param sessionId The session id.
909a97f1edf7624785c41ec0bfec6fd12c2388d9234Jeff Brown         * @param sessionStatus The session status, or null if unknown.
910a97f1edf7624785c41ec0bfec6fd12c2388d9234Jeff Brown         * @param itemId The item id.
911a97f1edf7624785c41ec0bfec6fd12c2388d9234Jeff Brown         * @param itemStatus The item status.
912a97f1edf7624785c41ec0bfec6fd12c2388d9234Jeff Brown         */
913a97f1edf7624785c41ec0bfec6fd12c2388d9234Jeff Brown        public void onResult(Bundle data, String sessionId, MediaSessionStatus sessionStatus,
914a97f1edf7624785c41ec0bfec6fd12c2388d9234Jeff Brown                String itemId, MediaItemStatus itemStatus) {
915a97f1edf7624785c41ec0bfec6fd12c2388d9234Jeff Brown        }
916a97f1edf7624785c41ec0bfec6fd12c2388d9234Jeff Brown    }
917a97f1edf7624785c41ec0bfec6fd12c2388d9234Jeff Brown
918a97f1edf7624785c41ec0bfec6fd12c2388d9234Jeff Brown    /**
919a97f1edf7624785c41ec0bfec6fd12c2388d9234Jeff Brown     * Callback for remote playback requests that operate on sessions.
920a97f1edf7624785c41ec0bfec6fd12c2388d9234Jeff Brown     */
921a97f1edf7624785c41ec0bfec6fd12c2388d9234Jeff Brown    public static abstract class SessionActionCallback extends ActionCallback {
922a97f1edf7624785c41ec0bfec6fd12c2388d9234Jeff Brown        /**
923a97f1edf7624785c41ec0bfec6fd12c2388d9234Jeff Brown         * Called when the request succeeds.
924a97f1edf7624785c41ec0bfec6fd12c2388d9234Jeff Brown         *
925a97f1edf7624785c41ec0bfec6fd12c2388d9234Jeff Brown         * @param data The result data bundle.
926a97f1edf7624785c41ec0bfec6fd12c2388d9234Jeff Brown         * @param sessionId The session id.
927a97f1edf7624785c41ec0bfec6fd12c2388d9234Jeff Brown         * @param sessionStatus The session status, or null if unknown.
928a97f1edf7624785c41ec0bfec6fd12c2388d9234Jeff Brown         */
929a97f1edf7624785c41ec0bfec6fd12c2388d9234Jeff Brown        public void onResult(Bundle data, String sessionId, MediaSessionStatus sessionStatus) {
930a97f1edf7624785c41ec0bfec6fd12c2388d9234Jeff Brown        }
931a97f1edf7624785c41ec0bfec6fd12c2388d9234Jeff Brown    }
932a97f1edf7624785c41ec0bfec6fd12c2388d9234Jeff Brown}
933