1package com.android.onemedia;
2
3import android.annotation.NonNull;
4import android.annotation.Nullable;
5import android.app.Notification;
6import android.app.NotificationManager;
7import android.app.PendingIntent;
8import android.app.Service;
9import android.content.BroadcastReceiver;
10import android.content.ComponentName;
11import android.content.Context;
12import android.content.Intent;
13import android.content.IntentFilter;
14import android.graphics.Bitmap;
15import android.media.MediaDescription;
16import android.media.MediaMetadata;
17import android.media.session.MediaController;
18import android.media.session.MediaSession;
19import android.media.session.PlaybackState;
20import android.text.format.DateUtils;
21import android.util.Log;
22import android.util.SparseArray;
23
24import com.android.onemedia.playback.RequestUtils;
25
26/**
27 * Keeps track of a notification and updates it automatically for a given
28 * MediaSession.
29 */
30public 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
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     */
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     */
101    public void onStop() {
102        mStarted = false;
103        mController.unregisterCallback(mCb);
104        mService.unregisterReceiver(this);
105        updateNotification();
106    }
107
108    @Override
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
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