1 package com.android.onemedia;
2 
3 import android.annotation.NonNull;
4 import android.annotation.Nullable;
5 import android.app.Notification;
6 import android.app.NotificationManager;
7 import android.app.PendingIntent;
8 import android.app.Service;
9 import android.content.BroadcastReceiver;
10 import android.content.ComponentName;
11 import android.content.Context;
12 import android.content.Intent;
13 import android.content.IntentFilter;
14 import android.graphics.Bitmap;
15 import android.media.MediaDescription;
16 import android.media.MediaMetadata;
17 import android.media.session.MediaController;
18 import android.media.session.MediaSession;
19 import android.media.session.PlaybackState;
20 import android.text.format.DateUtils;
21 import android.util.Log;
22 import android.util.SparseArray;
23 
24 import com.android.onemedia.playback.RequestUtils;
25 
26 /**
27  * Keeps track of a notification and updates it automatically for a given
28  * MediaSession.
29  */
30 public class NotificationHelper extends BroadcastReceiver {
31     private static final String TAG = "NotificationHelper";
32 
33     private static final int NOTIFICATION_ID = 433; // John Cage, 1952
34 
35     private final Service mService;
36     private final MediaSession mSession;
37     private final MediaController mController;
38     private final MediaController.TransportControls mTransportControls;
39     private final SparseArray<PendingIntent> mIntents = new SparseArray<PendingIntent>();
40 
41     private PlaybackState mPlaybackState;
42     private MediaMetadata mMetadata;
43 
44     private boolean mStarted = false;
45 
NotificationHelper(Service service, MediaSession session)46     public NotificationHelper(Service service, MediaSession session) {
47         mService = service;
48         mSession = session;
49         mController = session.getController();
50         mTransportControls = mController.getTransportControls();
51         String pkg = mService.getPackageName();
52 
53         mIntents.put(R.drawable.ic_pause, PendingIntent.getBroadcast(mService, 100, new Intent(
54                 com.android.onemedia.playback.RequestUtils.ACTION_PAUSE).setPackage(pkg),
55                 PendingIntent.FLAG_CANCEL_CURRENT));
56         mIntents.put(R.drawable.ic_play_arrow, PendingIntent.getBroadcast(mService, 100,
57                 new Intent(com.android.onemedia.playback.RequestUtils.ACTION_PLAY).setPackage(pkg),
58                 PendingIntent.FLAG_CANCEL_CURRENT));
59         mIntents.put(R.drawable.ic_skip_previous, PendingIntent.getBroadcast(mService, 100,
60                 new Intent(com.android.onemedia.playback.RequestUtils.ACTION_PREV).setPackage(pkg),
61                 PendingIntent.FLAG_CANCEL_CURRENT));
62         mIntents.put(R.drawable.ic_skip_next, PendingIntent.getBroadcast(mService, 100,
63                 new Intent(com.android.onemedia.playback.RequestUtils.ACTION_NEXT).setPackage(pkg),
64                 PendingIntent.FLAG_CANCEL_CURRENT));
65         mIntents.put(R.drawable.ic_fast_rewind, PendingIntent.getBroadcast(mService, 100,
66                 new Intent(com.android.onemedia.playback.RequestUtils.ACTION_REW).setPackage(pkg),
67                 PendingIntent.FLAG_CANCEL_CURRENT));
68         mIntents.put(R.drawable.ic_fast_forward, PendingIntent.getBroadcast(mService, 100,
69                 new Intent(com.android.onemedia.playback.RequestUtils.ACTION_FFWD).setPackage(pkg),
70                 PendingIntent.FLAG_CANCEL_CURRENT));
71     }
72 
73     /**
74      * Posts the notification and starts tracking the session to keep it
75      * updated. The notification will automatically be removed if the session is
76      * destroyed before {@link #onStop} is called.
77      */
onStart()78     public void onStart() {
79         mController.registerCallback(mCb);
80         IntentFilter filter = new IntentFilter();
81         filter.addAction(RequestUtils.ACTION_FFWD);
82         filter.addAction(RequestUtils.ACTION_NEXT);
83         filter.addAction(RequestUtils.ACTION_PAUSE);
84         filter.addAction(RequestUtils.ACTION_PLAY);
85         filter.addAction(RequestUtils.ACTION_PREV);
86         filter.addAction(RequestUtils.ACTION_REW);
87         mService.registerReceiver(this, filter);
88 
89         mMetadata = mController.getMetadata();
90         mPlaybackState = mController.getPlaybackState();
91 
92         mStarted = true;
93         // The notification must be updated after setting started to true
94         updateNotification();
95     }
96 
97     /**
98      * Removes the notification and stops tracking the session. If the session
99      * was destroyed this has no effect.
100      */
onStop()101     public void onStop() {
102         mStarted = false;
103         mController.unregisterCallback(mCb);
104         mService.unregisterReceiver(this);
105         updateNotification();
106     }
107 
108     @Override
onReceive(Context context, Intent intent)109     public void onReceive(Context context, Intent intent) {
110         final String action = intent.getAction();
111         Log.d(TAG, "Received intent with action " + action);
112         if (RequestUtils.ACTION_PAUSE.equals(action)) {
113             mTransportControls.pause();
114         } else if (RequestUtils.ACTION_PLAY.equals(action)) {
115             mTransportControls.play();
116         } else if (RequestUtils.ACTION_NEXT.equals(action)) {
117             mTransportControls.skipToNext();
118         } else if (RequestUtils.ACTION_PREV.equals(action)) {
119             mTransportControls.skipToPrevious();
120         } else if (RequestUtils.ACTION_REW.equals(action)) {
121             mTransportControls.rewind();
122         } else if (RequestUtils.ACTION_FFWD.equals(action)) {
123             mTransportControls.fastForward();
124         }
125 
126     }
127 
128     private final MediaController.Callback mCb = new MediaController.Callback() {
129         @Override
130         public void onPlaybackStateChanged(PlaybackState state) {
131             mPlaybackState = state;
132             Log.d(TAG, "Received new playback state" + state);
133             updateNotification();
134         }
135 
136         @Override
137         public void onMetadataChanged(MediaMetadata metadata) {
138             mMetadata = metadata;
139             Log.d(TAG, "Received new metadata " + metadata);
140             updateNotification();
141         }
142     };
143 
144     NotificationManager mNoMan = null;
145 
updateNotification()146     private void updateNotification() {
147         if (mNoMan == null) {
148             mNoMan = (NotificationManager) mService.getSystemService(Context.NOTIFICATION_SERVICE);
149         }
150         if (mPlaybackState == null) {
151             mNoMan.cancel(NOTIFICATION_ID);
152             return;
153         }
154         if (!mStarted) {
155             mNoMan.cancel(NOTIFICATION_ID);
156             return;
157         }
158 
159         String status;
160         final int state = mPlaybackState.getState();
161         switch (state) {
162             case PlaybackState.STATE_PLAYING:
163                 status = "PLAYING: ";
164                 break;
165             case PlaybackState.STATE_PAUSED:
166                 status = "PAUSED: ";
167                 break;
168             case PlaybackState.STATE_STOPPED:
169                 status = "STOPPED: ";
170                 break;
171             case PlaybackState.STATE_ERROR:
172                 status = "ERROR: ";
173                 break;
174             case PlaybackState.STATE_BUFFERING:
175                 status = "BUFFERING: ";
176                 break;
177             case PlaybackState.STATE_NONE:
178             default:
179                 status = "";
180                 break;
181         }
182         CharSequence title, text;
183         Bitmap art;
184         if (mMetadata == null) {
185             title = status;
186             text = "Empty metadata!";
187             art = null;
188         } else {
189             MediaDescription description = mMetadata.getDescription();
190             title = description.getTitle();
191             text = description.getSubtitle();
192             art = description.getIconBitmap();
193         }
194 
195         String playPauseLabel = "";
196         int playPauseIcon;
197         if (state == PlaybackState.STATE_PLAYING) {
198             playPauseLabel = "Pause";
199             playPauseIcon = R.drawable.ic_pause;
200         } else {
201             playPauseLabel = "Play";
202             playPauseIcon = R.drawable.ic_play_arrow;
203         }
204 
205         final long pos = mPlaybackState.getPosition();
206         final long end = mMetadata == null ? 0 : mMetadata
207                 .getLong(MediaMetadata.METADATA_KEY_DURATION);
208         Notification notification = new Notification.Builder(mService)
209                 .setSmallIcon(android.R.drawable.stat_notify_chat)
210                 .setContentTitle(title)
211                 .setContentText(text)
212                 .setShowWhen(false)
213                 .setContentInfo(DateUtils.formatElapsedTime(pos))
214                 .setProgress((int) end, (int) pos, false)
215                 .setLargeIcon(art)
216                 .addAction(R.drawable.ic_skip_previous, "Previous",
217                         mIntents.get(R.drawable.ic_skip_previous))
218                 .addAction(R.drawable.ic_fast_rewind, "Rewind",
219                         mIntents.get(R.drawable.ic_fast_rewind))
220                 .addAction(playPauseIcon, playPauseLabel,
221                         mIntents.get(playPauseIcon))
222                 .addAction(R.drawable.ic_fast_forward, "Fast Forward",
223                         mIntents.get(R.drawable.ic_fast_forward))
224                 .addAction(R.drawable.ic_skip_next, "Next",
225                         mIntents.get(R.drawable.ic_skip_next))
226                 .setStyle(new Notification.MediaStyle()
227                         .setShowActionsInCompactView(2)
228                         .setMediaSession(mSession.getSessionToken()))
229                 .setColor(0xFFDB4437)
230                 .build();
231 
232         mService.startForeground(NOTIFICATION_ID, notification);
233     }
234 
235 }
236