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