package com.android.onemedia; import android.annotation.NonNull; import android.annotation.Nullable; import android.app.Notification; import android.app.NotificationManager; import android.app.PendingIntent; import android.app.Service; import android.content.BroadcastReceiver; import android.content.ComponentName; import android.content.Context; import android.content.Intent; import android.content.IntentFilter; import android.graphics.Bitmap; import android.media.MediaDescription; import android.media.MediaMetadata; import android.media.session.MediaController; import android.media.session.MediaSession; import android.media.session.PlaybackState; import android.text.format.DateUtils; import android.util.Log; import android.util.SparseArray; import com.android.onemedia.playback.RequestUtils; /** * Keeps track of a notification and updates it automatically for a given * MediaSession. */ public class NotificationHelper extends BroadcastReceiver { private static final String TAG = "NotificationHelper"; private static final int NOTIFICATION_ID = 433; // John Cage, 1952 private final Service mService; private final MediaSession mSession; private final MediaController mController; private final MediaController.TransportControls mTransportControls; private final SparseArray<PendingIntent> mIntents = new SparseArray<PendingIntent>(); private PlaybackState mPlaybackState; private MediaMetadata mMetadata; private boolean mStarted = false; public NotificationHelper(Service service, MediaSession session) { mService = service; mSession = session; mController = session.getController(); mTransportControls = mController.getTransportControls(); String pkg = mService.getPackageName(); mIntents.put(R.drawable.ic_pause, PendingIntent.getBroadcast(mService, 100, new Intent( com.android.onemedia.playback.RequestUtils.ACTION_PAUSE).setPackage(pkg), PendingIntent.FLAG_CANCEL_CURRENT)); mIntents.put(R.drawable.ic_play_arrow, PendingIntent.getBroadcast(mService, 100, new Intent(com.android.onemedia.playback.RequestUtils.ACTION_PLAY).setPackage(pkg), PendingIntent.FLAG_CANCEL_CURRENT)); mIntents.put(R.drawable.ic_skip_previous, PendingIntent.getBroadcast(mService, 100, new Intent(com.android.onemedia.playback.RequestUtils.ACTION_PREV).setPackage(pkg), PendingIntent.FLAG_CANCEL_CURRENT)); mIntents.put(R.drawable.ic_skip_next, PendingIntent.getBroadcast(mService, 100, new Intent(com.android.onemedia.playback.RequestUtils.ACTION_NEXT).setPackage(pkg), PendingIntent.FLAG_CANCEL_CURRENT)); mIntents.put(R.drawable.ic_fast_rewind, PendingIntent.getBroadcast(mService, 100, new Intent(com.android.onemedia.playback.RequestUtils.ACTION_REW).setPackage(pkg), PendingIntent.FLAG_CANCEL_CURRENT)); mIntents.put(R.drawable.ic_fast_forward, PendingIntent.getBroadcast(mService, 100, new Intent(com.android.onemedia.playback.RequestUtils.ACTION_FFWD).setPackage(pkg), PendingIntent.FLAG_CANCEL_CURRENT)); } /** * Posts the notification and starts tracking the session to keep it * updated. The notification will automatically be removed if the session is * destroyed before {@link #onStop} is called. */ public void onStart() { mController.registerCallback(mCb); IntentFilter filter = new IntentFilter(); filter.addAction(RequestUtils.ACTION_FFWD); filter.addAction(RequestUtils.ACTION_NEXT); filter.addAction(RequestUtils.ACTION_PAUSE); filter.addAction(RequestUtils.ACTION_PLAY); filter.addAction(RequestUtils.ACTION_PREV); filter.addAction(RequestUtils.ACTION_REW); mService.registerReceiver(this, filter); mMetadata = mController.getMetadata(); mPlaybackState = mController.getPlaybackState(); mStarted = true; // The notification must be updated after setting started to true updateNotification(); } /** * Removes the notification and stops tracking the session. If the session * was destroyed this has no effect. */ public void onStop() { mStarted = false; mController.unregisterCallback(mCb); mService.unregisterReceiver(this); updateNotification(); } @Override public void onReceive(Context context, Intent intent) { final String action = intent.getAction(); Log.d(TAG, "Received intent with action " + action); if (RequestUtils.ACTION_PAUSE.equals(action)) { mTransportControls.pause(); } else if (RequestUtils.ACTION_PLAY.equals(action)) { mTransportControls.play(); } else if (RequestUtils.ACTION_NEXT.equals(action)) { mTransportControls.skipToNext(); } else if (RequestUtils.ACTION_PREV.equals(action)) { mTransportControls.skipToPrevious(); } else if (RequestUtils.ACTION_REW.equals(action)) { mTransportControls.rewind(); } else if (RequestUtils.ACTION_FFWD.equals(action)) { mTransportControls.fastForward(); } } private final MediaController.Callback mCb = new MediaController.Callback() { @Override public void onPlaybackStateChanged(PlaybackState state) { mPlaybackState = state; Log.d(TAG, "Received new playback state" + state); updateNotification(); } @Override public void onMetadataChanged(MediaMetadata metadata) { mMetadata = metadata; Log.d(TAG, "Received new metadata " + metadata); updateNotification(); } }; NotificationManager mNoMan = null; private void updateNotification() { if (mNoMan == null) { mNoMan = (NotificationManager) mService.getSystemService(Context.NOTIFICATION_SERVICE); } if (mPlaybackState == null) { mNoMan.cancel(NOTIFICATION_ID); return; } if (!mStarted) { mNoMan.cancel(NOTIFICATION_ID); return; } String status; final int state = mPlaybackState.getState(); switch (state) { case PlaybackState.STATE_PLAYING: status = "PLAYING: "; break; case PlaybackState.STATE_PAUSED: status = "PAUSED: "; break; case PlaybackState.STATE_STOPPED: status = "STOPPED: "; break; case PlaybackState.STATE_ERROR: status = "ERROR: "; break; case PlaybackState.STATE_BUFFERING: status = "BUFFERING: "; break; case PlaybackState.STATE_NONE: default: status = ""; break; } CharSequence title, text; Bitmap art; if (mMetadata == null) { title = status; text = "Empty metadata!"; art = null; } else { MediaDescription description = mMetadata.getDescription(); title = description.getTitle(); text = description.getSubtitle(); art = description.getIconBitmap(); } String playPauseLabel = ""; int playPauseIcon; if (state == PlaybackState.STATE_PLAYING) { playPauseLabel = "Pause"; playPauseIcon = R.drawable.ic_pause; } else { playPauseLabel = "Play"; playPauseIcon = R.drawable.ic_play_arrow; } final long pos = mPlaybackState.getPosition(); final long end = mMetadata == null ? 0 : mMetadata .getLong(MediaMetadata.METADATA_KEY_DURATION); Notification notification = new Notification.Builder(mService) .setSmallIcon(android.R.drawable.stat_notify_chat) .setContentTitle(title) .setContentText(text) .setShowWhen(false) .setContentInfo(DateUtils.formatElapsedTime(pos)) .setProgress((int) end, (int) pos, false) .setLargeIcon(art) .addAction(R.drawable.ic_skip_previous, "Previous", mIntents.get(R.drawable.ic_skip_previous)) .addAction(R.drawable.ic_fast_rewind, "Rewind", mIntents.get(R.drawable.ic_fast_rewind)) .addAction(playPauseIcon, playPauseLabel, mIntents.get(playPauseIcon)) .addAction(R.drawable.ic_fast_forward, "Fast Forward", mIntents.get(R.drawable.ic_fast_forward)) .addAction(R.drawable.ic_skip_next, "Next", mIntents.get(R.drawable.ic_skip_next)) .setStyle(new Notification.MediaStyle() .setShowActionsInCompactView(2) .setMediaSession(mSession.getSessionToken())) .setColor(0xFFDB4437) .build(); mService.startForeground(NOTIFICATION_ID, notification); } }