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
8270f3627685b55b2bc1b3f0c17c9c30aa3a7fd062Donghyun Cho        mPlayer.takeSnapshot();
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
10270f3627685b55b2bc1b3f0c17c9c30aa3a7fd062Donghyun Cho    public PlaylistItem add(String title, Uri uri, String mime) {
10370f3627685b55b2bc1b3f0c17c9c30aa3a7fd062Donghyun Cho        return add(title, uri, mime, null);
104a6bf581f7a7a6326505569f0d1215d0ba84779d7Chong Zhang    }
105a6bf581f7a7a6326505569f0d1215d0ba84779d7Chong Zhang
10670f3627685b55b2bc1b3f0c17c9c30aa3a7fd062Donghyun Cho    public PlaylistItem add(String title, Uri uri, String mime, PendingIntent receiver) {
107a6bf581f7a7a6326505569f0d1215d0ba84779d7Chong Zhang        if (DEBUG) {
10870f3627685b55b2bc1b3f0c17c9c30aa3a7fd062Donghyun Cho            log("add: title=" + title + ", 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
11570f3627685b55b2bc1b3f0c17c9c30aa3a7fd062Donghyun Cho        PlaylistItem item = new PlaylistItem(Integer.toString(mSessionId),
11670f3627685b55b2bc1b3f0c17c9c30aa3a7fd062Donghyun Cho                Integer.toString(mItemId), title, 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            }
35276d965dc41863b33f887db33d283cb7f1523f60dJae Seo        } else {
35376d965dc41863b33f887db33d283cb7f1523f60dJae Seo            mPlayer.initMediaSession();
354a6bf581f7a7a6326505569f0d1215d0ba84779d7Chong Zhang        }
355a6bf581f7a7a6326505569f0d1215d0ba84779d7Chong Zhang        updateStatus();
356a6bf581f7a7a6326505569f0d1215d0ba84779d7Chong Zhang    }
357a6bf581f7a7a6326505569f0d1215d0ba84779d7Chong Zhang
358a6bf581f7a7a6326505569f0d1215d0ba84779d7Chong Zhang    private PlaylistItem removeItem(String iid, int state) {
359a6bf581f7a7a6326505569f0d1215d0ba84779d7Chong Zhang        checkPlayerAndSession();
360a6bf581f7a7a6326505569f0d1215d0ba84779d7Chong Zhang        List<PlaylistItem> queue =
361a6bf581f7a7a6326505569f0d1215d0ba84779d7Chong Zhang                new ArrayList<PlaylistItem>(mPlaylist.size());
362a6bf581f7a7a6326505569f0d1215d0ba84779d7Chong Zhang        PlaylistItem found = null;
363a6bf581f7a7a6326505569f0d1215d0ba84779d7Chong Zhang        for (PlaylistItem item : mPlaylist) {
364a6bf581f7a7a6326505569f0d1215d0ba84779d7Chong Zhang            if (iid.equals(item.getItemId())) {
365a6bf581f7a7a6326505569f0d1215d0ba84779d7Chong Zhang                if (mPlayer.isQueuingSupported()) {
366a6bf581f7a7a6326505569f0d1215d0ba84779d7Chong Zhang                    mPlayer.remove(item.getRemoteItemId());
367a6bf581f7a7a6326505569f0d1215d0ba84779d7Chong Zhang                } else if (item.getState() == MediaItemStatus.PLAYBACK_STATE_PLAYING
368a6bf581f7a7a6326505569f0d1215d0ba84779d7Chong Zhang                        || item.getState() == MediaItemStatus.PLAYBACK_STATE_PAUSED){
369a6bf581f7a7a6326505569f0d1215d0ba84779d7Chong Zhang                    mPlayer.stop();
370a6bf581f7a7a6326505569f0d1215d0ba84779d7Chong Zhang                }
371a6bf581f7a7a6326505569f0d1215d0ba84779d7Chong Zhang                item.setState(state);
372a6bf581f7a7a6326505569f0d1215d0ba84779d7Chong Zhang                found = item;
373a6bf581f7a7a6326505569f0d1215d0ba84779d7Chong Zhang                // notify client that item is now removed
374a6bf581f7a7a6326505569f0d1215d0ba84779d7Chong Zhang                if (mCallback != null) {
375a6bf581f7a7a6326505569f0d1215d0ba84779d7Chong Zhang                    mCallback.onItemChanged(found);
376a6bf581f7a7a6326505569f0d1215d0ba84779d7Chong Zhang                }
377a6bf581f7a7a6326505569f0d1215d0ba84779d7Chong Zhang            } else {
378a6bf581f7a7a6326505569f0d1215d0ba84779d7Chong Zhang                queue.add(item);
379a6bf581f7a7a6326505569f0d1215d0ba84779d7Chong Zhang            }
380a6bf581f7a7a6326505569f0d1215d0ba84779d7Chong Zhang        }
381a6bf581f7a7a6326505569f0d1215d0ba84779d7Chong Zhang        if (found != null) {
382a6bf581f7a7a6326505569f0d1215d0ba84779d7Chong Zhang            mPlaylist = queue;
383a6bf581f7a7a6326505569f0d1215d0ba84779d7Chong Zhang            updatePlaybackState();
384a6bf581f7a7a6326505569f0d1215d0ba84779d7Chong Zhang        } else {
385a6bf581f7a7a6326505569f0d1215d0ba84779d7Chong Zhang            log("item not found");
386a6bf581f7a7a6326505569f0d1215d0ba84779d7Chong Zhang        }
387a6bf581f7a7a6326505569f0d1215d0ba84779d7Chong Zhang        return found;
388a6bf581f7a7a6326505569f0d1215d0ba84779d7Chong Zhang    }
389a6bf581f7a7a6326505569f0d1215d0ba84779d7Chong Zhang
390a6bf581f7a7a6326505569f0d1215d0ba84779d7Chong Zhang    private void finishItem(boolean error) {
391a6bf581f7a7a6326505569f0d1215d0ba84779d7Chong Zhang        PlaylistItem item = getCurrentItem();
392a6bf581f7a7a6326505569f0d1215d0ba84779d7Chong Zhang        if (item != null) {
393a6bf581f7a7a6326505569f0d1215d0ba84779d7Chong Zhang            removeItem(item.getItemId(), error ?
394a6bf581f7a7a6326505569f0d1215d0ba84779d7Chong Zhang                    MediaItemStatus.PLAYBACK_STATE_ERROR :
395a6bf581f7a7a6326505569f0d1215d0ba84779d7Chong Zhang                        MediaItemStatus.PLAYBACK_STATE_FINISHED);
396a6bf581f7a7a6326505569f0d1215d0ba84779d7Chong Zhang            updateStatus();
397a6bf581f7a7a6326505569f0d1215d0ba84779d7Chong Zhang        }
398a6bf581f7a7a6326505569f0d1215d0ba84779d7Chong Zhang    }
399a6bf581f7a7a6326505569f0d1215d0ba84779d7Chong Zhang
400a6bf581f7a7a6326505569f0d1215d0ba84779d7Chong Zhang    // set the Player that this playback manager will interact with
401a6bf581f7a7a6326505569f0d1215d0ba84779d7Chong Zhang    public void setPlayer(Player player) {
402a6bf581f7a7a6326505569f0d1215d0ba84779d7Chong Zhang        mPlayer = player;
403a6bf581f7a7a6326505569f0d1215d0ba84779d7Chong Zhang        checkPlayer();
404a6bf581f7a7a6326505569f0d1215d0ba84779d7Chong Zhang        mPlayer.setCallback(this);
405a6bf581f7a7a6326505569f0d1215d0ba84779d7Chong Zhang    }
406a6bf581f7a7a6326505569f0d1215d0ba84779d7Chong Zhang
407a6bf581f7a7a6326505569f0d1215d0ba84779d7Chong Zhang    // provide a callback interface to tell the UI when significant state changes occur
408a6bf581f7a7a6326505569f0d1215d0ba84779d7Chong Zhang    public void setCallback(Callback callback) {
409a6bf581f7a7a6326505569f0d1215d0ba84779d7Chong Zhang        mCallback = callback;
410a6bf581f7a7a6326505569f0d1215d0ba84779d7Chong Zhang    }
411a6bf581f7a7a6326505569f0d1215d0ba84779d7Chong Zhang
412a6bf581f7a7a6326505569f0d1215d0ba84779d7Chong Zhang    @Override
413a6bf581f7a7a6326505569f0d1215d0ba84779d7Chong Zhang    public String toString() {
414a6bf581f7a7a6326505569f0d1215d0ba84779d7Chong Zhang        String result = "Media Queue: ";
415a6bf581f7a7a6326505569f0d1215d0ba84779d7Chong Zhang        if (!mPlaylist.isEmpty()) {
416a6bf581f7a7a6326505569f0d1215d0ba84779d7Chong Zhang            for (PlaylistItem item : mPlaylist) {
417a6bf581f7a7a6326505569f0d1215d0ba84779d7Chong Zhang                result += "\n" + item.toString();
418a6bf581f7a7a6326505569f0d1215d0ba84779d7Chong Zhang            }
419a6bf581f7a7a6326505569f0d1215d0ba84779d7Chong Zhang        } else {
420a6bf581f7a7a6326505569f0d1215d0ba84779d7Chong Zhang            result += "<empty>";
421a6bf581f7a7a6326505569f0d1215d0ba84779d7Chong Zhang        }
422a6bf581f7a7a6326505569f0d1215d0ba84779d7Chong Zhang        return result;
423a6bf581f7a7a6326505569f0d1215d0ba84779d7Chong Zhang    }
424a6bf581f7a7a6326505569f0d1215d0ba84779d7Chong Zhang
425a6bf581f7a7a6326505569f0d1215d0ba84779d7Chong Zhang    public interface Callback {
426a6bf581f7a7a6326505569f0d1215d0ba84779d7Chong Zhang        void onStatusChanged();
427a6bf581f7a7a6326505569f0d1215d0ba84779d7Chong Zhang        void onItemChanged(PlaylistItem item);
428a6bf581f7a7a6326505569f0d1215d0ba84779d7Chong Zhang    }
429a6bf581f7a7a6326505569f0d1215d0ba84779d7Chong Zhang}
430