RemotePlayer.java revision 5d429bc3a8195d6f37cf2f7da0935972950539b4
1a6bf581f7a7a6326505569f0d1215d0ba84779d7Chong Zhang/*
2a6bf581f7a7a6326505569f0d1215d0ba84779d7Chong Zhang * Copyright (C) 2013 The Android Open Source Project
3a6bf581f7a7a6326505569f0d1215d0ba84779d7Chong Zhang *
4a6bf581f7a7a6326505569f0d1215d0ba84779d7Chong Zhang * Licensed under the Apache License, Version 2.0 (the "License");
5a6bf581f7a7a6326505569f0d1215d0ba84779d7Chong Zhang * you may not use this file except in compliance with the License.
6a6bf581f7a7a6326505569f0d1215d0ba84779d7Chong Zhang * You may obtain a copy of the License at
7a6bf581f7a7a6326505569f0d1215d0ba84779d7Chong Zhang *
8a6bf581f7a7a6326505569f0d1215d0ba84779d7Chong Zhang *      http://www.apache.org/licenses/LICENSE-2.0
9a6bf581f7a7a6326505569f0d1215d0ba84779d7Chong Zhang *
10a6bf581f7a7a6326505569f0d1215d0ba84779d7Chong Zhang * Unless required by applicable law or agreed to in writing, software
11a6bf581f7a7a6326505569f0d1215d0ba84779d7Chong Zhang * distributed under the License is distributed on an "AS IS" BASIS,
12a6bf581f7a7a6326505569f0d1215d0ba84779d7Chong Zhang * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13a6bf581f7a7a6326505569f0d1215d0ba84779d7Chong Zhang * See the License for the specific language governing permissions and
14a6bf581f7a7a6326505569f0d1215d0ba84779d7Chong Zhang * limitations under the License.
15a6bf581f7a7a6326505569f0d1215d0ba84779d7Chong Zhang */
16a6bf581f7a7a6326505569f0d1215d0ba84779d7Chong Zhang
17a6bf581f7a7a6326505569f0d1215d0ba84779d7Chong Zhangpackage com.example.android.supportv7.media;
18a6bf581f7a7a6326505569f0d1215d0ba84779d7Chong Zhang
19a6bf581f7a7a6326505569f0d1215d0ba84779d7Chong Zhangimport android.content.Context;
20a6bf581f7a7a6326505569f0d1215d0ba84779d7Chong Zhangimport android.content.Intent;
21cf61a6ed2bfa6141b832fdc40a9fbfb70af91416Chong Zhangimport android.graphics.Bitmap;
22a6bf581f7a7a6326505569f0d1215d0ba84779d7Chong Zhangimport android.net.Uri;
23a6bf581f7a7a6326505569f0d1215d0ba84779d7Chong Zhangimport android.os.Bundle;
24a6bf581f7a7a6326505569f0d1215d0ba84779d7Chong Zhangimport android.support.v7.media.MediaItemStatus;
25a6bf581f7a7a6326505569f0d1215d0ba84779d7Chong Zhangimport android.support.v7.media.MediaControlIntent;
26a6bf581f7a7a6326505569f0d1215d0ba84779d7Chong Zhangimport android.support.v7.media.MediaRouter.ControlRequestCallback;
27a6bf581f7a7a6326505569f0d1215d0ba84779d7Chong Zhangimport android.support.v7.media.MediaRouter.RouteInfo;
28a6bf581f7a7a6326505569f0d1215d0ba84779d7Chong Zhangimport android.support.v7.media.MediaSessionStatus;
29a6bf581f7a7a6326505569f0d1215d0ba84779d7Chong Zhangimport android.support.v7.media.RemotePlaybackClient;
30a6bf581f7a7a6326505569f0d1215d0ba84779d7Chong Zhangimport android.support.v7.media.RemotePlaybackClient.ItemActionCallback;
31a6bf581f7a7a6326505569f0d1215d0ba84779d7Chong Zhangimport android.support.v7.media.RemotePlaybackClient.SessionActionCallback;
32a6bf581f7a7a6326505569f0d1215d0ba84779d7Chong Zhangimport android.support.v7.media.RemotePlaybackClient.StatusCallback;
33a6bf581f7a7a6326505569f0d1215d0ba84779d7Chong Zhangimport android.util.Log;
34a6bf581f7a7a6326505569f0d1215d0ba84779d7Chong Zhang
35a6bf581f7a7a6326505569f0d1215d0ba84779d7Chong Zhangimport java.util.ArrayList;
36a6bf581f7a7a6326505569f0d1215d0ba84779d7Chong Zhangimport java.util.List;
37a6bf581f7a7a6326505569f0d1215d0ba84779d7Chong Zhang
38a6bf581f7a7a6326505569f0d1215d0ba84779d7Chong Zhang/**
39a6bf581f7a7a6326505569f0d1215d0ba84779d7Chong Zhang * Handles playback of media items using a remote route.
40a6bf581f7a7a6326505569f0d1215d0ba84779d7Chong Zhang *
41a6bf581f7a7a6326505569f0d1215d0ba84779d7Chong Zhang * This class is used as a backend by PlaybackManager to feed media items to
42a6bf581f7a7a6326505569f0d1215d0ba84779d7Chong Zhang * the remote route. When the remote route doesn't support queuing, media items
43a6bf581f7a7a6326505569f0d1215d0ba84779d7Chong Zhang * are fed one-at-a-time; otherwise media items are enqueued to the remote side.
44a6bf581f7a7a6326505569f0d1215d0ba84779d7Chong Zhang */
45a6bf581f7a7a6326505569f0d1215d0ba84779d7Chong Zhangpublic class RemotePlayer extends Player {
46a6bf581f7a7a6326505569f0d1215d0ba84779d7Chong Zhang    private static final String TAG = "RemotePlayer";
47a6bf581f7a7a6326505569f0d1215d0ba84779d7Chong Zhang    private static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG);
48a6bf581f7a7a6326505569f0d1215d0ba84779d7Chong Zhang    private Context mContext;
49a6bf581f7a7a6326505569f0d1215d0ba84779d7Chong Zhang    private RouteInfo mRoute;
50a6bf581f7a7a6326505569f0d1215d0ba84779d7Chong Zhang    private boolean mEnqueuePending;
51cf61a6ed2bfa6141b832fdc40a9fbfb70af91416Chong Zhang    private String mTrackInfo = "";
52cf61a6ed2bfa6141b832fdc40a9fbfb70af91416Chong Zhang    private Bitmap mSnapshot;
53a6bf581f7a7a6326505569f0d1215d0ba84779d7Chong Zhang    private List<PlaylistItem> mTempQueue = new ArrayList<PlaylistItem>();
54a6bf581f7a7a6326505569f0d1215d0ba84779d7Chong Zhang
55a6bf581f7a7a6326505569f0d1215d0ba84779d7Chong Zhang    private RemotePlaybackClient mClient;
56a6bf581f7a7a6326505569f0d1215d0ba84779d7Chong Zhang    private StatusCallback mStatusCallback = new StatusCallback() {
57a6bf581f7a7a6326505569f0d1215d0ba84779d7Chong Zhang        @Override
58a6bf581f7a7a6326505569f0d1215d0ba84779d7Chong Zhang        public void onItemStatusChanged(Bundle data,
59a6bf581f7a7a6326505569f0d1215d0ba84779d7Chong Zhang                String sessionId, MediaSessionStatus sessionStatus,
60a6bf581f7a7a6326505569f0d1215d0ba84779d7Chong Zhang                String itemId, MediaItemStatus itemStatus) {
61a6bf581f7a7a6326505569f0d1215d0ba84779d7Chong Zhang            logStatus("onItemStatusChanged", sessionId, sessionStatus, itemId, itemStatus);
62a6bf581f7a7a6326505569f0d1215d0ba84779d7Chong Zhang            if (mCallback != null) {
63a6bf581f7a7a6326505569f0d1215d0ba84779d7Chong Zhang                if (itemStatus.getPlaybackState() ==
64a6bf581f7a7a6326505569f0d1215d0ba84779d7Chong Zhang                        MediaItemStatus.PLAYBACK_STATE_FINISHED) {
65a6bf581f7a7a6326505569f0d1215d0ba84779d7Chong Zhang                    mCallback.onCompletion();
66a6bf581f7a7a6326505569f0d1215d0ba84779d7Chong Zhang                } else if (itemStatus.getPlaybackState() ==
67a6bf581f7a7a6326505569f0d1215d0ba84779d7Chong Zhang                        MediaItemStatus.PLAYBACK_STATE_ERROR) {
68a6bf581f7a7a6326505569f0d1215d0ba84779d7Chong Zhang                    mCallback.onError();
69a6bf581f7a7a6326505569f0d1215d0ba84779d7Chong Zhang                }
70a6bf581f7a7a6326505569f0d1215d0ba84779d7Chong Zhang            }
71a6bf581f7a7a6326505569f0d1215d0ba84779d7Chong Zhang        }
72a6bf581f7a7a6326505569f0d1215d0ba84779d7Chong Zhang
73a6bf581f7a7a6326505569f0d1215d0ba84779d7Chong Zhang        @Override
74a6bf581f7a7a6326505569f0d1215d0ba84779d7Chong Zhang        public void onSessionStatusChanged(Bundle data,
75a6bf581f7a7a6326505569f0d1215d0ba84779d7Chong Zhang                String sessionId, MediaSessionStatus sessionStatus) {
76a6bf581f7a7a6326505569f0d1215d0ba84779d7Chong Zhang            logStatus("onSessionStatusChanged", sessionId, sessionStatus, null, null);
77a6bf581f7a7a6326505569f0d1215d0ba84779d7Chong Zhang            if (mCallback != null) {
78a6bf581f7a7a6326505569f0d1215d0ba84779d7Chong Zhang                mCallback.onPlaylistChanged();
79a6bf581f7a7a6326505569f0d1215d0ba84779d7Chong Zhang            }
80a6bf581f7a7a6326505569f0d1215d0ba84779d7Chong Zhang        }
81a6bf581f7a7a6326505569f0d1215d0ba84779d7Chong Zhang
82a6bf581f7a7a6326505569f0d1215d0ba84779d7Chong Zhang        @Override
83a6bf581f7a7a6326505569f0d1215d0ba84779d7Chong Zhang        public void onSessionChanged(String sessionId) {
84a6bf581f7a7a6326505569f0d1215d0ba84779d7Chong Zhang            if (DEBUG) {
85a6bf581f7a7a6326505569f0d1215d0ba84779d7Chong Zhang                Log.d(TAG, "onSessionChanged: sessionId=" + sessionId);
86a6bf581f7a7a6326505569f0d1215d0ba84779d7Chong Zhang            }
87a6bf581f7a7a6326505569f0d1215d0ba84779d7Chong Zhang        }
88a6bf581f7a7a6326505569f0d1215d0ba84779d7Chong Zhang    };
89a6bf581f7a7a6326505569f0d1215d0ba84779d7Chong Zhang
90a6bf581f7a7a6326505569f0d1215d0ba84779d7Chong Zhang    public RemotePlayer(Context context) {
91a6bf581f7a7a6326505569f0d1215d0ba84779d7Chong Zhang        mContext = context;
92a6bf581f7a7a6326505569f0d1215d0ba84779d7Chong Zhang    }
93a6bf581f7a7a6326505569f0d1215d0ba84779d7Chong Zhang
94a6bf581f7a7a6326505569f0d1215d0ba84779d7Chong Zhang    @Override
95a6bf581f7a7a6326505569f0d1215d0ba84779d7Chong Zhang    public boolean isRemotePlayback() {
96a6bf581f7a7a6326505569f0d1215d0ba84779d7Chong Zhang        return true;
97a6bf581f7a7a6326505569f0d1215d0ba84779d7Chong Zhang    }
98a6bf581f7a7a6326505569f0d1215d0ba84779d7Chong Zhang
99a6bf581f7a7a6326505569f0d1215d0ba84779d7Chong Zhang    @Override
100a6bf581f7a7a6326505569f0d1215d0ba84779d7Chong Zhang    public boolean isQueuingSupported() {
101a6bf581f7a7a6326505569f0d1215d0ba84779d7Chong Zhang        return mClient.isQueuingSupported();
102a6bf581f7a7a6326505569f0d1215d0ba84779d7Chong Zhang    }
103a6bf581f7a7a6326505569f0d1215d0ba84779d7Chong Zhang
104a6bf581f7a7a6326505569f0d1215d0ba84779d7Chong Zhang    @Override
105a6bf581f7a7a6326505569f0d1215d0ba84779d7Chong Zhang    public void connect(RouteInfo route) {
106a6bf581f7a7a6326505569f0d1215d0ba84779d7Chong Zhang        mRoute = route;
107a6bf581f7a7a6326505569f0d1215d0ba84779d7Chong Zhang        mClient = new RemotePlaybackClient(mContext, route);
108a6bf581f7a7a6326505569f0d1215d0ba84779d7Chong Zhang        mClient.setStatusCallback(mStatusCallback);
109a6bf581f7a7a6326505569f0d1215d0ba84779d7Chong Zhang
110a6bf581f7a7a6326505569f0d1215d0ba84779d7Chong Zhang        if (DEBUG) {
111a6bf581f7a7a6326505569f0d1215d0ba84779d7Chong Zhang            Log.d(TAG, "connected to: " + route
112a6bf581f7a7a6326505569f0d1215d0ba84779d7Chong Zhang                    + ", isRemotePlaybackSupported: " + mClient.isRemotePlaybackSupported()
113a6bf581f7a7a6326505569f0d1215d0ba84779d7Chong Zhang                    + ", isQueuingSupported: "+ mClient.isQueuingSupported());
114a6bf581f7a7a6326505569f0d1215d0ba84779d7Chong Zhang        }
115a6bf581f7a7a6326505569f0d1215d0ba84779d7Chong Zhang    }
116a6bf581f7a7a6326505569f0d1215d0ba84779d7Chong Zhang
117a6bf581f7a7a6326505569f0d1215d0ba84779d7Chong Zhang    @Override
118a6bf581f7a7a6326505569f0d1215d0ba84779d7Chong Zhang    public void release() {
119a6bf581f7a7a6326505569f0d1215d0ba84779d7Chong Zhang        mClient.release();
120a6bf581f7a7a6326505569f0d1215d0ba84779d7Chong Zhang
121a6bf581f7a7a6326505569f0d1215d0ba84779d7Chong Zhang        if (DEBUG) {
122a6bf581f7a7a6326505569f0d1215d0ba84779d7Chong Zhang            Log.d(TAG, "released.");
123a6bf581f7a7a6326505569f0d1215d0ba84779d7Chong Zhang        }
124a6bf581f7a7a6326505569f0d1215d0ba84779d7Chong Zhang    }
125a6bf581f7a7a6326505569f0d1215d0ba84779d7Chong Zhang
126a6bf581f7a7a6326505569f0d1215d0ba84779d7Chong Zhang    // basic playback operations that are always supported
127a6bf581f7a7a6326505569f0d1215d0ba84779d7Chong Zhang    @Override
128a6bf581f7a7a6326505569f0d1215d0ba84779d7Chong Zhang    public void play(final PlaylistItem item) {
129a6bf581f7a7a6326505569f0d1215d0ba84779d7Chong Zhang        if (DEBUG) {
130a6bf581f7a7a6326505569f0d1215d0ba84779d7Chong Zhang            Log.d(TAG, "play: item=" + item);
131a6bf581f7a7a6326505569f0d1215d0ba84779d7Chong Zhang        }
132a6bf581f7a7a6326505569f0d1215d0ba84779d7Chong Zhang        mClient.play(item.getUri(), "video/mp4", null, 0, null, new ItemActionCallback() {
133a6bf581f7a7a6326505569f0d1215d0ba84779d7Chong Zhang            @Override
134a6bf581f7a7a6326505569f0d1215d0ba84779d7Chong Zhang            public void onResult(Bundle data, String sessionId, MediaSessionStatus sessionStatus,
135a6bf581f7a7a6326505569f0d1215d0ba84779d7Chong Zhang                    String itemId, MediaItemStatus itemStatus) {
136a6bf581f7a7a6326505569f0d1215d0ba84779d7Chong Zhang                logStatus("play: succeeded", sessionId, sessionStatus, itemId, itemStatus);
137a6bf581f7a7a6326505569f0d1215d0ba84779d7Chong Zhang                item.setRemoteItemId(itemId);
138a6bf581f7a7a6326505569f0d1215d0ba84779d7Chong Zhang                if (item.getPosition() > 0) {
139a6bf581f7a7a6326505569f0d1215d0ba84779d7Chong Zhang                    seekInternal(item);
140a6bf581f7a7a6326505569f0d1215d0ba84779d7Chong Zhang                }
141a6bf581f7a7a6326505569f0d1215d0ba84779d7Chong Zhang                if (item.getState() == MediaItemStatus.PLAYBACK_STATE_PAUSED) {
142a6bf581f7a7a6326505569f0d1215d0ba84779d7Chong Zhang                    pause();
1435d429bc3a8195d6f37cf2f7da0935972950539b4RoboErik                    publishState(STATE_PAUSED);
1445d429bc3a8195d6f37cf2f7da0935972950539b4RoboErik                } else {
1455d429bc3a8195d6f37cf2f7da0935972950539b4RoboErik                    publishState(STATE_PLAYING);
146a6bf581f7a7a6326505569f0d1215d0ba84779d7Chong Zhang                }
147a6bf581f7a7a6326505569f0d1215d0ba84779d7Chong Zhang                if (mCallback != null) {
148a6bf581f7a7a6326505569f0d1215d0ba84779d7Chong Zhang                    mCallback.onPlaylistChanged();
149a6bf581f7a7a6326505569f0d1215d0ba84779d7Chong Zhang                }
150a6bf581f7a7a6326505569f0d1215d0ba84779d7Chong Zhang            }
151a6bf581f7a7a6326505569f0d1215d0ba84779d7Chong Zhang
152a6bf581f7a7a6326505569f0d1215d0ba84779d7Chong Zhang            @Override
153a6bf581f7a7a6326505569f0d1215d0ba84779d7Chong Zhang            public void onError(String error, int code, Bundle data) {
154a6bf581f7a7a6326505569f0d1215d0ba84779d7Chong Zhang                logError("play: failed", error, code);
155a6bf581f7a7a6326505569f0d1215d0ba84779d7Chong Zhang            }
156a6bf581f7a7a6326505569f0d1215d0ba84779d7Chong Zhang        });
157a6bf581f7a7a6326505569f0d1215d0ba84779d7Chong Zhang    }
158a6bf581f7a7a6326505569f0d1215d0ba84779d7Chong Zhang
159a6bf581f7a7a6326505569f0d1215d0ba84779d7Chong Zhang    @Override
160a6bf581f7a7a6326505569f0d1215d0ba84779d7Chong Zhang    public void seek(final PlaylistItem item) {
161a6bf581f7a7a6326505569f0d1215d0ba84779d7Chong Zhang        seekInternal(item);
162a6bf581f7a7a6326505569f0d1215d0ba84779d7Chong Zhang    }
163a6bf581f7a7a6326505569f0d1215d0ba84779d7Chong Zhang
164a6bf581f7a7a6326505569f0d1215d0ba84779d7Chong Zhang    @Override
165a6bf581f7a7a6326505569f0d1215d0ba84779d7Chong Zhang    public void getStatus(final PlaylistItem item, final boolean update) {
166a6bf581f7a7a6326505569f0d1215d0ba84779d7Chong Zhang        if (!mClient.hasSession() || item.getRemoteItemId() == null) {
167a6bf581f7a7a6326505569f0d1215d0ba84779d7Chong Zhang            // if session is not valid or item id not assigend yet.
168a6bf581f7a7a6326505569f0d1215d0ba84779d7Chong Zhang            // just return, it's not fatal
169a6bf581f7a7a6326505569f0d1215d0ba84779d7Chong Zhang            return;
170a6bf581f7a7a6326505569f0d1215d0ba84779d7Chong Zhang        }
171a6bf581f7a7a6326505569f0d1215d0ba84779d7Chong Zhang
172a6bf581f7a7a6326505569f0d1215d0ba84779d7Chong Zhang        if (DEBUG) {
173a6bf581f7a7a6326505569f0d1215d0ba84779d7Chong Zhang            Log.d(TAG, "getStatus: item=" + item + ", update=" + update);
174a6bf581f7a7a6326505569f0d1215d0ba84779d7Chong Zhang        }
175a6bf581f7a7a6326505569f0d1215d0ba84779d7Chong Zhang        mClient.getStatus(item.getRemoteItemId(), null, new ItemActionCallback() {
176a6bf581f7a7a6326505569f0d1215d0ba84779d7Chong Zhang            @Override
177a6bf581f7a7a6326505569f0d1215d0ba84779d7Chong Zhang            public void onResult(Bundle data, String sessionId, MediaSessionStatus sessionStatus,
178a6bf581f7a7a6326505569f0d1215d0ba84779d7Chong Zhang                    String itemId, MediaItemStatus itemStatus) {
179a6bf581f7a7a6326505569f0d1215d0ba84779d7Chong Zhang                logStatus("getStatus: succeeded", sessionId, sessionStatus, itemId, itemStatus);
180a6bf581f7a7a6326505569f0d1215d0ba84779d7Chong Zhang                int state = itemStatus.getPlaybackState();
181a6bf581f7a7a6326505569f0d1215d0ba84779d7Chong Zhang                if (state == MediaItemStatus.PLAYBACK_STATE_PLAYING
182a6bf581f7a7a6326505569f0d1215d0ba84779d7Chong Zhang                        || state == MediaItemStatus.PLAYBACK_STATE_PAUSED
183a6bf581f7a7a6326505569f0d1215d0ba84779d7Chong Zhang                        || state == MediaItemStatus.PLAYBACK_STATE_PENDING) {
184a6bf581f7a7a6326505569f0d1215d0ba84779d7Chong Zhang                    item.setState(state);
185a6bf581f7a7a6326505569f0d1215d0ba84779d7Chong Zhang                    item.setPosition(itemStatus.getContentPosition());
186a6bf581f7a7a6326505569f0d1215d0ba84779d7Chong Zhang                    item.setDuration(itemStatus.getContentDuration());
187a6bf581f7a7a6326505569f0d1215d0ba84779d7Chong Zhang                    item.setTimestamp(itemStatus.getTimestamp());
188a6bf581f7a7a6326505569f0d1215d0ba84779d7Chong Zhang                }
189a6bf581f7a7a6326505569f0d1215d0ba84779d7Chong Zhang                if (update && mCallback != null) {
190a6bf581f7a7a6326505569f0d1215d0ba84779d7Chong Zhang                    mCallback.onPlaylistReady();
191a6bf581f7a7a6326505569f0d1215d0ba84779d7Chong Zhang                }
192a6bf581f7a7a6326505569f0d1215d0ba84779d7Chong Zhang            }
193a6bf581f7a7a6326505569f0d1215d0ba84779d7Chong Zhang
194a6bf581f7a7a6326505569f0d1215d0ba84779d7Chong Zhang            @Override
195a6bf581f7a7a6326505569f0d1215d0ba84779d7Chong Zhang            public void onError(String error, int code, Bundle data) {
196a6bf581f7a7a6326505569f0d1215d0ba84779d7Chong Zhang                logError("getStatus: failed", error, code);
197a6bf581f7a7a6326505569f0d1215d0ba84779d7Chong Zhang                if (update && mCallback != null) {
198a6bf581f7a7a6326505569f0d1215d0ba84779d7Chong Zhang                    mCallback.onPlaylistReady();
199a6bf581f7a7a6326505569f0d1215d0ba84779d7Chong Zhang                }
200a6bf581f7a7a6326505569f0d1215d0ba84779d7Chong Zhang            }
201a6bf581f7a7a6326505569f0d1215d0ba84779d7Chong Zhang        });
202a6bf581f7a7a6326505569f0d1215d0ba84779d7Chong Zhang    }
203a6bf581f7a7a6326505569f0d1215d0ba84779d7Chong Zhang
204a6bf581f7a7a6326505569f0d1215d0ba84779d7Chong Zhang    @Override
205a6bf581f7a7a6326505569f0d1215d0ba84779d7Chong Zhang    public void pause() {
206a6bf581f7a7a6326505569f0d1215d0ba84779d7Chong Zhang        if (!mClient.hasSession()) {
207a6bf581f7a7a6326505569f0d1215d0ba84779d7Chong Zhang            // ignore if no session
208a6bf581f7a7a6326505569f0d1215d0ba84779d7Chong Zhang            return;
209a6bf581f7a7a6326505569f0d1215d0ba84779d7Chong Zhang        }
210a6bf581f7a7a6326505569f0d1215d0ba84779d7Chong Zhang        if (DEBUG) {
211a6bf581f7a7a6326505569f0d1215d0ba84779d7Chong Zhang            Log.d(TAG, "pause");
212a6bf581f7a7a6326505569f0d1215d0ba84779d7Chong Zhang        }
213a6bf581f7a7a6326505569f0d1215d0ba84779d7Chong Zhang        mClient.pause(null, new SessionActionCallback() {
214a6bf581f7a7a6326505569f0d1215d0ba84779d7Chong Zhang            @Override
215a6bf581f7a7a6326505569f0d1215d0ba84779d7Chong Zhang            public void onResult(Bundle data, String sessionId, MediaSessionStatus sessionStatus) {
216a6bf581f7a7a6326505569f0d1215d0ba84779d7Chong Zhang                logStatus("pause: succeeded", sessionId, sessionStatus, null, null);
217a6bf581f7a7a6326505569f0d1215d0ba84779d7Chong Zhang                if (mCallback != null) {
218a6bf581f7a7a6326505569f0d1215d0ba84779d7Chong Zhang                    mCallback.onPlaylistChanged();
219a6bf581f7a7a6326505569f0d1215d0ba84779d7Chong Zhang                }
2205d429bc3a8195d6f37cf2f7da0935972950539b4RoboErik                publishState(STATE_PAUSED);
221a6bf581f7a7a6326505569f0d1215d0ba84779d7Chong Zhang            }
222a6bf581f7a7a6326505569f0d1215d0ba84779d7Chong Zhang
223a6bf581f7a7a6326505569f0d1215d0ba84779d7Chong Zhang            @Override
224a6bf581f7a7a6326505569f0d1215d0ba84779d7Chong Zhang            public void onError(String error, int code, Bundle data) {
225a6bf581f7a7a6326505569f0d1215d0ba84779d7Chong Zhang                logError("pause: failed", error, code);
226a6bf581f7a7a6326505569f0d1215d0ba84779d7Chong Zhang            }
227a6bf581f7a7a6326505569f0d1215d0ba84779d7Chong Zhang        });
228a6bf581f7a7a6326505569f0d1215d0ba84779d7Chong Zhang    }
229a6bf581f7a7a6326505569f0d1215d0ba84779d7Chong Zhang
230a6bf581f7a7a6326505569f0d1215d0ba84779d7Chong Zhang    @Override
231a6bf581f7a7a6326505569f0d1215d0ba84779d7Chong Zhang    public void resume() {
232a6bf581f7a7a6326505569f0d1215d0ba84779d7Chong Zhang        if (!mClient.hasSession()) {
233a6bf581f7a7a6326505569f0d1215d0ba84779d7Chong Zhang            // ignore if no session
234a6bf581f7a7a6326505569f0d1215d0ba84779d7Chong Zhang            return;
235a6bf581f7a7a6326505569f0d1215d0ba84779d7Chong Zhang        }
236a6bf581f7a7a6326505569f0d1215d0ba84779d7Chong Zhang        if (DEBUG) {
237a6bf581f7a7a6326505569f0d1215d0ba84779d7Chong Zhang            Log.d(TAG, "resume");
238a6bf581f7a7a6326505569f0d1215d0ba84779d7Chong Zhang        }
239a6bf581f7a7a6326505569f0d1215d0ba84779d7Chong Zhang        mClient.resume(null, new SessionActionCallback() {
240a6bf581f7a7a6326505569f0d1215d0ba84779d7Chong Zhang            @Override
241a6bf581f7a7a6326505569f0d1215d0ba84779d7Chong Zhang            public void onResult(Bundle data, String sessionId, MediaSessionStatus sessionStatus) {
242a6bf581f7a7a6326505569f0d1215d0ba84779d7Chong Zhang                logStatus("resume: succeeded", sessionId, sessionStatus, null, null);
243a6bf581f7a7a6326505569f0d1215d0ba84779d7Chong Zhang                if (mCallback != null) {
244a6bf581f7a7a6326505569f0d1215d0ba84779d7Chong Zhang                    mCallback.onPlaylistChanged();
245a6bf581f7a7a6326505569f0d1215d0ba84779d7Chong Zhang                }
2465d429bc3a8195d6f37cf2f7da0935972950539b4RoboErik                publishState(STATE_PLAYING);
247a6bf581f7a7a6326505569f0d1215d0ba84779d7Chong Zhang            }
248a6bf581f7a7a6326505569f0d1215d0ba84779d7Chong Zhang
249a6bf581f7a7a6326505569f0d1215d0ba84779d7Chong Zhang            @Override
250a6bf581f7a7a6326505569f0d1215d0ba84779d7Chong Zhang            public void onError(String error, int code, Bundle data) {
251a6bf581f7a7a6326505569f0d1215d0ba84779d7Chong Zhang                logError("resume: failed", error, code);
252a6bf581f7a7a6326505569f0d1215d0ba84779d7Chong Zhang            }
253a6bf581f7a7a6326505569f0d1215d0ba84779d7Chong Zhang        });
254a6bf581f7a7a6326505569f0d1215d0ba84779d7Chong Zhang    }
255a6bf581f7a7a6326505569f0d1215d0ba84779d7Chong Zhang
256a6bf581f7a7a6326505569f0d1215d0ba84779d7Chong Zhang    @Override
257a6bf581f7a7a6326505569f0d1215d0ba84779d7Chong Zhang    public void stop() {
258a6bf581f7a7a6326505569f0d1215d0ba84779d7Chong Zhang        if (!mClient.hasSession()) {
259a6bf581f7a7a6326505569f0d1215d0ba84779d7Chong Zhang            // ignore if no session
260a6bf581f7a7a6326505569f0d1215d0ba84779d7Chong Zhang            return;
261a6bf581f7a7a6326505569f0d1215d0ba84779d7Chong Zhang        }
2625d429bc3a8195d6f37cf2f7da0935972950539b4RoboErik        publishState(STATE_IDLE);
263a6bf581f7a7a6326505569f0d1215d0ba84779d7Chong Zhang        if (DEBUG) {
264a6bf581f7a7a6326505569f0d1215d0ba84779d7Chong Zhang            Log.d(TAG, "stop");
265a6bf581f7a7a6326505569f0d1215d0ba84779d7Chong Zhang        }
266a6bf581f7a7a6326505569f0d1215d0ba84779d7Chong Zhang        mClient.stop(null, new SessionActionCallback() {
267a6bf581f7a7a6326505569f0d1215d0ba84779d7Chong Zhang            @Override
268a6bf581f7a7a6326505569f0d1215d0ba84779d7Chong Zhang            public void onResult(Bundle data, String sessionId, MediaSessionStatus sessionStatus) {
269a6bf581f7a7a6326505569f0d1215d0ba84779d7Chong Zhang                logStatus("stop: succeeded", sessionId, sessionStatus, null, null);
270a6bf581f7a7a6326505569f0d1215d0ba84779d7Chong Zhang                if (mClient.isSessionManagementSupported()) {
271a6bf581f7a7a6326505569f0d1215d0ba84779d7Chong Zhang                    endSession();
272a6bf581f7a7a6326505569f0d1215d0ba84779d7Chong Zhang                }
273a6bf581f7a7a6326505569f0d1215d0ba84779d7Chong Zhang                if (mCallback != null) {
274a6bf581f7a7a6326505569f0d1215d0ba84779d7Chong Zhang                    mCallback.onPlaylistChanged();
275a6bf581f7a7a6326505569f0d1215d0ba84779d7Chong Zhang                }
276a6bf581f7a7a6326505569f0d1215d0ba84779d7Chong Zhang            }
277a6bf581f7a7a6326505569f0d1215d0ba84779d7Chong Zhang
278a6bf581f7a7a6326505569f0d1215d0ba84779d7Chong Zhang            @Override
279a6bf581f7a7a6326505569f0d1215d0ba84779d7Chong Zhang            public void onError(String error, int code, Bundle data) {
280a6bf581f7a7a6326505569f0d1215d0ba84779d7Chong Zhang                logError("stop: failed", error, code);
281a6bf581f7a7a6326505569f0d1215d0ba84779d7Chong Zhang            }
282a6bf581f7a7a6326505569f0d1215d0ba84779d7Chong Zhang        });
283a6bf581f7a7a6326505569f0d1215d0ba84779d7Chong Zhang    }
284a6bf581f7a7a6326505569f0d1215d0ba84779d7Chong Zhang
285a6bf581f7a7a6326505569f0d1215d0ba84779d7Chong Zhang    // enqueue & remove are only supported if isQueuingSupported() returns true
286a6bf581f7a7a6326505569f0d1215d0ba84779d7Chong Zhang    @Override
287a6bf581f7a7a6326505569f0d1215d0ba84779d7Chong Zhang    public void enqueue(final PlaylistItem item) {
288a6bf581f7a7a6326505569f0d1215d0ba84779d7Chong Zhang        throwIfQueuingUnsupported();
289a6bf581f7a7a6326505569f0d1215d0ba84779d7Chong Zhang
290a6bf581f7a7a6326505569f0d1215d0ba84779d7Chong Zhang        if (!mClient.hasSession() && !mEnqueuePending) {
291a6bf581f7a7a6326505569f0d1215d0ba84779d7Chong Zhang            mEnqueuePending = true;
292a6bf581f7a7a6326505569f0d1215d0ba84779d7Chong Zhang            if (mClient.isSessionManagementSupported()) {
293a6bf581f7a7a6326505569f0d1215d0ba84779d7Chong Zhang                startSession(item);
294a6bf581f7a7a6326505569f0d1215d0ba84779d7Chong Zhang            } else {
295a6bf581f7a7a6326505569f0d1215d0ba84779d7Chong Zhang                enqueueInternal(item);
296a6bf581f7a7a6326505569f0d1215d0ba84779d7Chong Zhang            }
297a6bf581f7a7a6326505569f0d1215d0ba84779d7Chong Zhang        } else if (mEnqueuePending){
298a6bf581f7a7a6326505569f0d1215d0ba84779d7Chong Zhang            mTempQueue.add(item);
299a6bf581f7a7a6326505569f0d1215d0ba84779d7Chong Zhang        } else {
300a6bf581f7a7a6326505569f0d1215d0ba84779d7Chong Zhang            enqueueInternal(item);
301a6bf581f7a7a6326505569f0d1215d0ba84779d7Chong Zhang        }
302a6bf581f7a7a6326505569f0d1215d0ba84779d7Chong Zhang    }
303a6bf581f7a7a6326505569f0d1215d0ba84779d7Chong Zhang
304a6bf581f7a7a6326505569f0d1215d0ba84779d7Chong Zhang    @Override
305a6bf581f7a7a6326505569f0d1215d0ba84779d7Chong Zhang    public PlaylistItem remove(String itemId) {
306a6bf581f7a7a6326505569f0d1215d0ba84779d7Chong Zhang        throwIfNoSession();
307a6bf581f7a7a6326505569f0d1215d0ba84779d7Chong Zhang        throwIfQueuingUnsupported();
308a6bf581f7a7a6326505569f0d1215d0ba84779d7Chong Zhang
309a6bf581f7a7a6326505569f0d1215d0ba84779d7Chong Zhang        if (DEBUG) {
310a6bf581f7a7a6326505569f0d1215d0ba84779d7Chong Zhang            Log.d(TAG, "remove: itemId=" + itemId);
311a6bf581f7a7a6326505569f0d1215d0ba84779d7Chong Zhang        }
312a6bf581f7a7a6326505569f0d1215d0ba84779d7Chong Zhang        mClient.remove(itemId, null, new ItemActionCallback() {
313a6bf581f7a7a6326505569f0d1215d0ba84779d7Chong Zhang            @Override
314a6bf581f7a7a6326505569f0d1215d0ba84779d7Chong Zhang            public void onResult(Bundle data, String sessionId, MediaSessionStatus sessionStatus,
315a6bf581f7a7a6326505569f0d1215d0ba84779d7Chong Zhang                    String itemId, MediaItemStatus itemStatus) {
316a6bf581f7a7a6326505569f0d1215d0ba84779d7Chong Zhang                logStatus("remove: succeeded", sessionId, sessionStatus, itemId, itemStatus);
317a6bf581f7a7a6326505569f0d1215d0ba84779d7Chong Zhang            }
318a6bf581f7a7a6326505569f0d1215d0ba84779d7Chong Zhang
319a6bf581f7a7a6326505569f0d1215d0ba84779d7Chong Zhang            @Override
320a6bf581f7a7a6326505569f0d1215d0ba84779d7Chong Zhang            public void onError(String error, int code, Bundle data) {
321a6bf581f7a7a6326505569f0d1215d0ba84779d7Chong Zhang                logError("remove: failed", error, code);
322a6bf581f7a7a6326505569f0d1215d0ba84779d7Chong Zhang            }
323a6bf581f7a7a6326505569f0d1215d0ba84779d7Chong Zhang        });
324a6bf581f7a7a6326505569f0d1215d0ba84779d7Chong Zhang
325a6bf581f7a7a6326505569f0d1215d0ba84779d7Chong Zhang        return null;
326a6bf581f7a7a6326505569f0d1215d0ba84779d7Chong Zhang    }
327a6bf581f7a7a6326505569f0d1215d0ba84779d7Chong Zhang
328a6bf581f7a7a6326505569f0d1215d0ba84779d7Chong Zhang    @Override
329cf61a6ed2bfa6141b832fdc40a9fbfb70af91416Chong Zhang    public void updateTrackInfo() {
330a6bf581f7a7a6326505569f0d1215d0ba84779d7Chong Zhang        // clear stats info first
331cf61a6ed2bfa6141b832fdc40a9fbfb70af91416Chong Zhang        mTrackInfo = "";
332cf61a6ed2bfa6141b832fdc40a9fbfb70af91416Chong Zhang        mSnapshot = null;
333a6bf581f7a7a6326505569f0d1215d0ba84779d7Chong Zhang
334cf61a6ed2bfa6141b832fdc40a9fbfb70af91416Chong Zhang        Intent intent = new Intent(SampleMediaRouteProvider.ACTION_GET_TRACK_INFO);
335a6bf581f7a7a6326505569f0d1215d0ba84779d7Chong Zhang        intent.addCategory(SampleMediaRouteProvider.CATEGORY_SAMPLE_ROUTE);
336a6bf581f7a7a6326505569f0d1215d0ba84779d7Chong Zhang
337a6bf581f7a7a6326505569f0d1215d0ba84779d7Chong Zhang        if (mRoute != null && mRoute.supportsControlRequest(intent)) {
338a6bf581f7a7a6326505569f0d1215d0ba84779d7Chong Zhang            ControlRequestCallback callback = new ControlRequestCallback() {
339a6bf581f7a7a6326505569f0d1215d0ba84779d7Chong Zhang                @Override
340a6bf581f7a7a6326505569f0d1215d0ba84779d7Chong Zhang                public void onResult(Bundle data) {
341a6bf581f7a7a6326505569f0d1215d0ba84779d7Chong Zhang                    if (DEBUG) {
342a6bf581f7a7a6326505569f0d1215d0ba84779d7Chong Zhang                        Log.d(TAG, "getStatistics: succeeded: data=" + data);
343a6bf581f7a7a6326505569f0d1215d0ba84779d7Chong Zhang                    }
344a6bf581f7a7a6326505569f0d1215d0ba84779d7Chong Zhang                    if (data != null) {
345cf61a6ed2bfa6141b832fdc40a9fbfb70af91416Chong Zhang                        mTrackInfo = data.getString(SampleMediaRouteProvider.TRACK_INFO_DESC);
346cf61a6ed2bfa6141b832fdc40a9fbfb70af91416Chong Zhang                        mSnapshot = data.getParcelable(
347cf61a6ed2bfa6141b832fdc40a9fbfb70af91416Chong Zhang                                SampleMediaRouteProvider.TRACK_INFO_SNAPSHOT);
348a6bf581f7a7a6326505569f0d1215d0ba84779d7Chong Zhang                    }
349a6bf581f7a7a6326505569f0d1215d0ba84779d7Chong Zhang                }
350a6bf581f7a7a6326505569f0d1215d0ba84779d7Chong Zhang
351a6bf581f7a7a6326505569f0d1215d0ba84779d7Chong Zhang                @Override
352a6bf581f7a7a6326505569f0d1215d0ba84779d7Chong Zhang                public void onError(String error, Bundle data) {
353a6bf581f7a7a6326505569f0d1215d0ba84779d7Chong Zhang                    Log.d(TAG, "getStatistics: failed: error=" + error + ", data=" + data);
354a6bf581f7a7a6326505569f0d1215d0ba84779d7Chong Zhang                }
355a6bf581f7a7a6326505569f0d1215d0ba84779d7Chong Zhang            };
356a6bf581f7a7a6326505569f0d1215d0ba84779d7Chong Zhang
357a6bf581f7a7a6326505569f0d1215d0ba84779d7Chong Zhang            mRoute.sendControlRequest(intent, callback);
358a6bf581f7a7a6326505569f0d1215d0ba84779d7Chong Zhang        }
359a6bf581f7a7a6326505569f0d1215d0ba84779d7Chong Zhang    }
360a6bf581f7a7a6326505569f0d1215d0ba84779d7Chong Zhang
361a6bf581f7a7a6326505569f0d1215d0ba84779d7Chong Zhang    @Override
362cf61a6ed2bfa6141b832fdc40a9fbfb70af91416Chong Zhang    public String getDescription() {
363cf61a6ed2bfa6141b832fdc40a9fbfb70af91416Chong Zhang        return mTrackInfo;
364cf61a6ed2bfa6141b832fdc40a9fbfb70af91416Chong Zhang    }
365cf61a6ed2bfa6141b832fdc40a9fbfb70af91416Chong Zhang
366cf61a6ed2bfa6141b832fdc40a9fbfb70af91416Chong Zhang    @Override
367cf61a6ed2bfa6141b832fdc40a9fbfb70af91416Chong Zhang    public Bitmap getSnapshot() {
368cf61a6ed2bfa6141b832fdc40a9fbfb70af91416Chong Zhang        return mSnapshot;
369a6bf581f7a7a6326505569f0d1215d0ba84779d7Chong Zhang    }
370a6bf581f7a7a6326505569f0d1215d0ba84779d7Chong Zhang
371a6bf581f7a7a6326505569f0d1215d0ba84779d7Chong Zhang    private void enqueueInternal(final PlaylistItem item) {
372a6bf581f7a7a6326505569f0d1215d0ba84779d7Chong Zhang        throwIfQueuingUnsupported();
373a6bf581f7a7a6326505569f0d1215d0ba84779d7Chong Zhang
374a6bf581f7a7a6326505569f0d1215d0ba84779d7Chong Zhang        if (DEBUG) {
375a6bf581f7a7a6326505569f0d1215d0ba84779d7Chong Zhang            Log.d(TAG, "enqueue: item=" + item);
376a6bf581f7a7a6326505569f0d1215d0ba84779d7Chong Zhang        }
377a6bf581f7a7a6326505569f0d1215d0ba84779d7Chong Zhang        mClient.enqueue(item.getUri(), "video/mp4", null, 0, null, new ItemActionCallback() {
378a6bf581f7a7a6326505569f0d1215d0ba84779d7Chong Zhang            @Override
379a6bf581f7a7a6326505569f0d1215d0ba84779d7Chong Zhang            public void onResult(Bundle data, String sessionId, MediaSessionStatus sessionStatus,
380a6bf581f7a7a6326505569f0d1215d0ba84779d7Chong Zhang                    String itemId, MediaItemStatus itemStatus) {
381a6bf581f7a7a6326505569f0d1215d0ba84779d7Chong Zhang                logStatus("enqueue: succeeded", sessionId, sessionStatus, itemId, itemStatus);
382a6bf581f7a7a6326505569f0d1215d0ba84779d7Chong Zhang                item.setRemoteItemId(itemId);
383a6bf581f7a7a6326505569f0d1215d0ba84779d7Chong Zhang                if (item.getPosition() > 0) {
384a6bf581f7a7a6326505569f0d1215d0ba84779d7Chong Zhang                    seekInternal(item);
385a6bf581f7a7a6326505569f0d1215d0ba84779d7Chong Zhang                }
386a6bf581f7a7a6326505569f0d1215d0ba84779d7Chong Zhang                if (item.getState() == MediaItemStatus.PLAYBACK_STATE_PAUSED) {
387a6bf581f7a7a6326505569f0d1215d0ba84779d7Chong Zhang                    pause();
388a6bf581f7a7a6326505569f0d1215d0ba84779d7Chong Zhang                }
389a6bf581f7a7a6326505569f0d1215d0ba84779d7Chong Zhang                if (mEnqueuePending) {
390a6bf581f7a7a6326505569f0d1215d0ba84779d7Chong Zhang                    mEnqueuePending = false;
391a6bf581f7a7a6326505569f0d1215d0ba84779d7Chong Zhang                    for (PlaylistItem item : mTempQueue) {
392a6bf581f7a7a6326505569f0d1215d0ba84779d7Chong Zhang                        enqueueInternal(item);
393a6bf581f7a7a6326505569f0d1215d0ba84779d7Chong Zhang                    }
394a6bf581f7a7a6326505569f0d1215d0ba84779d7Chong Zhang                    mTempQueue.clear();
395a6bf581f7a7a6326505569f0d1215d0ba84779d7Chong Zhang                }
396a6bf581f7a7a6326505569f0d1215d0ba84779d7Chong Zhang                if (mCallback != null) {
397a6bf581f7a7a6326505569f0d1215d0ba84779d7Chong Zhang                    mCallback.onPlaylistChanged();
398a6bf581f7a7a6326505569f0d1215d0ba84779d7Chong Zhang                }
399a6bf581f7a7a6326505569f0d1215d0ba84779d7Chong Zhang            }
400a6bf581f7a7a6326505569f0d1215d0ba84779d7Chong Zhang
401a6bf581f7a7a6326505569f0d1215d0ba84779d7Chong Zhang            @Override
402a6bf581f7a7a6326505569f0d1215d0ba84779d7Chong Zhang            public void onError(String error, int code, Bundle data) {
403a6bf581f7a7a6326505569f0d1215d0ba84779d7Chong Zhang                logError("enqueue: failed", error, code);
404a6bf581f7a7a6326505569f0d1215d0ba84779d7Chong Zhang                if (mCallback != null) {
405a6bf581f7a7a6326505569f0d1215d0ba84779d7Chong Zhang                    mCallback.onPlaylistChanged();
406a6bf581f7a7a6326505569f0d1215d0ba84779d7Chong Zhang                }
407a6bf581f7a7a6326505569f0d1215d0ba84779d7Chong Zhang            }
408a6bf581f7a7a6326505569f0d1215d0ba84779d7Chong Zhang        });
409a6bf581f7a7a6326505569f0d1215d0ba84779d7Chong Zhang    }
410a6bf581f7a7a6326505569f0d1215d0ba84779d7Chong Zhang
411a6bf581f7a7a6326505569f0d1215d0ba84779d7Chong Zhang    private void seekInternal(final PlaylistItem item) {
412a6bf581f7a7a6326505569f0d1215d0ba84779d7Chong Zhang        throwIfNoSession();
413a6bf581f7a7a6326505569f0d1215d0ba84779d7Chong Zhang
414a6bf581f7a7a6326505569f0d1215d0ba84779d7Chong Zhang        if (DEBUG) {
415a6bf581f7a7a6326505569f0d1215d0ba84779d7Chong Zhang            Log.d(TAG, "seek: item=" + item);
416a6bf581f7a7a6326505569f0d1215d0ba84779d7Chong Zhang        }
417a6bf581f7a7a6326505569f0d1215d0ba84779d7Chong Zhang        mClient.seek(item.getRemoteItemId(), item.getPosition(), null, new ItemActionCallback() {
418a6bf581f7a7a6326505569f0d1215d0ba84779d7Chong Zhang           @Override
419a6bf581f7a7a6326505569f0d1215d0ba84779d7Chong Zhang           public void onResult(Bundle data, String sessionId, MediaSessionStatus sessionStatus,
420a6bf581f7a7a6326505569f0d1215d0ba84779d7Chong Zhang                   String itemId, MediaItemStatus itemStatus) {
421a6bf581f7a7a6326505569f0d1215d0ba84779d7Chong Zhang               logStatus("seek: succeeded", sessionId, sessionStatus, itemId, itemStatus);
422a6bf581f7a7a6326505569f0d1215d0ba84779d7Chong Zhang               if (mCallback != null) {
423a6bf581f7a7a6326505569f0d1215d0ba84779d7Chong Zhang                   mCallback.onPlaylistChanged();
424a6bf581f7a7a6326505569f0d1215d0ba84779d7Chong Zhang               }
425a6bf581f7a7a6326505569f0d1215d0ba84779d7Chong Zhang           }
426a6bf581f7a7a6326505569f0d1215d0ba84779d7Chong Zhang
427a6bf581f7a7a6326505569f0d1215d0ba84779d7Chong Zhang           @Override
428a6bf581f7a7a6326505569f0d1215d0ba84779d7Chong Zhang           public void onError(String error, int code, Bundle data) {
429a6bf581f7a7a6326505569f0d1215d0ba84779d7Chong Zhang               logError("seek: failed", error, code);
430a6bf581f7a7a6326505569f0d1215d0ba84779d7Chong Zhang           }
431a6bf581f7a7a6326505569f0d1215d0ba84779d7Chong Zhang        });
432a6bf581f7a7a6326505569f0d1215d0ba84779d7Chong Zhang    }
433a6bf581f7a7a6326505569f0d1215d0ba84779d7Chong Zhang
434a6bf581f7a7a6326505569f0d1215d0ba84779d7Chong Zhang    private void startSession(final PlaylistItem item) {
435a6bf581f7a7a6326505569f0d1215d0ba84779d7Chong Zhang        mClient.startSession(null, new SessionActionCallback() {
436a6bf581f7a7a6326505569f0d1215d0ba84779d7Chong Zhang            @Override
437a6bf581f7a7a6326505569f0d1215d0ba84779d7Chong Zhang            public void onResult(Bundle data, String sessionId, MediaSessionStatus sessionStatus) {
438a6bf581f7a7a6326505569f0d1215d0ba84779d7Chong Zhang                logStatus("startSession: succeeded", sessionId, sessionStatus, null, null);
439a6bf581f7a7a6326505569f0d1215d0ba84779d7Chong Zhang                enqueueInternal(item);
440a6bf581f7a7a6326505569f0d1215d0ba84779d7Chong Zhang            }
441a6bf581f7a7a6326505569f0d1215d0ba84779d7Chong Zhang
442a6bf581f7a7a6326505569f0d1215d0ba84779d7Chong Zhang            @Override
443a6bf581f7a7a6326505569f0d1215d0ba84779d7Chong Zhang            public void onError(String error, int code, Bundle data) {
444a6bf581f7a7a6326505569f0d1215d0ba84779d7Chong Zhang                logError("startSession: failed", error, code);
445a6bf581f7a7a6326505569f0d1215d0ba84779d7Chong Zhang            }
446a6bf581f7a7a6326505569f0d1215d0ba84779d7Chong Zhang        });
447a6bf581f7a7a6326505569f0d1215d0ba84779d7Chong Zhang    }
448a6bf581f7a7a6326505569f0d1215d0ba84779d7Chong Zhang
449a6bf581f7a7a6326505569f0d1215d0ba84779d7Chong Zhang    private void endSession() {
450a6bf581f7a7a6326505569f0d1215d0ba84779d7Chong Zhang        mClient.endSession(null, new SessionActionCallback() {
451a6bf581f7a7a6326505569f0d1215d0ba84779d7Chong Zhang            @Override
452a6bf581f7a7a6326505569f0d1215d0ba84779d7Chong Zhang            public void onResult(Bundle data, String sessionId, MediaSessionStatus sessionStatus) {
453a6bf581f7a7a6326505569f0d1215d0ba84779d7Chong Zhang                logStatus("endSession: succeeded", sessionId, sessionStatus, null, null);
454a6bf581f7a7a6326505569f0d1215d0ba84779d7Chong Zhang            }
455a6bf581f7a7a6326505569f0d1215d0ba84779d7Chong Zhang
456a6bf581f7a7a6326505569f0d1215d0ba84779d7Chong Zhang            @Override
457a6bf581f7a7a6326505569f0d1215d0ba84779d7Chong Zhang            public void onError(String error, int code, Bundle data) {
458a6bf581f7a7a6326505569f0d1215d0ba84779d7Chong Zhang                logError("endSession: failed", error, code);
459a6bf581f7a7a6326505569f0d1215d0ba84779d7Chong Zhang            }
460a6bf581f7a7a6326505569f0d1215d0ba84779d7Chong Zhang        });
461a6bf581f7a7a6326505569f0d1215d0ba84779d7Chong Zhang    }
462a6bf581f7a7a6326505569f0d1215d0ba84779d7Chong Zhang
463a6bf581f7a7a6326505569f0d1215d0ba84779d7Chong Zhang    private void logStatus(String message,
464a6bf581f7a7a6326505569f0d1215d0ba84779d7Chong Zhang            String sessionId, MediaSessionStatus sessionStatus,
465a6bf581f7a7a6326505569f0d1215d0ba84779d7Chong Zhang            String itemId, MediaItemStatus itemStatus) {
466a6bf581f7a7a6326505569f0d1215d0ba84779d7Chong Zhang        if (DEBUG) {
467a6bf581f7a7a6326505569f0d1215d0ba84779d7Chong Zhang            String result = "";
468a6bf581f7a7a6326505569f0d1215d0ba84779d7Chong Zhang            if (sessionId != null && sessionStatus != null) {
469a6bf581f7a7a6326505569f0d1215d0ba84779d7Chong Zhang                result += "sessionId=" + sessionId + ", sessionStatus=" + sessionStatus;
470a6bf581f7a7a6326505569f0d1215d0ba84779d7Chong Zhang            }
471a6bf581f7a7a6326505569f0d1215d0ba84779d7Chong Zhang            if (itemId != null & itemStatus != null) {
472a6bf581f7a7a6326505569f0d1215d0ba84779d7Chong Zhang                result += (result.isEmpty() ? "" : ", ")
473a6bf581f7a7a6326505569f0d1215d0ba84779d7Chong Zhang                        + "itemId=" + itemId + ", itemStatus=" + itemStatus;
474a6bf581f7a7a6326505569f0d1215d0ba84779d7Chong Zhang            }
475a6bf581f7a7a6326505569f0d1215d0ba84779d7Chong Zhang            Log.d(TAG, message + ": " + result);
476a6bf581f7a7a6326505569f0d1215d0ba84779d7Chong Zhang        }
477a6bf581f7a7a6326505569f0d1215d0ba84779d7Chong Zhang    }
478a6bf581f7a7a6326505569f0d1215d0ba84779d7Chong Zhang
479a6bf581f7a7a6326505569f0d1215d0ba84779d7Chong Zhang    private void logError(String message, String error, int code) {
480a6bf581f7a7a6326505569f0d1215d0ba84779d7Chong Zhang        Log.d(TAG, message + ": error=" + error + ", code=" + code);
481a6bf581f7a7a6326505569f0d1215d0ba84779d7Chong Zhang    }
482a6bf581f7a7a6326505569f0d1215d0ba84779d7Chong Zhang
483a6bf581f7a7a6326505569f0d1215d0ba84779d7Chong Zhang    private void throwIfNoSession() {
484a6bf581f7a7a6326505569f0d1215d0ba84779d7Chong Zhang        if (!mClient.hasSession()) {
485a6bf581f7a7a6326505569f0d1215d0ba84779d7Chong Zhang            throw new IllegalStateException("Session is invalid");
486a6bf581f7a7a6326505569f0d1215d0ba84779d7Chong Zhang        }
487a6bf581f7a7a6326505569f0d1215d0ba84779d7Chong Zhang    }
488a6bf581f7a7a6326505569f0d1215d0ba84779d7Chong Zhang
489a6bf581f7a7a6326505569f0d1215d0ba84779d7Chong Zhang    private void throwIfQueuingUnsupported() {
490a6bf581f7a7a6326505569f0d1215d0ba84779d7Chong Zhang        if (!isQueuingSupported()) {
491a6bf581f7a7a6326505569f0d1215d0ba84779d7Chong Zhang            throw new UnsupportedOperationException("Queuing is unsupported");
492a6bf581f7a7a6326505569f0d1215d0ba84779d7Chong Zhang        }
493a6bf581f7a7a6326505569f0d1215d0ba84779d7Chong Zhang    }
494a6bf581f7a7a6326505569f0d1215d0ba84779d7Chong Zhang}
495