SessionManager.java revision cf61a6ed2bfa6141b832fdc40a9fbfb70af91416
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.app.PendingIntent; 20a6bf581f7a7a6326505569f0d1215d0ba84779d7Chong Zhangimport android.net.Uri; 21a6bf581f7a7a6326505569f0d1215d0ba84779d7Chong Zhangimport android.support.v7.media.MediaItemStatus; 22a6bf581f7a7a6326505569f0d1215d0ba84779d7Chong Zhangimport android.support.v7.media.MediaSessionStatus; 23a6bf581f7a7a6326505569f0d1215d0ba84779d7Chong Zhangimport android.util.Log; 24a6bf581f7a7a6326505569f0d1215d0ba84779d7Chong Zhang 25a6bf581f7a7a6326505569f0d1215d0ba84779d7Chong Zhangimport java.util.List; 26a6bf581f7a7a6326505569f0d1215d0ba84779d7Chong Zhangimport java.util.ArrayList; 27a6bf581f7a7a6326505569f0d1215d0ba84779d7Chong Zhang 28a6bf581f7a7a6326505569f0d1215d0ba84779d7Chong Zhang/** 29a6bf581f7a7a6326505569f0d1215d0ba84779d7Chong Zhang * SessionManager manages a media session as a queue. It supports common 30a6bf581f7a7a6326505569f0d1215d0ba84779d7Chong Zhang * queuing behaviors such as enqueue/remove of media items, pause/resume/stop, 31a6bf581f7a7a6326505569f0d1215d0ba84779d7Chong Zhang * etc. 32a6bf581f7a7a6326505569f0d1215d0ba84779d7Chong Zhang * 33a6bf581f7a7a6326505569f0d1215d0ba84779d7Chong Zhang * Actual playback of a single media item is abstracted into a Player interface, 34a6bf581f7a7a6326505569f0d1215d0ba84779d7Chong Zhang * and is handled outside this class. 35a6bf581f7a7a6326505569f0d1215d0ba84779d7Chong Zhang */ 36a6bf581f7a7a6326505569f0d1215d0ba84779d7Chong Zhangpublic class SessionManager implements Player.Callback { 37a6bf581f7a7a6326505569f0d1215d0ba84779d7Chong Zhang private static final String TAG = "SessionManager"; 38a6bf581f7a7a6326505569f0d1215d0ba84779d7Chong Zhang private static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG); 39a6bf581f7a7a6326505569f0d1215d0ba84779d7Chong Zhang 40a6bf581f7a7a6326505569f0d1215d0ba84779d7Chong Zhang private String mName; 41a6bf581f7a7a6326505569f0d1215d0ba84779d7Chong Zhang private int mSessionId; 42a6bf581f7a7a6326505569f0d1215d0ba84779d7Chong Zhang private int mItemId; 43a6bf581f7a7a6326505569f0d1215d0ba84779d7Chong Zhang private boolean mPaused; 44a6bf581f7a7a6326505569f0d1215d0ba84779d7Chong Zhang private boolean mSessionValid; 45a6bf581f7a7a6326505569f0d1215d0ba84779d7Chong Zhang private Player mPlayer; 46a6bf581f7a7a6326505569f0d1215d0ba84779d7Chong Zhang private Callback mCallback; 47a6bf581f7a7a6326505569f0d1215d0ba84779d7Chong Zhang private List<PlaylistItem> mPlaylist = new ArrayList<PlaylistItem>(); 48a6bf581f7a7a6326505569f0d1215d0ba84779d7Chong Zhang 49a6bf581f7a7a6326505569f0d1215d0ba84779d7Chong Zhang public SessionManager(String name) { 50a6bf581f7a7a6326505569f0d1215d0ba84779d7Chong Zhang mName = name; 51a6bf581f7a7a6326505569f0d1215d0ba84779d7Chong Zhang } 52a6bf581f7a7a6326505569f0d1215d0ba84779d7Chong Zhang 53cf61a6ed2bfa6141b832fdc40a9fbfb70af91416Chong Zhang public boolean isPaused() { 54cf61a6ed2bfa6141b832fdc40a9fbfb70af91416Chong Zhang return hasSession() && mPaused; 55cf61a6ed2bfa6141b832fdc40a9fbfb70af91416Chong Zhang } 56cf61a6ed2bfa6141b832fdc40a9fbfb70af91416Chong Zhang 57a6bf581f7a7a6326505569f0d1215d0ba84779d7Chong Zhang public boolean hasSession() { 58a6bf581f7a7a6326505569f0d1215d0ba84779d7Chong Zhang return mSessionValid; 59a6bf581f7a7a6326505569f0d1215d0ba84779d7Chong Zhang } 60a6bf581f7a7a6326505569f0d1215d0ba84779d7Chong Zhang 61a6bf581f7a7a6326505569f0d1215d0ba84779d7Chong Zhang public String getSessionId() { 62a6bf581f7a7a6326505569f0d1215d0ba84779d7Chong Zhang return mSessionValid ? Integer.toString(mSessionId) : null; 63a6bf581f7a7a6326505569f0d1215d0ba84779d7Chong Zhang } 64a6bf581f7a7a6326505569f0d1215d0ba84779d7Chong Zhang 65a6bf581f7a7a6326505569f0d1215d0ba84779d7Chong Zhang public PlaylistItem getCurrentItem() { 66a6bf581f7a7a6326505569f0d1215d0ba84779d7Chong Zhang return mPlaylist.isEmpty() ? null : mPlaylist.get(0); 67a6bf581f7a7a6326505569f0d1215d0ba84779d7Chong Zhang } 68a6bf581f7a7a6326505569f0d1215d0ba84779d7Chong Zhang 69a6bf581f7a7a6326505569f0d1215d0ba84779d7Chong Zhang // Returns the cached playlist (note this is not responsible for updating it) 70a6bf581f7a7a6326505569f0d1215d0ba84779d7Chong Zhang public List<PlaylistItem> getPlaylist() { 71a6bf581f7a7a6326505569f0d1215d0ba84779d7Chong Zhang return mPlaylist; 72a6bf581f7a7a6326505569f0d1215d0ba84779d7Chong Zhang } 73a6bf581f7a7a6326505569f0d1215d0ba84779d7Chong Zhang 74a6bf581f7a7a6326505569f0d1215d0ba84779d7Chong Zhang // Updates the playlist asynchronously, calls onPlaylistReady() when finished. 75a6bf581f7a7a6326505569f0d1215d0ba84779d7Chong Zhang public void updateStatus() { 76a6bf581f7a7a6326505569f0d1215d0ba84779d7Chong Zhang if (DEBUG) { 77a6bf581f7a7a6326505569f0d1215d0ba84779d7Chong Zhang log("updateStatus"); 78a6bf581f7a7a6326505569f0d1215d0ba84779d7Chong Zhang } 79a6bf581f7a7a6326505569f0d1215d0ba84779d7Chong Zhang checkPlayer(); 80a6bf581f7a7a6326505569f0d1215d0ba84779d7Chong Zhang // update the statistics first, so that the stats string is valid when 81a6bf581f7a7a6326505569f0d1215d0ba84779d7Chong Zhang // onPlaylistReady() gets called in the end 82cf61a6ed2bfa6141b832fdc40a9fbfb70af91416Chong Zhang mPlayer.updateTrackInfo(); 83a6bf581f7a7a6326505569f0d1215d0ba84779d7Chong Zhang 84a6bf581f7a7a6326505569f0d1215d0ba84779d7Chong Zhang if (mPlaylist.isEmpty()) { 85a6bf581f7a7a6326505569f0d1215d0ba84779d7Chong Zhang // If queue is empty, don't forget to call onPlaylistReady()! 86a6bf581f7a7a6326505569f0d1215d0ba84779d7Chong Zhang onPlaylistReady(); 87a6bf581f7a7a6326505569f0d1215d0ba84779d7Chong Zhang } else if (mPlayer.isQueuingSupported()) { 88a6bf581f7a7a6326505569f0d1215d0ba84779d7Chong Zhang // If player supports queuing, get status of each item. Player is 89a6bf581f7a7a6326505569f0d1215d0ba84779d7Chong Zhang // responsible to call onPlaylistReady() after last getStatus(). 90a6bf581f7a7a6326505569f0d1215d0ba84779d7Chong Zhang // (update=1 requires player to callback onPlaylistReady()) 91a6bf581f7a7a6326505569f0d1215d0ba84779d7Chong Zhang for (int i = 0; i < mPlaylist.size(); i++) { 92a6bf581f7a7a6326505569f0d1215d0ba84779d7Chong Zhang PlaylistItem item = mPlaylist.get(i); 93a6bf581f7a7a6326505569f0d1215d0ba84779d7Chong Zhang mPlayer.getStatus(item, (i == mPlaylist.size() - 1) /* update */); 94a6bf581f7a7a6326505569f0d1215d0ba84779d7Chong Zhang } 95a6bf581f7a7a6326505569f0d1215d0ba84779d7Chong Zhang } else { 96a6bf581f7a7a6326505569f0d1215d0ba84779d7Chong Zhang // Otherwise, only need to get status for current item. Player is 97a6bf581f7a7a6326505569f0d1215d0ba84779d7Chong Zhang // responsible to call onPlaylistReady() when finished. 98a6bf581f7a7a6326505569f0d1215d0ba84779d7Chong Zhang mPlayer.getStatus(getCurrentItem(), true /* update */); 99a6bf581f7a7a6326505569f0d1215d0ba84779d7Chong Zhang } 100a6bf581f7a7a6326505569f0d1215d0ba84779d7Chong Zhang } 101a6bf581f7a7a6326505569f0d1215d0ba84779d7Chong Zhang 102a6bf581f7a7a6326505569f0d1215d0ba84779d7Chong Zhang public PlaylistItem add(Uri uri, String mime) { 103a6bf581f7a7a6326505569f0d1215d0ba84779d7Chong Zhang return add(uri, mime, null); 104a6bf581f7a7a6326505569f0d1215d0ba84779d7Chong Zhang } 105a6bf581f7a7a6326505569f0d1215d0ba84779d7Chong Zhang 106a6bf581f7a7a6326505569f0d1215d0ba84779d7Chong Zhang public PlaylistItem add(Uri uri, String mime, PendingIntent receiver) { 107a6bf581f7a7a6326505569f0d1215d0ba84779d7Chong Zhang if (DEBUG) { 108a6bf581f7a7a6326505569f0d1215d0ba84779d7Chong Zhang log("add: uri=" + uri + ", receiver=" + receiver); 109a6bf581f7a7a6326505569f0d1215d0ba84779d7Chong Zhang } 110a6bf581f7a7a6326505569f0d1215d0ba84779d7Chong Zhang // create new session if needed 111a6bf581f7a7a6326505569f0d1215d0ba84779d7Chong Zhang startSession(); 112a6bf581f7a7a6326505569f0d1215d0ba84779d7Chong Zhang checkPlayerAndSession(); 113a6bf581f7a7a6326505569f0d1215d0ba84779d7Chong Zhang 114a6bf581f7a7a6326505569f0d1215d0ba84779d7Chong Zhang // append new item with initial status PLAYBACK_STATE_PENDING 115a6bf581f7a7a6326505569f0d1215d0ba84779d7Chong Zhang PlaylistItem item = new PlaylistItem( 116a6bf581f7a7a6326505569f0d1215d0ba84779d7Chong Zhang Integer.toString(mSessionId), Integer.toString(mItemId), uri, mime, receiver); 117a6bf581f7a7a6326505569f0d1215d0ba84779d7Chong Zhang mPlaylist.add(item); 118a6bf581f7a7a6326505569f0d1215d0ba84779d7Chong Zhang mItemId++; 119a6bf581f7a7a6326505569f0d1215d0ba84779d7Chong Zhang 120a6bf581f7a7a6326505569f0d1215d0ba84779d7Chong Zhang // if player supports queuing, enqueue the item now 121a6bf581f7a7a6326505569f0d1215d0ba84779d7Chong Zhang if (mPlayer.isQueuingSupported()) { 122a6bf581f7a7a6326505569f0d1215d0ba84779d7Chong Zhang mPlayer.enqueue(item); 123a6bf581f7a7a6326505569f0d1215d0ba84779d7Chong Zhang } 124a6bf581f7a7a6326505569f0d1215d0ba84779d7Chong Zhang updatePlaybackState(); 125a6bf581f7a7a6326505569f0d1215d0ba84779d7Chong Zhang return item; 126a6bf581f7a7a6326505569f0d1215d0ba84779d7Chong Zhang } 127a6bf581f7a7a6326505569f0d1215d0ba84779d7Chong Zhang 128a6bf581f7a7a6326505569f0d1215d0ba84779d7Chong Zhang public PlaylistItem remove(String iid) { 129a6bf581f7a7a6326505569f0d1215d0ba84779d7Chong Zhang if (DEBUG) { 130a6bf581f7a7a6326505569f0d1215d0ba84779d7Chong Zhang log("remove: iid=" + iid); 131a6bf581f7a7a6326505569f0d1215d0ba84779d7Chong Zhang } 132a6bf581f7a7a6326505569f0d1215d0ba84779d7Chong Zhang checkPlayerAndSession(); 133a6bf581f7a7a6326505569f0d1215d0ba84779d7Chong Zhang return removeItem(iid, MediaItemStatus.PLAYBACK_STATE_CANCELED); 134a6bf581f7a7a6326505569f0d1215d0ba84779d7Chong Zhang } 135a6bf581f7a7a6326505569f0d1215d0ba84779d7Chong Zhang 136a6bf581f7a7a6326505569f0d1215d0ba84779d7Chong Zhang public PlaylistItem seek(String iid, long pos) { 137a6bf581f7a7a6326505569f0d1215d0ba84779d7Chong Zhang if (DEBUG) { 138a6bf581f7a7a6326505569f0d1215d0ba84779d7Chong Zhang log("seek: iid=" + iid +", pos=" + pos); 139a6bf581f7a7a6326505569f0d1215d0ba84779d7Chong Zhang } 140a6bf581f7a7a6326505569f0d1215d0ba84779d7Chong Zhang checkPlayerAndSession(); 141a6bf581f7a7a6326505569f0d1215d0ba84779d7Chong Zhang // seeking on pending items are not yet supported 142a6bf581f7a7a6326505569f0d1215d0ba84779d7Chong Zhang checkItemCurrent(iid); 143a6bf581f7a7a6326505569f0d1215d0ba84779d7Chong Zhang 144a6bf581f7a7a6326505569f0d1215d0ba84779d7Chong Zhang PlaylistItem item = getCurrentItem(); 145a6bf581f7a7a6326505569f0d1215d0ba84779d7Chong Zhang if (pos != item.getPosition()) { 146a6bf581f7a7a6326505569f0d1215d0ba84779d7Chong Zhang item.setPosition(pos); 147a6bf581f7a7a6326505569f0d1215d0ba84779d7Chong Zhang if (item.getState() == MediaItemStatus.PLAYBACK_STATE_PLAYING 148a6bf581f7a7a6326505569f0d1215d0ba84779d7Chong Zhang || item.getState() == MediaItemStatus.PLAYBACK_STATE_PAUSED) { 149a6bf581f7a7a6326505569f0d1215d0ba84779d7Chong Zhang mPlayer.seek(item); 150a6bf581f7a7a6326505569f0d1215d0ba84779d7Chong Zhang } 151a6bf581f7a7a6326505569f0d1215d0ba84779d7Chong Zhang } 152a6bf581f7a7a6326505569f0d1215d0ba84779d7Chong Zhang return item; 153a6bf581f7a7a6326505569f0d1215d0ba84779d7Chong Zhang } 154a6bf581f7a7a6326505569f0d1215d0ba84779d7Chong Zhang 155a6bf581f7a7a6326505569f0d1215d0ba84779d7Chong Zhang public PlaylistItem getStatus(String iid) { 156a6bf581f7a7a6326505569f0d1215d0ba84779d7Chong Zhang checkPlayerAndSession(); 157a6bf581f7a7a6326505569f0d1215d0ba84779d7Chong Zhang 158a6bf581f7a7a6326505569f0d1215d0ba84779d7Chong Zhang // This should only be called for local player. Remote player is 159a6bf581f7a7a6326505569f0d1215d0ba84779d7Chong Zhang // asynchronous, need to use updateStatus() instead. 160a6bf581f7a7a6326505569f0d1215d0ba84779d7Chong Zhang if (mPlayer.isRemotePlayback()) { 161a6bf581f7a7a6326505569f0d1215d0ba84779d7Chong Zhang throw new IllegalStateException( 162a6bf581f7a7a6326505569f0d1215d0ba84779d7Chong Zhang "getStatus should not be called on remote player!"); 163a6bf581f7a7a6326505569f0d1215d0ba84779d7Chong Zhang } 164a6bf581f7a7a6326505569f0d1215d0ba84779d7Chong Zhang 165a6bf581f7a7a6326505569f0d1215d0ba84779d7Chong Zhang for (PlaylistItem item : mPlaylist) { 166a6bf581f7a7a6326505569f0d1215d0ba84779d7Chong Zhang if (item.getItemId().equals(iid)) { 167a6bf581f7a7a6326505569f0d1215d0ba84779d7Chong Zhang if (item == getCurrentItem()) { 168a6bf581f7a7a6326505569f0d1215d0ba84779d7Chong Zhang mPlayer.getStatus(item, false); 169a6bf581f7a7a6326505569f0d1215d0ba84779d7Chong Zhang } 170a6bf581f7a7a6326505569f0d1215d0ba84779d7Chong Zhang return item; 171a6bf581f7a7a6326505569f0d1215d0ba84779d7Chong Zhang } 172a6bf581f7a7a6326505569f0d1215d0ba84779d7Chong Zhang } 173a6bf581f7a7a6326505569f0d1215d0ba84779d7Chong Zhang return null; 174a6bf581f7a7a6326505569f0d1215d0ba84779d7Chong Zhang } 175a6bf581f7a7a6326505569f0d1215d0ba84779d7Chong Zhang 176a6bf581f7a7a6326505569f0d1215d0ba84779d7Chong Zhang public void pause() { 177a6bf581f7a7a6326505569f0d1215d0ba84779d7Chong Zhang if (DEBUG) { 178a6bf581f7a7a6326505569f0d1215d0ba84779d7Chong Zhang log("pause"); 179a6bf581f7a7a6326505569f0d1215d0ba84779d7Chong Zhang } 180cf61a6ed2bfa6141b832fdc40a9fbfb70af91416Chong Zhang if (!mSessionValid) { 181cf61a6ed2bfa6141b832fdc40a9fbfb70af91416Chong Zhang return; 182cf61a6ed2bfa6141b832fdc40a9fbfb70af91416Chong Zhang } 183cf61a6ed2bfa6141b832fdc40a9fbfb70af91416Chong Zhang checkPlayer(); 184a6bf581f7a7a6326505569f0d1215d0ba84779d7Chong Zhang mPaused = true; 185a6bf581f7a7a6326505569f0d1215d0ba84779d7Chong Zhang updatePlaybackState(); 186a6bf581f7a7a6326505569f0d1215d0ba84779d7Chong Zhang } 187a6bf581f7a7a6326505569f0d1215d0ba84779d7Chong Zhang 188a6bf581f7a7a6326505569f0d1215d0ba84779d7Chong Zhang public void resume() { 189a6bf581f7a7a6326505569f0d1215d0ba84779d7Chong Zhang if (DEBUG) { 190a6bf581f7a7a6326505569f0d1215d0ba84779d7Chong Zhang log("resume"); 191a6bf581f7a7a6326505569f0d1215d0ba84779d7Chong Zhang } 192cf61a6ed2bfa6141b832fdc40a9fbfb70af91416Chong Zhang if (!mSessionValid) { 193cf61a6ed2bfa6141b832fdc40a9fbfb70af91416Chong Zhang return; 194cf61a6ed2bfa6141b832fdc40a9fbfb70af91416Chong Zhang } 195cf61a6ed2bfa6141b832fdc40a9fbfb70af91416Chong Zhang checkPlayer(); 196a6bf581f7a7a6326505569f0d1215d0ba84779d7Chong Zhang mPaused = false; 197a6bf581f7a7a6326505569f0d1215d0ba84779d7Chong Zhang updatePlaybackState(); 198a6bf581f7a7a6326505569f0d1215d0ba84779d7Chong Zhang } 199a6bf581f7a7a6326505569f0d1215d0ba84779d7Chong Zhang 200a6bf581f7a7a6326505569f0d1215d0ba84779d7Chong Zhang public void stop() { 201a6bf581f7a7a6326505569f0d1215d0ba84779d7Chong Zhang if (DEBUG) { 202a6bf581f7a7a6326505569f0d1215d0ba84779d7Chong Zhang log("stop"); 203a6bf581f7a7a6326505569f0d1215d0ba84779d7Chong Zhang } 204cf61a6ed2bfa6141b832fdc40a9fbfb70af91416Chong Zhang if (!mSessionValid) { 205cf61a6ed2bfa6141b832fdc40a9fbfb70af91416Chong Zhang return; 206cf61a6ed2bfa6141b832fdc40a9fbfb70af91416Chong Zhang } 207cf61a6ed2bfa6141b832fdc40a9fbfb70af91416Chong Zhang checkPlayer(); 208a6bf581f7a7a6326505569f0d1215d0ba84779d7Chong Zhang mPlayer.stop(); 209a6bf581f7a7a6326505569f0d1215d0ba84779d7Chong Zhang mPlaylist.clear(); 210a6bf581f7a7a6326505569f0d1215d0ba84779d7Chong Zhang mPaused = false; 211a6bf581f7a7a6326505569f0d1215d0ba84779d7Chong Zhang updateStatus(); 212a6bf581f7a7a6326505569f0d1215d0ba84779d7Chong Zhang } 213a6bf581f7a7a6326505569f0d1215d0ba84779d7Chong Zhang 214a6bf581f7a7a6326505569f0d1215d0ba84779d7Chong Zhang public String startSession() { 215a6bf581f7a7a6326505569f0d1215d0ba84779d7Chong Zhang if (!mSessionValid) { 216a6bf581f7a7a6326505569f0d1215d0ba84779d7Chong Zhang mSessionId++; 217a6bf581f7a7a6326505569f0d1215d0ba84779d7Chong Zhang mItemId = 0; 218a6bf581f7a7a6326505569f0d1215d0ba84779d7Chong Zhang mPaused = false; 219a6bf581f7a7a6326505569f0d1215d0ba84779d7Chong Zhang mSessionValid = true; 220a6bf581f7a7a6326505569f0d1215d0ba84779d7Chong Zhang return Integer.toString(mSessionId); 221a6bf581f7a7a6326505569f0d1215d0ba84779d7Chong Zhang } 222a6bf581f7a7a6326505569f0d1215d0ba84779d7Chong Zhang return null; 223a6bf581f7a7a6326505569f0d1215d0ba84779d7Chong Zhang } 224a6bf581f7a7a6326505569f0d1215d0ba84779d7Chong Zhang 225a6bf581f7a7a6326505569f0d1215d0ba84779d7Chong Zhang public boolean endSession() { 226a6bf581f7a7a6326505569f0d1215d0ba84779d7Chong Zhang if (mSessionValid) { 227a6bf581f7a7a6326505569f0d1215d0ba84779d7Chong Zhang mSessionValid = false; 228a6bf581f7a7a6326505569f0d1215d0ba84779d7Chong Zhang return true; 229a6bf581f7a7a6326505569f0d1215d0ba84779d7Chong Zhang } 230a6bf581f7a7a6326505569f0d1215d0ba84779d7Chong Zhang return false; 231a6bf581f7a7a6326505569f0d1215d0ba84779d7Chong Zhang } 232a6bf581f7a7a6326505569f0d1215d0ba84779d7Chong Zhang 233a6bf581f7a7a6326505569f0d1215d0ba84779d7Chong Zhang MediaSessionStatus getSessionStatus(String sid) { 234a6bf581f7a7a6326505569f0d1215d0ba84779d7Chong Zhang int sessionState = (sid != null && sid.equals(mSessionId)) ? 235a6bf581f7a7a6326505569f0d1215d0ba84779d7Chong Zhang MediaSessionStatus.SESSION_STATE_ACTIVE : 236a6bf581f7a7a6326505569f0d1215d0ba84779d7Chong Zhang MediaSessionStatus.SESSION_STATE_INVALIDATED; 237a6bf581f7a7a6326505569f0d1215d0ba84779d7Chong Zhang 238a6bf581f7a7a6326505569f0d1215d0ba84779d7Chong Zhang return new MediaSessionStatus.Builder(sessionState) 239a6bf581f7a7a6326505569f0d1215d0ba84779d7Chong Zhang .setQueuePaused(mPaused) 240a6bf581f7a7a6326505569f0d1215d0ba84779d7Chong Zhang .build(); 241a6bf581f7a7a6326505569f0d1215d0ba84779d7Chong Zhang } 242a6bf581f7a7a6326505569f0d1215d0ba84779d7Chong Zhang 243a6bf581f7a7a6326505569f0d1215d0ba84779d7Chong Zhang // Suspend the playback manager. Put the current item back into PENDING 244a6bf581f7a7a6326505569f0d1215d0ba84779d7Chong Zhang // state, and remember the current playback position. Called when switching 245a6bf581f7a7a6326505569f0d1215d0ba84779d7Chong Zhang // to a different player (route). 246a6bf581f7a7a6326505569f0d1215d0ba84779d7Chong Zhang public void suspend(long pos) { 247a6bf581f7a7a6326505569f0d1215d0ba84779d7Chong Zhang for (PlaylistItem item : mPlaylist) { 248a6bf581f7a7a6326505569f0d1215d0ba84779d7Chong Zhang item.setRemoteItemId(null); 249a6bf581f7a7a6326505569f0d1215d0ba84779d7Chong Zhang item.setDuration(0); 250a6bf581f7a7a6326505569f0d1215d0ba84779d7Chong Zhang } 251a6bf581f7a7a6326505569f0d1215d0ba84779d7Chong Zhang PlaylistItem item = getCurrentItem(); 252a6bf581f7a7a6326505569f0d1215d0ba84779d7Chong Zhang if (DEBUG) { 253a6bf581f7a7a6326505569f0d1215d0ba84779d7Chong Zhang log("suspend: item=" + item + ", pos=" + pos); 254a6bf581f7a7a6326505569f0d1215d0ba84779d7Chong Zhang } 255a6bf581f7a7a6326505569f0d1215d0ba84779d7Chong Zhang if (item != null) { 256a6bf581f7a7a6326505569f0d1215d0ba84779d7Chong Zhang if (item.getState() == MediaItemStatus.PLAYBACK_STATE_PLAYING 257a6bf581f7a7a6326505569f0d1215d0ba84779d7Chong Zhang || item.getState() == MediaItemStatus.PLAYBACK_STATE_PAUSED) { 258a6bf581f7a7a6326505569f0d1215d0ba84779d7Chong Zhang item.setState(MediaItemStatus.PLAYBACK_STATE_PENDING); 259a6bf581f7a7a6326505569f0d1215d0ba84779d7Chong Zhang item.setPosition(pos); 260a6bf581f7a7a6326505569f0d1215d0ba84779d7Chong Zhang } 261a6bf581f7a7a6326505569f0d1215d0ba84779d7Chong Zhang } 262a6bf581f7a7a6326505569f0d1215d0ba84779d7Chong Zhang } 263a6bf581f7a7a6326505569f0d1215d0ba84779d7Chong Zhang 264a6bf581f7a7a6326505569f0d1215d0ba84779d7Chong Zhang // Unsuspend the playback manager. Restart playback on new player (route). 265a6bf581f7a7a6326505569f0d1215d0ba84779d7Chong Zhang // This will resume playback of current item. Furthermore, if the new player 266a6bf581f7a7a6326505569f0d1215d0ba84779d7Chong Zhang // supports queuing, playlist will be re-established on the remote player. 267a6bf581f7a7a6326505569f0d1215d0ba84779d7Chong Zhang public void unsuspend() { 268a6bf581f7a7a6326505569f0d1215d0ba84779d7Chong Zhang if (DEBUG) { 269a6bf581f7a7a6326505569f0d1215d0ba84779d7Chong Zhang log("unsuspend"); 270a6bf581f7a7a6326505569f0d1215d0ba84779d7Chong Zhang } 271a6bf581f7a7a6326505569f0d1215d0ba84779d7Chong Zhang if (mPlayer.isQueuingSupported()) { 272a6bf581f7a7a6326505569f0d1215d0ba84779d7Chong Zhang for (PlaylistItem item : mPlaylist) { 273a6bf581f7a7a6326505569f0d1215d0ba84779d7Chong Zhang mPlayer.enqueue(item); 274a6bf581f7a7a6326505569f0d1215d0ba84779d7Chong Zhang } 275a6bf581f7a7a6326505569f0d1215d0ba84779d7Chong Zhang } 276a6bf581f7a7a6326505569f0d1215d0ba84779d7Chong Zhang updatePlaybackState(); 277a6bf581f7a7a6326505569f0d1215d0ba84779d7Chong Zhang } 278a6bf581f7a7a6326505569f0d1215d0ba84779d7Chong Zhang 279a6bf581f7a7a6326505569f0d1215d0ba84779d7Chong Zhang // Player.Callback 280a6bf581f7a7a6326505569f0d1215d0ba84779d7Chong Zhang @Override 281a6bf581f7a7a6326505569f0d1215d0ba84779d7Chong Zhang public void onError() { 282a6bf581f7a7a6326505569f0d1215d0ba84779d7Chong Zhang finishItem(true); 283a6bf581f7a7a6326505569f0d1215d0ba84779d7Chong Zhang } 284a6bf581f7a7a6326505569f0d1215d0ba84779d7Chong Zhang 285a6bf581f7a7a6326505569f0d1215d0ba84779d7Chong Zhang @Override 286a6bf581f7a7a6326505569f0d1215d0ba84779d7Chong Zhang public void onCompletion() { 287a6bf581f7a7a6326505569f0d1215d0ba84779d7Chong Zhang finishItem(false); 288a6bf581f7a7a6326505569f0d1215d0ba84779d7Chong Zhang } 289a6bf581f7a7a6326505569f0d1215d0ba84779d7Chong Zhang 290a6bf581f7a7a6326505569f0d1215d0ba84779d7Chong Zhang @Override 291a6bf581f7a7a6326505569f0d1215d0ba84779d7Chong Zhang public void onPlaylistChanged() { 292a6bf581f7a7a6326505569f0d1215d0ba84779d7Chong Zhang // Playlist has changed, update the cached playlist 293a6bf581f7a7a6326505569f0d1215d0ba84779d7Chong Zhang updateStatus(); 294a6bf581f7a7a6326505569f0d1215d0ba84779d7Chong Zhang } 295a6bf581f7a7a6326505569f0d1215d0ba84779d7Chong Zhang 296a6bf581f7a7a6326505569f0d1215d0ba84779d7Chong Zhang @Override 297a6bf581f7a7a6326505569f0d1215d0ba84779d7Chong Zhang public void onPlaylistReady() { 298a6bf581f7a7a6326505569f0d1215d0ba84779d7Chong Zhang // Notify activity to update Ui 299a6bf581f7a7a6326505569f0d1215d0ba84779d7Chong Zhang if (mCallback != null) { 300a6bf581f7a7a6326505569f0d1215d0ba84779d7Chong Zhang mCallback.onStatusChanged(); 301a6bf581f7a7a6326505569f0d1215d0ba84779d7Chong Zhang } 302a6bf581f7a7a6326505569f0d1215d0ba84779d7Chong Zhang } 303a6bf581f7a7a6326505569f0d1215d0ba84779d7Chong Zhang 304a6bf581f7a7a6326505569f0d1215d0ba84779d7Chong Zhang private void log(String message) { 305a6bf581f7a7a6326505569f0d1215d0ba84779d7Chong Zhang Log.d(TAG, mName + ": " + message); 306a6bf581f7a7a6326505569f0d1215d0ba84779d7Chong Zhang } 307a6bf581f7a7a6326505569f0d1215d0ba84779d7Chong Zhang 308a6bf581f7a7a6326505569f0d1215d0ba84779d7Chong Zhang private void checkPlayer() { 309a6bf581f7a7a6326505569f0d1215d0ba84779d7Chong Zhang if (mPlayer == null) { 310a6bf581f7a7a6326505569f0d1215d0ba84779d7Chong Zhang throw new IllegalStateException("Player not set!"); 311a6bf581f7a7a6326505569f0d1215d0ba84779d7Chong Zhang } 312a6bf581f7a7a6326505569f0d1215d0ba84779d7Chong Zhang } 313a6bf581f7a7a6326505569f0d1215d0ba84779d7Chong Zhang 314a6bf581f7a7a6326505569f0d1215d0ba84779d7Chong Zhang private void checkSession() { 315a6bf581f7a7a6326505569f0d1215d0ba84779d7Chong Zhang if (!mSessionValid) { 316a6bf581f7a7a6326505569f0d1215d0ba84779d7Chong Zhang throw new IllegalStateException("Session not set!"); 317a6bf581f7a7a6326505569f0d1215d0ba84779d7Chong Zhang } 318a6bf581f7a7a6326505569f0d1215d0ba84779d7Chong Zhang } 319a6bf581f7a7a6326505569f0d1215d0ba84779d7Chong Zhang 320a6bf581f7a7a6326505569f0d1215d0ba84779d7Chong Zhang private void checkPlayerAndSession() { 321a6bf581f7a7a6326505569f0d1215d0ba84779d7Chong Zhang checkPlayer(); 322a6bf581f7a7a6326505569f0d1215d0ba84779d7Chong Zhang checkSession(); 323a6bf581f7a7a6326505569f0d1215d0ba84779d7Chong Zhang } 324a6bf581f7a7a6326505569f0d1215d0ba84779d7Chong Zhang 325a6bf581f7a7a6326505569f0d1215d0ba84779d7Chong Zhang private void checkItemCurrent(String iid) { 326a6bf581f7a7a6326505569f0d1215d0ba84779d7Chong Zhang PlaylistItem item = getCurrentItem(); 327a6bf581f7a7a6326505569f0d1215d0ba84779d7Chong Zhang if (item == null || !item.getItemId().equals(iid)) { 328a6bf581f7a7a6326505569f0d1215d0ba84779d7Chong Zhang throw new IllegalArgumentException("Item is not current!"); 329a6bf581f7a7a6326505569f0d1215d0ba84779d7Chong Zhang } 330a6bf581f7a7a6326505569f0d1215d0ba84779d7Chong Zhang } 331a6bf581f7a7a6326505569f0d1215d0ba84779d7Chong Zhang 332a6bf581f7a7a6326505569f0d1215d0ba84779d7Chong Zhang private void updatePlaybackState() { 333a6bf581f7a7a6326505569f0d1215d0ba84779d7Chong Zhang PlaylistItem item = getCurrentItem(); 334a6bf581f7a7a6326505569f0d1215d0ba84779d7Chong Zhang if (item != null) { 335a6bf581f7a7a6326505569f0d1215d0ba84779d7Chong Zhang if (item.getState() == MediaItemStatus.PLAYBACK_STATE_PENDING) { 336a6bf581f7a7a6326505569f0d1215d0ba84779d7Chong Zhang item.setState(mPaused ? MediaItemStatus.PLAYBACK_STATE_PAUSED 337a6bf581f7a7a6326505569f0d1215d0ba84779d7Chong Zhang : MediaItemStatus.PLAYBACK_STATE_PLAYING); 338a6bf581f7a7a6326505569f0d1215d0ba84779d7Chong Zhang if (!mPlayer.isQueuingSupported()) { 339a6bf581f7a7a6326505569f0d1215d0ba84779d7Chong Zhang mPlayer.play(item); 340a6bf581f7a7a6326505569f0d1215d0ba84779d7Chong Zhang } 341a6bf581f7a7a6326505569f0d1215d0ba84779d7Chong Zhang } else if (mPaused && item.getState() == MediaItemStatus.PLAYBACK_STATE_PLAYING) { 342a6bf581f7a7a6326505569f0d1215d0ba84779d7Chong Zhang mPlayer.pause(); 343a6bf581f7a7a6326505569f0d1215d0ba84779d7Chong Zhang item.setState(MediaItemStatus.PLAYBACK_STATE_PAUSED); 344a6bf581f7a7a6326505569f0d1215d0ba84779d7Chong Zhang } else if (!mPaused && item.getState() == MediaItemStatus.PLAYBACK_STATE_PAUSED) { 345a6bf581f7a7a6326505569f0d1215d0ba84779d7Chong Zhang mPlayer.resume(); 346a6bf581f7a7a6326505569f0d1215d0ba84779d7Chong Zhang item.setState(MediaItemStatus.PLAYBACK_STATE_PLAYING); 347a6bf581f7a7a6326505569f0d1215d0ba84779d7Chong Zhang } 348a6bf581f7a7a6326505569f0d1215d0ba84779d7Chong Zhang // notify client that item playback status has changed 349a6bf581f7a7a6326505569f0d1215d0ba84779d7Chong Zhang if (mCallback != null) { 350a6bf581f7a7a6326505569f0d1215d0ba84779d7Chong Zhang mCallback.onItemChanged(item); 351a6bf581f7a7a6326505569f0d1215d0ba84779d7Chong Zhang } 352a6bf581f7a7a6326505569f0d1215d0ba84779d7Chong Zhang } 353a6bf581f7a7a6326505569f0d1215d0ba84779d7Chong Zhang updateStatus(); 354a6bf581f7a7a6326505569f0d1215d0ba84779d7Chong Zhang } 355a6bf581f7a7a6326505569f0d1215d0ba84779d7Chong Zhang 356a6bf581f7a7a6326505569f0d1215d0ba84779d7Chong Zhang private PlaylistItem removeItem(String iid, int state) { 357a6bf581f7a7a6326505569f0d1215d0ba84779d7Chong Zhang checkPlayerAndSession(); 358a6bf581f7a7a6326505569f0d1215d0ba84779d7Chong Zhang List<PlaylistItem> queue = 359a6bf581f7a7a6326505569f0d1215d0ba84779d7Chong Zhang new ArrayList<PlaylistItem>(mPlaylist.size()); 360a6bf581f7a7a6326505569f0d1215d0ba84779d7Chong Zhang PlaylistItem found = null; 361a6bf581f7a7a6326505569f0d1215d0ba84779d7Chong Zhang for (PlaylistItem item : mPlaylist) { 362a6bf581f7a7a6326505569f0d1215d0ba84779d7Chong Zhang if (iid.equals(item.getItemId())) { 363a6bf581f7a7a6326505569f0d1215d0ba84779d7Chong Zhang if (mPlayer.isQueuingSupported()) { 364a6bf581f7a7a6326505569f0d1215d0ba84779d7Chong Zhang mPlayer.remove(item.getRemoteItemId()); 365a6bf581f7a7a6326505569f0d1215d0ba84779d7Chong Zhang } else if (item.getState() == MediaItemStatus.PLAYBACK_STATE_PLAYING 366a6bf581f7a7a6326505569f0d1215d0ba84779d7Chong Zhang || item.getState() == MediaItemStatus.PLAYBACK_STATE_PAUSED){ 367a6bf581f7a7a6326505569f0d1215d0ba84779d7Chong Zhang mPlayer.stop(); 368a6bf581f7a7a6326505569f0d1215d0ba84779d7Chong Zhang } 369a6bf581f7a7a6326505569f0d1215d0ba84779d7Chong Zhang item.setState(state); 370a6bf581f7a7a6326505569f0d1215d0ba84779d7Chong Zhang found = item; 371a6bf581f7a7a6326505569f0d1215d0ba84779d7Chong Zhang // notify client that item is now removed 372a6bf581f7a7a6326505569f0d1215d0ba84779d7Chong Zhang if (mCallback != null) { 373a6bf581f7a7a6326505569f0d1215d0ba84779d7Chong Zhang mCallback.onItemChanged(found); 374a6bf581f7a7a6326505569f0d1215d0ba84779d7Chong Zhang } 375a6bf581f7a7a6326505569f0d1215d0ba84779d7Chong Zhang } else { 376a6bf581f7a7a6326505569f0d1215d0ba84779d7Chong Zhang queue.add(item); 377a6bf581f7a7a6326505569f0d1215d0ba84779d7Chong Zhang } 378a6bf581f7a7a6326505569f0d1215d0ba84779d7Chong Zhang } 379a6bf581f7a7a6326505569f0d1215d0ba84779d7Chong Zhang if (found != null) { 380a6bf581f7a7a6326505569f0d1215d0ba84779d7Chong Zhang mPlaylist = queue; 381a6bf581f7a7a6326505569f0d1215d0ba84779d7Chong Zhang updatePlaybackState(); 382a6bf581f7a7a6326505569f0d1215d0ba84779d7Chong Zhang } else { 383a6bf581f7a7a6326505569f0d1215d0ba84779d7Chong Zhang log("item not found"); 384a6bf581f7a7a6326505569f0d1215d0ba84779d7Chong Zhang } 385a6bf581f7a7a6326505569f0d1215d0ba84779d7Chong Zhang return found; 386a6bf581f7a7a6326505569f0d1215d0ba84779d7Chong Zhang } 387a6bf581f7a7a6326505569f0d1215d0ba84779d7Chong Zhang 388a6bf581f7a7a6326505569f0d1215d0ba84779d7Chong Zhang private void finishItem(boolean error) { 389a6bf581f7a7a6326505569f0d1215d0ba84779d7Chong Zhang PlaylistItem item = getCurrentItem(); 390a6bf581f7a7a6326505569f0d1215d0ba84779d7Chong Zhang if (item != null) { 391a6bf581f7a7a6326505569f0d1215d0ba84779d7Chong Zhang removeItem(item.getItemId(), error ? 392a6bf581f7a7a6326505569f0d1215d0ba84779d7Chong Zhang MediaItemStatus.PLAYBACK_STATE_ERROR : 393a6bf581f7a7a6326505569f0d1215d0ba84779d7Chong Zhang MediaItemStatus.PLAYBACK_STATE_FINISHED); 394a6bf581f7a7a6326505569f0d1215d0ba84779d7Chong Zhang updateStatus(); 395a6bf581f7a7a6326505569f0d1215d0ba84779d7Chong Zhang } 396a6bf581f7a7a6326505569f0d1215d0ba84779d7Chong Zhang } 397a6bf581f7a7a6326505569f0d1215d0ba84779d7Chong Zhang 398a6bf581f7a7a6326505569f0d1215d0ba84779d7Chong Zhang // set the Player that this playback manager will interact with 399a6bf581f7a7a6326505569f0d1215d0ba84779d7Chong Zhang public void setPlayer(Player player) { 400a6bf581f7a7a6326505569f0d1215d0ba84779d7Chong Zhang mPlayer = player; 401a6bf581f7a7a6326505569f0d1215d0ba84779d7Chong Zhang checkPlayer(); 402a6bf581f7a7a6326505569f0d1215d0ba84779d7Chong Zhang mPlayer.setCallback(this); 403a6bf581f7a7a6326505569f0d1215d0ba84779d7Chong Zhang } 404a6bf581f7a7a6326505569f0d1215d0ba84779d7Chong Zhang 405a6bf581f7a7a6326505569f0d1215d0ba84779d7Chong Zhang // provide a callback interface to tell the UI when significant state changes occur 406a6bf581f7a7a6326505569f0d1215d0ba84779d7Chong Zhang public void setCallback(Callback callback) { 407a6bf581f7a7a6326505569f0d1215d0ba84779d7Chong Zhang mCallback = callback; 408a6bf581f7a7a6326505569f0d1215d0ba84779d7Chong Zhang } 409a6bf581f7a7a6326505569f0d1215d0ba84779d7Chong Zhang 410a6bf581f7a7a6326505569f0d1215d0ba84779d7Chong Zhang @Override 411a6bf581f7a7a6326505569f0d1215d0ba84779d7Chong Zhang public String toString() { 412a6bf581f7a7a6326505569f0d1215d0ba84779d7Chong Zhang String result = "Media Queue: "; 413a6bf581f7a7a6326505569f0d1215d0ba84779d7Chong Zhang if (!mPlaylist.isEmpty()) { 414a6bf581f7a7a6326505569f0d1215d0ba84779d7Chong Zhang for (PlaylistItem item : mPlaylist) { 415a6bf581f7a7a6326505569f0d1215d0ba84779d7Chong Zhang result += "\n" + item.toString(); 416a6bf581f7a7a6326505569f0d1215d0ba84779d7Chong Zhang } 417a6bf581f7a7a6326505569f0d1215d0ba84779d7Chong Zhang } else { 418a6bf581f7a7a6326505569f0d1215d0ba84779d7Chong Zhang result += "<empty>"; 419a6bf581f7a7a6326505569f0d1215d0ba84779d7Chong Zhang } 420a6bf581f7a7a6326505569f0d1215d0ba84779d7Chong Zhang return result; 421a6bf581f7a7a6326505569f0d1215d0ba84779d7Chong Zhang } 422a6bf581f7a7a6326505569f0d1215d0ba84779d7Chong Zhang 423a6bf581f7a7a6326505569f0d1215d0ba84779d7Chong Zhang public interface Callback { 424a6bf581f7a7a6326505569f0d1215d0ba84779d7Chong Zhang void onStatusChanged(); 425a6bf581f7a7a6326505569f0d1215d0ba84779d7Chong Zhang void onItemChanged(PlaylistItem item); 426a6bf581f7a7a6326505569f0d1215d0ba84779d7Chong Zhang } 427a6bf581f7a7a6326505569f0d1215d0ba84779d7Chong Zhang} 428