1 /* 2 * Copyright (C) 2014 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 package com.android.onemedia; 17 18 import android.content.Context; 19 import android.content.Intent; 20 import android.graphics.Bitmap; 21 import android.media.MediaMetadata; 22 import android.media.session.MediaSession; 23 import android.media.session.MediaSession.QueueItem; 24 import android.media.session.MediaSessionManager; 25 import android.media.session.PlaybackState; 26 import android.os.Bundle; 27 import android.os.RemoteException; 28 import android.os.SystemClock; 29 import android.util.Log; 30 import android.view.KeyEvent; 31 32 import com.android.onemedia.playback.LocalRenderer; 33 import com.android.onemedia.playback.Renderer; 34 import com.android.onemedia.playback.RequestUtils; 35 36 import java.util.ArrayList; 37 import java.util.List; 38 39 public class PlayerSession { 40 private static final String TAG = "PlayerSession"; 41 42 protected MediaSession mSession; 43 protected Context mContext; 44 protected Renderer mRenderer; 45 protected MediaSession.Callback mCallback; 46 protected Renderer.Listener mRenderListener; 47 protected MediaMetadata.Builder mMetadataBuilder; 48 protected ArrayList<MediaSession.QueueItem> mQueue; 49 protected boolean mUseQueue; 50 51 protected PlaybackState mPlaybackState; 52 protected Listener mListener; 53 54 private String mContent; 55 PlayerSession(Context context)56 public PlayerSession(Context context) { 57 mContext = context; 58 mRenderer = new LocalRenderer(context, null); 59 mCallback = new SessionCb(); 60 mRenderListener = new RenderListener(); 61 PlaybackState.Builder psBob = new PlaybackState.Builder(); 62 psBob.setActions(PlaybackState.ACTION_PAUSE | PlaybackState.ACTION_PLAY); 63 mPlaybackState = psBob.build(); 64 mQueue = new ArrayList<MediaSession.QueueItem>(); 65 66 mRenderer.registerListener(mRenderListener); 67 68 initMetadata(); 69 } 70 createSession()71 public void createSession() { 72 releaseSession(); 73 74 MediaSessionManager man = (MediaSessionManager) mContext 75 .getSystemService(Context.MEDIA_SESSION_SERVICE); 76 Log.d(TAG, "Creating session for package " + mContext.getBasePackageName()); 77 78 mSession = new MediaSession(mContext, "OneMedia"); 79 mSession.setCallback(mCallback); 80 mSession.setPlaybackState(mPlaybackState); 81 mSession.setFlags(MediaSession.FLAG_HANDLES_TRANSPORT_CONTROLS 82 | MediaSession.FLAG_HANDLES_MEDIA_BUTTONS); 83 mSession.setActive(true); 84 updateMetadata(); 85 } 86 onDestroy()87 public void onDestroy() { 88 releaseSession(); 89 if (mRenderer != null) { 90 mRenderer.unregisterListener(mRenderListener); 91 mRenderer.onDestroy(); 92 } 93 } 94 releaseSession()95 private void releaseSession() { 96 if (mSession != null) { 97 mSession.release(); 98 mSession = null; 99 } 100 } 101 setListener(Listener listener)102 public void setListener(Listener listener) { 103 mListener = listener; 104 } 105 getSessionToken()106 public MediaSession.Token getSessionToken() { 107 return mSession.getSessionToken(); 108 } 109 setContent(Bundle request)110 public void setContent(Bundle request) { 111 mRenderer.setContent(request); 112 mContent = request.getString(RequestUtils.EXTRA_KEY_SOURCE); 113 } 114 setNextContent(Bundle request)115 public void setNextContent(Bundle request) { 116 mRenderer.setNextContent(request); 117 } 118 setIcon(Bitmap icon)119 public void setIcon(Bitmap icon) { 120 mMetadataBuilder.putBitmap(MediaMetadata.METADATA_KEY_DISPLAY_ICON, icon); 121 mQueue.clear(); 122 mQueue.add(new QueueItem(mMetadataBuilder.build().getDescription(), 11)); 123 updateMetadata(); 124 } 125 updateMetadata()126 private void updateMetadata() { 127 // This is a mild abuse of metadata and shouldn't be duplicated in real 128 // code 129 if (mSession != null && mSession.isActive()) { 130 mSession.setMetadata(mMetadataBuilder.build()); 131 // Just toggle the queue every time we update for testing 132 mSession.setQueue(mUseQueue ? mQueue : null); 133 mSession.setQueueTitle(mUseQueue ? "Queue title" : null); 134 mUseQueue = !mUseQueue; 135 } 136 } 137 updateState(int newState)138 private void updateState(int newState) { 139 float rate = newState == PlaybackState.STATE_PLAYING ? 1 : 0; 140 long position = mRenderer == null ? -1 : mRenderer.getSeekPosition(); 141 PlaybackState.Builder bob = new PlaybackState.Builder(mPlaybackState); 142 bob.setState(newState, position, rate, SystemClock.elapsedRealtime()); 143 bob.setErrorMessage(null); 144 mPlaybackState = bob.build(); 145 mSession.setPlaybackState(mPlaybackState); 146 } 147 initMetadata()148 private void initMetadata() { 149 mMetadataBuilder = new MediaMetadata.Builder(); 150 mMetadataBuilder.putString(MediaMetadata.METADATA_KEY_DISPLAY_TITLE, 151 "OneMedia display title"); 152 mMetadataBuilder.putString(MediaMetadata.METADATA_KEY_DISPLAY_SUBTITLE, 153 "OneMedia display subtitle"); 154 155 mQueue.add(new QueueItem(mMetadataBuilder.build().getDescription(), 11)); 156 } 157 158 public interface Listener { onPlayStateChanged(PlaybackState state)159 public void onPlayStateChanged(PlaybackState state); 160 } 161 162 private class RenderListener implements Renderer.Listener { 163 164 @Override onError(int type, int extra, Bundle extras, Throwable error)165 public void onError(int type, int extra, Bundle extras, Throwable error) { 166 Log.d(TAG, "Sending onError with type " + type + " and extra " + extra); 167 PlaybackState.Builder bob = new PlaybackState.Builder(mPlaybackState); 168 bob.setState(PlaybackState.STATE_ERROR, -1, 0, 0); 169 if (error != null) { 170 bob.setErrorMessage(error.getLocalizedMessage()); 171 } 172 mPlaybackState = bob.build(); 173 mSession.setPlaybackState(mPlaybackState); 174 if (mListener != null) { 175 mListener.onPlayStateChanged(mPlaybackState); 176 } 177 } 178 179 @Override onStateChanged(int newState)180 public void onStateChanged(int newState) { 181 long position = -1; 182 if (mRenderer != null) { 183 position = mRenderer.getSeekPosition(); 184 } 185 int pbState; 186 float rate = 0; 187 String errorMsg = null; 188 switch (newState) { 189 case Renderer.STATE_ENDED: 190 case Renderer.STATE_STOPPED: 191 pbState = PlaybackState.STATE_STOPPED; 192 break; 193 case Renderer.STATE_INIT: 194 case Renderer.STATE_PREPARING: 195 pbState = PlaybackState.STATE_BUFFERING; 196 break; 197 case Renderer.STATE_ERROR: 198 pbState = PlaybackState.STATE_ERROR; 199 break; 200 case Renderer.STATE_PAUSED: 201 pbState = PlaybackState.STATE_PAUSED; 202 break; 203 case Renderer.STATE_PLAYING: 204 pbState = PlaybackState.STATE_PLAYING; 205 rate = 1; 206 break; 207 default: 208 pbState = PlaybackState.STATE_ERROR; 209 errorMsg = "unknown state"; 210 break; 211 } 212 PlaybackState.Builder bob = new PlaybackState.Builder(mPlaybackState); 213 bob.setState(pbState, position, rate, SystemClock.elapsedRealtime()); 214 bob.setErrorMessage(errorMsg); 215 mPlaybackState = bob.build(); 216 mSession.setPlaybackState(mPlaybackState); 217 if (mListener != null) { 218 mListener.onPlayStateChanged(mPlaybackState); 219 } 220 } 221 222 @Override onBufferingUpdate(int percent)223 public void onBufferingUpdate(int percent) { 224 } 225 226 @Override onFocusLost()227 public void onFocusLost() { 228 Log.d(TAG, "Focus lost, changing state to " + Renderer.STATE_PAUSED); 229 long position = mRenderer == null ? -1 : mRenderer.getSeekPosition(); 230 PlaybackState.Builder bob = new PlaybackState.Builder(mPlaybackState); 231 bob.setState(PlaybackState.STATE_PAUSED, position, 0, SystemClock.elapsedRealtime()); 232 bob.setErrorMessage(null); 233 mPlaybackState = bob.build(); 234 mSession.setPlaybackState(mPlaybackState); 235 if (mListener != null) { 236 mListener.onPlayStateChanged(mPlaybackState); 237 } 238 } 239 240 @Override onNextStarted()241 public void onNextStarted() { 242 } 243 244 } 245 246 private class SessionCb extends MediaSession.Callback { 247 @Override onPlay()248 public void onPlay() { 249 mRenderer.onPlay(); 250 } 251 252 @Override onPause()253 public void onPause() { 254 mRenderer.onPause(); 255 } 256 } 257 } 258