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 | PendingIntent.FLAG_MUTABLE_UNAUDITED)); 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 | PendingIntent.FLAG_MUTABLE_UNAUDITED)); 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 | PendingIntent.FLAG_MUTABLE_UNAUDITED)); 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 | PendingIntent.FLAG_MUTABLE_UNAUDITED)); 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 | PendingIntent.FLAG_MUTABLE_UNAUDITED)); 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 | PendingIntent.FLAG_MUTABLE_UNAUDITED)); 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