1 /* 2 * Copyright (C) 2013 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 package com.example.android.supportv7.media; 18 19 import android.annotation.TargetApi; 20 import android.content.Context; 21 import android.graphics.Bitmap; 22 import android.os.Build; 23 import android.support.v4.media.MediaMetadataCompat; 24 import android.support.v4.media.session.MediaSessionCompat; 25 import android.support.v4.media.session.PlaybackStateCompat; 26 import android.support.v7.media.MediaControlIntent; 27 import android.support.v7.media.MediaRouter.RouteInfo; 28 import android.util.Log; 29 30 /** 31 * Abstraction of common playback operations of media items, such as play, 32 * seek, etc. Used by PlaybackManager as a backend to handle actual playback 33 * of media items. 34 * 35 * TODO: Introduce prepare() method and refactor subclasses accordingly. 36 */ 37 public abstract class Player { 38 private static final String TAG = "SampleMediaRoutePlayer"; 39 private static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG); 40 protected static final int STATE_IDLE = 0; 41 protected static final int STATE_PREPARING_FOR_PLAY = 1; 42 protected static final int STATE_PREPARING_FOR_PAUSE = 2; 43 protected static final int STATE_READY = 3; 44 protected static final int STATE_PLAYING = 4; 45 protected static final int STATE_PAUSED = 5; 46 47 private static final long PLAYBACK_ACTIONS = PlaybackStateCompat.ACTION_PAUSE 48 | PlaybackStateCompat.ACTION_PLAY; 49 private static final PlaybackStateCompat INIT_PLAYBACK_STATE = new PlaybackStateCompat.Builder() 50 .setState(PlaybackStateCompat.STATE_NONE, 0, .0f).build(); 51 52 protected Callback mCallback; 53 protected MediaSessionCompat mMediaSession; 54 isRemotePlayback()55 public abstract boolean isRemotePlayback(); isQueuingSupported()56 public abstract boolean isQueuingSupported(); 57 connect(RouteInfo route)58 public abstract void connect(RouteInfo route); release()59 public abstract void release(); 60 61 // basic operations that are always supported play(final PlaylistItem item)62 public abstract void play(final PlaylistItem item); seek(final PlaylistItem item)63 public abstract void seek(final PlaylistItem item); getStatus(final PlaylistItem item, final boolean update)64 public abstract void getStatus(final PlaylistItem item, final boolean update); pause()65 public abstract void pause(); resume()66 public abstract void resume(); stop()67 public abstract void stop(); 68 69 // advanced queuing (enqueue & remove) are only supported 70 // if isQueuingSupported() returns true enqueue(final PlaylistItem item)71 public abstract void enqueue(final PlaylistItem item); remove(String iid)72 public abstract PlaylistItem remove(String iid); 73 takeSnapshot()74 public void takeSnapshot() {} getSnapshot()75 public Bitmap getSnapshot() { return null; } 76 77 /** 78 * presentation display 79 */ 80 @TargetApi(Build.VERSION_CODES.JELLY_BEAN_MR1) updatePresentation()81 public void updatePresentation() {} 82 setCallback(Callback callback)83 public void setCallback(Callback callback) { 84 mCallback = callback; 85 } 86 create(Context context, RouteInfo route, MediaSessionCompat session)87 public static Player create(Context context, RouteInfo route, MediaSessionCompat session) { 88 Player player; 89 if (route != null && route.supportsControlCategory( 90 MediaControlIntent.CATEGORY_REMOTE_PLAYBACK)) { 91 player = new RemotePlayer(context); 92 } else if (route != null) { 93 player = new LocalPlayer.SurfaceViewPlayer(context); 94 } else { 95 player = new LocalPlayer.OverlayPlayer(context); 96 } 97 player.setMediaSession(session); 98 player.initMediaSession(); 99 player.connect(route); 100 return player; 101 } 102 initMediaSession()103 protected void initMediaSession() { 104 if (mMediaSession == null) { 105 return; 106 } 107 mMediaSession.setMetadata(null); 108 mMediaSession.setPlaybackState(INIT_PLAYBACK_STATE); 109 } 110 updateMetadata(PlaylistItem currentItem)111 protected void updateMetadata(PlaylistItem currentItem) { 112 if (mMediaSession == null) { 113 return; 114 } 115 if (DEBUG) { 116 Log.d(TAG, "Update metadata: currentItem=" + currentItem); 117 } 118 if (currentItem == null) { 119 mMediaSession.setMetadata(null); 120 return; 121 } 122 MediaMetadataCompat.Builder bob = new MediaMetadataCompat.Builder(); 123 bob.putString(MediaMetadataCompat.METADATA_KEY_DISPLAY_TITLE, currentItem.getTitle()); 124 bob.putString(MediaMetadataCompat.METADATA_KEY_DISPLAY_SUBTITLE, "Subtitle of the thing"); 125 bob.putString(MediaMetadataCompat.METADATA_KEY_DISPLAY_DESCRIPTION, 126 "Description of the thing"); 127 bob.putBitmap(MediaMetadataCompat.METADATA_KEY_DISPLAY_ICON, getSnapshot()); 128 mMediaSession.setMetadata(bob.build()); 129 } 130 publishState(int state)131 protected void publishState(int state) { 132 if (mMediaSession == null) { 133 return; 134 } 135 PlaybackStateCompat.Builder bob = new PlaybackStateCompat.Builder(); 136 bob.setActions(PLAYBACK_ACTIONS); 137 switch (state) { 138 case STATE_PLAYING: 139 bob.setState(PlaybackStateCompat.STATE_PLAYING, -1, 1); 140 break; 141 case STATE_READY: 142 case STATE_PAUSED: 143 bob.setState(PlaybackStateCompat.STATE_PAUSED, -1, 0); 144 break; 145 case STATE_PREPARING_FOR_PLAY: 146 case STATE_PREPARING_FOR_PAUSE: 147 bob.setState(PlaybackStateCompat.STATE_BUFFERING, -1, 0); 148 break; 149 case STATE_IDLE: 150 bob.setState(PlaybackStateCompat.STATE_STOPPED, -1, 0); 151 break; 152 } 153 PlaybackStateCompat pbState = bob.build(); 154 Log.d(TAG, "Setting state to " + pbState); 155 mMediaSession.setPlaybackState(pbState); 156 if (state != STATE_IDLE) { 157 mMediaSession.setActive(true); 158 } else { 159 mMediaSession.setActive(false); 160 } 161 } 162 setMediaSession(MediaSessionCompat session)163 private void setMediaSession(MediaSessionCompat session) { 164 mMediaSession = session; 165 } 166 167 public interface Callback { onError()168 void onError(); onCompletion()169 void onCompletion(); onPlaylistChanged()170 void onPlaylistChanged(); onPlaylistReady()171 void onPlaylistReady(); 172 } 173 } 174