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
1961636870e0a12db5909955eca58e0070357dde42Donghyun Choimport android.annotation.TargetApi;
20a6bf581f7a7a6326505569f0d1215d0ba84779d7Chong Zhangimport android.content.Context;
21cf61a6ed2bfa6141b832fdc40a9fbfb70af91416Chong Zhangimport android.graphics.Bitmap;
2261636870e0a12db5909955eca58e0070357dde42Donghyun Choimport android.os.Build;
235d429bc3a8195d6f37cf2f7da0935972950539b4RoboErikimport android.support.v4.media.MediaMetadataCompat;
245d429bc3a8195d6f37cf2f7da0935972950539b4RoboErikimport android.support.v4.media.session.MediaSessionCompat;
255d429bc3a8195d6f37cf2f7da0935972950539b4RoboErikimport android.support.v4.media.session.PlaybackStateCompat;
26a6bf581f7a7a6326505569f0d1215d0ba84779d7Chong Zhangimport android.support.v7.media.MediaControlIntent;
27a6bf581f7a7a6326505569f0d1215d0ba84779d7Chong Zhangimport android.support.v7.media.MediaRouter.RouteInfo;
285d429bc3a8195d6f37cf2f7da0935972950539b4RoboErikimport android.util.Log;
29a6bf581f7a7a6326505569f0d1215d0ba84779d7Chong Zhang
30a6bf581f7a7a6326505569f0d1215d0ba84779d7Chong Zhang/**
31a6bf581f7a7a6326505569f0d1215d0ba84779d7Chong Zhang * Abstraction of common playback operations of media items, such as play,
32a6bf581f7a7a6326505569f0d1215d0ba84779d7Chong Zhang * seek, etc. Used by PlaybackManager as a backend to handle actual playback
33a6bf581f7a7a6326505569f0d1215d0ba84779d7Chong Zhang * of media items.
3476d965dc41863b33f887db33d283cb7f1523f60dJae Seo *
3576d965dc41863b33f887db33d283cb7f1523f60dJae Seo * TODO: Introduce prepare() method and refactor subclasses accordingly.
36a6bf581f7a7a6326505569f0d1215d0ba84779d7Chong Zhang */
37a6bf581f7a7a6326505569f0d1215d0ba84779d7Chong Zhangpublic abstract class Player {
385d429bc3a8195d6f37cf2f7da0935972950539b4RoboErik    private static final String TAG = "SampleMediaRoutePlayer";
3970f3627685b55b2bc1b3f0c17c9c30aa3a7fd062Donghyun Cho    private static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG);
405d429bc3a8195d6f37cf2f7da0935972950539b4RoboErik    protected static final int STATE_IDLE = 0;
4176d965dc41863b33f887db33d283cb7f1523f60dJae Seo    protected static final int STATE_PREPARING_FOR_PLAY = 1;
4276d965dc41863b33f887db33d283cb7f1523f60dJae Seo    protected static final int STATE_PREPARING_FOR_PAUSE = 2;
4376d965dc41863b33f887db33d283cb7f1523f60dJae Seo    protected static final int STATE_READY = 3;
4476d965dc41863b33f887db33d283cb7f1523f60dJae Seo    protected static final int STATE_PLAYING = 4;
4576d965dc41863b33f887db33d283cb7f1523f60dJae Seo    protected static final int STATE_PAUSED = 5;
465d429bc3a8195d6f37cf2f7da0935972950539b4RoboErik
475d429bc3a8195d6f37cf2f7da0935972950539b4RoboErik    private static final long PLAYBACK_ACTIONS = PlaybackStateCompat.ACTION_PAUSE
485d429bc3a8195d6f37cf2f7da0935972950539b4RoboErik            | PlaybackStateCompat.ACTION_PLAY;
4976d965dc41863b33f887db33d283cb7f1523f60dJae Seo    private static final PlaybackStateCompat INIT_PLAYBACK_STATE = new PlaybackStateCompat.Builder()
5076d965dc41863b33f887db33d283cb7f1523f60dJae Seo            .setState(PlaybackStateCompat.STATE_NONE, 0, .0f).build();
515d429bc3a8195d6f37cf2f7da0935972950539b4RoboErik
52a6bf581f7a7a6326505569f0d1215d0ba84779d7Chong Zhang    protected Callback mCallback;
535d429bc3a8195d6f37cf2f7da0935972950539b4RoboErik    protected MediaSessionCompat mMediaSession;
54a6bf581f7a7a6326505569f0d1215d0ba84779d7Chong Zhang
55a6bf581f7a7a6326505569f0d1215d0ba84779d7Chong Zhang    public abstract boolean isRemotePlayback();
56a6bf581f7a7a6326505569f0d1215d0ba84779d7Chong Zhang    public abstract boolean isQueuingSupported();
57a6bf581f7a7a6326505569f0d1215d0ba84779d7Chong Zhang
58a6bf581f7a7a6326505569f0d1215d0ba84779d7Chong Zhang    public abstract void connect(RouteInfo route);
59a6bf581f7a7a6326505569f0d1215d0ba84779d7Chong Zhang    public abstract void release();
60a6bf581f7a7a6326505569f0d1215d0ba84779d7Chong Zhang
61a6bf581f7a7a6326505569f0d1215d0ba84779d7Chong Zhang    // basic operations that are always supported
62a6bf581f7a7a6326505569f0d1215d0ba84779d7Chong Zhang    public abstract void play(final PlaylistItem item);
63a6bf581f7a7a6326505569f0d1215d0ba84779d7Chong Zhang    public abstract void seek(final PlaylistItem item);
64a6bf581f7a7a6326505569f0d1215d0ba84779d7Chong Zhang    public abstract void getStatus(final PlaylistItem item, final boolean update);
65a6bf581f7a7a6326505569f0d1215d0ba84779d7Chong Zhang    public abstract void pause();
66a6bf581f7a7a6326505569f0d1215d0ba84779d7Chong Zhang    public abstract void resume();
67a6bf581f7a7a6326505569f0d1215d0ba84779d7Chong Zhang    public abstract void stop();
68a6bf581f7a7a6326505569f0d1215d0ba84779d7Chong Zhang
69a6bf581f7a7a6326505569f0d1215d0ba84779d7Chong Zhang    // advanced queuing (enqueue & remove) are only supported
70a6bf581f7a7a6326505569f0d1215d0ba84779d7Chong Zhang    // if isQueuingSupported() returns true
71a6bf581f7a7a6326505569f0d1215d0ba84779d7Chong Zhang    public abstract void enqueue(final PlaylistItem item);
72a6bf581f7a7a6326505569f0d1215d0ba84779d7Chong Zhang    public abstract PlaylistItem remove(String iid);
73a6bf581f7a7a6326505569f0d1215d0ba84779d7Chong Zhang
7470f3627685b55b2bc1b3f0c17c9c30aa3a7fd062Donghyun Cho    public void takeSnapshot() {}
75cf61a6ed2bfa6141b832fdc40a9fbfb70af91416Chong Zhang    public Bitmap getSnapshot() { return null; }
76a6bf581f7a7a6326505569f0d1215d0ba84779d7Chong Zhang
7761636870e0a12db5909955eca58e0070357dde42Donghyun Cho    /**
7861636870e0a12db5909955eca58e0070357dde42Donghyun Cho     * presentation display
7961636870e0a12db5909955eca58e0070357dde42Donghyun Cho     */
8061636870e0a12db5909955eca58e0070357dde42Donghyun Cho    @TargetApi(Build.VERSION_CODES.JELLY_BEAN_MR1)
81a6bf581f7a7a6326505569f0d1215d0ba84779d7Chong Zhang    public void updatePresentation() {}
82a6bf581f7a7a6326505569f0d1215d0ba84779d7Chong Zhang
83a6bf581f7a7a6326505569f0d1215d0ba84779d7Chong Zhang    public void setCallback(Callback callback) {
84a6bf581f7a7a6326505569f0d1215d0ba84779d7Chong Zhang        mCallback = callback;
85a6bf581f7a7a6326505569f0d1215d0ba84779d7Chong Zhang    }
86a6bf581f7a7a6326505569f0d1215d0ba84779d7Chong Zhang
879a2de97bec8126f8c8c05e9c995f378c5a73319aRoboErik    public static Player create(Context context, RouteInfo route, MediaSessionCompat session) {
88a6bf581f7a7a6326505569f0d1215d0ba84779d7Chong Zhang        Player player;
89a6bf581f7a7a6326505569f0d1215d0ba84779d7Chong Zhang        if (route != null && route.supportsControlCategory(
90a6bf581f7a7a6326505569f0d1215d0ba84779d7Chong Zhang                MediaControlIntent.CATEGORY_REMOTE_PLAYBACK)) {
91a6bf581f7a7a6326505569f0d1215d0ba84779d7Chong Zhang            player = new RemotePlayer(context);
92a6bf581f7a7a6326505569f0d1215d0ba84779d7Chong Zhang        } else if (route != null) {
93a6bf581f7a7a6326505569f0d1215d0ba84779d7Chong Zhang            player = new LocalPlayer.SurfaceViewPlayer(context);
94a6bf581f7a7a6326505569f0d1215d0ba84779d7Chong Zhang        } else {
95a6bf581f7a7a6326505569f0d1215d0ba84779d7Chong Zhang            player = new LocalPlayer.OverlayPlayer(context);
96a6bf581f7a7a6326505569f0d1215d0ba84779d7Chong Zhang        }
9776d965dc41863b33f887db33d283cb7f1523f60dJae Seo        player.setMediaSession(session);
9876d965dc41863b33f887db33d283cb7f1523f60dJae Seo        player.initMediaSession();
99a6bf581f7a7a6326505569f0d1215d0ba84779d7Chong Zhang        player.connect(route);
100a6bf581f7a7a6326505569f0d1215d0ba84779d7Chong Zhang        return player;
101a6bf581f7a7a6326505569f0d1215d0ba84779d7Chong Zhang    }
102a6bf581f7a7a6326505569f0d1215d0ba84779d7Chong Zhang
10376d965dc41863b33f887db33d283cb7f1523f60dJae Seo    protected void initMediaSession() {
10476d965dc41863b33f887db33d283cb7f1523f60dJae Seo        if (mMediaSession == null) {
10576d965dc41863b33f887db33d283cb7f1523f60dJae Seo            return;
10676d965dc41863b33f887db33d283cb7f1523f60dJae Seo        }
10776d965dc41863b33f887db33d283cb7f1523f60dJae Seo        mMediaSession.setMetadata(null);
10876d965dc41863b33f887db33d283cb7f1523f60dJae Seo        mMediaSession.setPlaybackState(INIT_PLAYBACK_STATE);
1095d429bc3a8195d6f37cf2f7da0935972950539b4RoboErik    }
1105d429bc3a8195d6f37cf2f7da0935972950539b4RoboErik
11170f3627685b55b2bc1b3f0c17c9c30aa3a7fd062Donghyun Cho    protected void updateMetadata(PlaylistItem currentItem) {
1129a2de97bec8126f8c8c05e9c995f378c5a73319aRoboErik        if (mMediaSession == null) {
1139a2de97bec8126f8c8c05e9c995f378c5a73319aRoboErik            return;
1149a2de97bec8126f8c8c05e9c995f378c5a73319aRoboErik        }
11570f3627685b55b2bc1b3f0c17c9c30aa3a7fd062Donghyun Cho        if (DEBUG) {
11670f3627685b55b2bc1b3f0c17c9c30aa3a7fd062Donghyun Cho            Log.d(TAG, "Update metadata: currentItem=" + currentItem);
11770f3627685b55b2bc1b3f0c17c9c30aa3a7fd062Donghyun Cho        }
11870f3627685b55b2bc1b3f0c17c9c30aa3a7fd062Donghyun Cho        if (currentItem == null) {
11970f3627685b55b2bc1b3f0c17c9c30aa3a7fd062Donghyun Cho            mMediaSession.setMetadata(null);
12070f3627685b55b2bc1b3f0c17c9c30aa3a7fd062Donghyun Cho            return;
12170f3627685b55b2bc1b3f0c17c9c30aa3a7fd062Donghyun Cho        }
1225d429bc3a8195d6f37cf2f7da0935972950539b4RoboErik        MediaMetadataCompat.Builder bob = new MediaMetadataCompat.Builder();
12370f3627685b55b2bc1b3f0c17c9c30aa3a7fd062Donghyun Cho        bob.putString(MediaMetadataCompat.METADATA_KEY_DISPLAY_TITLE, currentItem.getTitle());
1245d429bc3a8195d6f37cf2f7da0935972950539b4RoboErik        bob.putString(MediaMetadataCompat.METADATA_KEY_DISPLAY_SUBTITLE, "Subtitle of the thing");
1255d429bc3a8195d6f37cf2f7da0935972950539b4RoboErik        bob.putString(MediaMetadataCompat.METADATA_KEY_DISPLAY_DESCRIPTION,
1265d429bc3a8195d6f37cf2f7da0935972950539b4RoboErik                "Description of the thing");
1275d429bc3a8195d6f37cf2f7da0935972950539b4RoboErik        bob.putBitmap(MediaMetadataCompat.METADATA_KEY_DISPLAY_ICON, getSnapshot());
1285d429bc3a8195d6f37cf2f7da0935972950539b4RoboErik        mMediaSession.setMetadata(bob.build());
1295d429bc3a8195d6f37cf2f7da0935972950539b4RoboErik    }
1305d429bc3a8195d6f37cf2f7da0935972950539b4RoboErik
1315d429bc3a8195d6f37cf2f7da0935972950539b4RoboErik    protected void publishState(int state) {
1329a2de97bec8126f8c8c05e9c995f378c5a73319aRoboErik        if (mMediaSession == null) {
1339a2de97bec8126f8c8c05e9c995f378c5a73319aRoboErik            return;
1349a2de97bec8126f8c8c05e9c995f378c5a73319aRoboErik        }
1355d429bc3a8195d6f37cf2f7da0935972950539b4RoboErik        PlaybackStateCompat.Builder bob = new PlaybackStateCompat.Builder();
1365d429bc3a8195d6f37cf2f7da0935972950539b4RoboErik        bob.setActions(PLAYBACK_ACTIONS);
1375d429bc3a8195d6f37cf2f7da0935972950539b4RoboErik        switch (state) {
1385d429bc3a8195d6f37cf2f7da0935972950539b4RoboErik            case STATE_PLAYING:
1395d429bc3a8195d6f37cf2f7da0935972950539b4RoboErik                bob.setState(PlaybackStateCompat.STATE_PLAYING, -1, 1);
1405d429bc3a8195d6f37cf2f7da0935972950539b4RoboErik                break;
1415d429bc3a8195d6f37cf2f7da0935972950539b4RoboErik            case STATE_READY:
1425d429bc3a8195d6f37cf2f7da0935972950539b4RoboErik            case STATE_PAUSED:
1435d429bc3a8195d6f37cf2f7da0935972950539b4RoboErik                bob.setState(PlaybackStateCompat.STATE_PAUSED, -1, 0);
1445d429bc3a8195d6f37cf2f7da0935972950539b4RoboErik                break;
14570f3627685b55b2bc1b3f0c17c9c30aa3a7fd062Donghyun Cho            case STATE_PREPARING_FOR_PLAY:
14670f3627685b55b2bc1b3f0c17c9c30aa3a7fd062Donghyun Cho            case STATE_PREPARING_FOR_PAUSE:
14770f3627685b55b2bc1b3f0c17c9c30aa3a7fd062Donghyun Cho                bob.setState(PlaybackStateCompat.STATE_BUFFERING, -1, 0);
14870f3627685b55b2bc1b3f0c17c9c30aa3a7fd062Donghyun Cho                break;
1495d429bc3a8195d6f37cf2f7da0935972950539b4RoboErik            case STATE_IDLE:
1505d429bc3a8195d6f37cf2f7da0935972950539b4RoboErik                bob.setState(PlaybackStateCompat.STATE_STOPPED, -1, 0);
1515d429bc3a8195d6f37cf2f7da0935972950539b4RoboErik                break;
1525d429bc3a8195d6f37cf2f7da0935972950539b4RoboErik        }
1535d429bc3a8195d6f37cf2f7da0935972950539b4RoboErik        PlaybackStateCompat pbState = bob.build();
1545d429bc3a8195d6f37cf2f7da0935972950539b4RoboErik        Log.d(TAG, "Setting state to " + pbState);
1555d429bc3a8195d6f37cf2f7da0935972950539b4RoboErik        mMediaSession.setPlaybackState(pbState);
1565d429bc3a8195d6f37cf2f7da0935972950539b4RoboErik        if (state != STATE_IDLE) {
1575d429bc3a8195d6f37cf2f7da0935972950539b4RoboErik            mMediaSession.setActive(true);
1585d429bc3a8195d6f37cf2f7da0935972950539b4RoboErik        } else {
1595d429bc3a8195d6f37cf2f7da0935972950539b4RoboErik            mMediaSession.setActive(false);
1605d429bc3a8195d6f37cf2f7da0935972950539b4RoboErik        }
1615d429bc3a8195d6f37cf2f7da0935972950539b4RoboErik    }
1625d429bc3a8195d6f37cf2f7da0935972950539b4RoboErik
16376d965dc41863b33f887db33d283cb7f1523f60dJae Seo    private void setMediaSession(MediaSessionCompat session) {
1649a2de97bec8126f8c8c05e9c995f378c5a73319aRoboErik        mMediaSession = session;
1655d429bc3a8195d6f37cf2f7da0935972950539b4RoboErik    }
1665d429bc3a8195d6f37cf2f7da0935972950539b4RoboErik
167a6bf581f7a7a6326505569f0d1215d0ba84779d7Chong Zhang    public interface Callback {
168a6bf581f7a7a6326505569f0d1215d0ba84779d7Chong Zhang        void onError();
169a6bf581f7a7a6326505569f0d1215d0ba84779d7Chong Zhang        void onCompletion();
170a6bf581f7a7a6326505569f0d1215d0ba84779d7Chong Zhang        void onPlaylistChanged();
171a6bf581f7a7a6326505569f0d1215d0ba84779d7Chong Zhang        void onPlaylistReady();
172a6bf581f7a7a6326505569f0d1215d0ba84779d7Chong Zhang    }
17376d965dc41863b33f887db33d283cb7f1523f60dJae Seo}
174