1a907614755847b2630561a1e5949b2b416600d97Sungsoo Lim/*
2a907614755847b2630561a1e5949b2b416600d97Sungsoo Lim * Copyright (C) 2015 The Android Open Source Project
3a907614755847b2630561a1e5949b2b416600d97Sungsoo Lim *
4a907614755847b2630561a1e5949b2b416600d97Sungsoo Lim * Licensed under the Apache License, Version 2.0 (the "License");
5a907614755847b2630561a1e5949b2b416600d97Sungsoo Lim * you may not use this file except in compliance with the License.
6a907614755847b2630561a1e5949b2b416600d97Sungsoo Lim * You may obtain a copy of the License at
7a907614755847b2630561a1e5949b2b416600d97Sungsoo Lim *
8a907614755847b2630561a1e5949b2b416600d97Sungsoo Lim *      http://www.apache.org/licenses/LICENSE-2.0
9a907614755847b2630561a1e5949b2b416600d97Sungsoo Lim *
10a907614755847b2630561a1e5949b2b416600d97Sungsoo Lim * Unless required by applicable law or agreed to in writing, software
11a907614755847b2630561a1e5949b2b416600d97Sungsoo Lim * distributed under the License is distributed on an "AS IS" BASIS,
12a907614755847b2630561a1e5949b2b416600d97Sungsoo Lim * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13a907614755847b2630561a1e5949b2b416600d97Sungsoo Lim * See the License for the specific language governing permissions and
14a907614755847b2630561a1e5949b2b416600d97Sungsoo Lim * limitations under the License.
15a907614755847b2630561a1e5949b2b416600d97Sungsoo Lim */
16a907614755847b2630561a1e5949b2b416600d97Sungsoo Lim
17a907614755847b2630561a1e5949b2b416600d97Sungsoo Limpackage com.example.android.supportv4.media;
18a907614755847b2630561a1e5949b2b416600d97Sungsoo Lim
19a907614755847b2630561a1e5949b2b416600d97Sungsoo Limimport android.app.Notification;
20a907614755847b2630561a1e5949b2b416600d97Sungsoo Limimport android.app.PendingIntent;
21a907614755847b2630561a1e5949b2b416600d97Sungsoo Limimport android.content.BroadcastReceiver;
22a907614755847b2630561a1e5949b2b416600d97Sungsoo Limimport android.content.Context;
23a907614755847b2630561a1e5949b2b416600d97Sungsoo Limimport android.content.Intent;
24a907614755847b2630561a1e5949b2b416600d97Sungsoo Limimport android.content.IntentFilter;
25a907614755847b2630561a1e5949b2b416600d97Sungsoo Limimport android.graphics.Bitmap;
26a907614755847b2630561a1e5949b2b416600d97Sungsoo Limimport android.graphics.BitmapFactory;
27a907614755847b2630561a1e5949b2b416600d97Sungsoo Limimport android.graphics.Color;
28a907614755847b2630561a1e5949b2b416600d97Sungsoo Limimport android.os.RemoteException;
29a907614755847b2630561a1e5949b2b416600d97Sungsoo Limimport android.support.v4.app.NotificationCompat;
30a907614755847b2630561a1e5949b2b416600d97Sungsoo Limimport android.support.v4.app.NotificationManagerCompat;
31a907614755847b2630561a1e5949b2b416600d97Sungsoo Limimport android.support.v4.media.MediaDescriptionCompat;
32a907614755847b2630561a1e5949b2b416600d97Sungsoo Limimport android.support.v4.media.MediaMetadataCompat;
33a907614755847b2630561a1e5949b2b416600d97Sungsoo Limimport android.support.v4.media.session.MediaControllerCompat;
34a907614755847b2630561a1e5949b2b416600d97Sungsoo Limimport android.support.v4.media.session.MediaSessionCompat;
35a907614755847b2630561a1e5949b2b416600d97Sungsoo Limimport android.support.v4.media.session.PlaybackStateCompat;
36a907614755847b2630561a1e5949b2b416600d97Sungsoo Limimport android.util.Log;
37a907614755847b2630561a1e5949b2b416600d97Sungsoo Lim
38a907614755847b2630561a1e5949b2b416600d97Sungsoo Limimport com.example.android.supportv4.R;
39a907614755847b2630561a1e5949b2b416600d97Sungsoo Limimport com.example.android.supportv4.media.utils.ResourceHelper;
40a907614755847b2630561a1e5949b2b416600d97Sungsoo Lim
41a907614755847b2630561a1e5949b2b416600d97Sungsoo Lim/**
42a907614755847b2630561a1e5949b2b416600d97Sungsoo Lim * Keeps track of a notification and updates it automatically for a given
43a907614755847b2630561a1e5949b2b416600d97Sungsoo Lim * MediaSession. Maintaining a visible notification (usually) guarantees that the music service
44a907614755847b2630561a1e5949b2b416600d97Sungsoo Lim * won't be killed during playback.
45a907614755847b2630561a1e5949b2b416600d97Sungsoo Lim */
46a907614755847b2630561a1e5949b2b416600d97Sungsoo Limpublic class MediaNotificationManager extends BroadcastReceiver {
47a907614755847b2630561a1e5949b2b416600d97Sungsoo Lim    private static final String TAG = "MediaNotiManager";
48a907614755847b2630561a1e5949b2b416600d97Sungsoo Lim
49a907614755847b2630561a1e5949b2b416600d97Sungsoo Lim    private static final int NOTIFICATION_ID = 412;
50a907614755847b2630561a1e5949b2b416600d97Sungsoo Lim    private static final int REQUEST_CODE = 100;
51a907614755847b2630561a1e5949b2b416600d97Sungsoo Lim
52a907614755847b2630561a1e5949b2b416600d97Sungsoo Lim    public static final String ACTION_PAUSE = "com.example.android.supportv4.media.pause";
53a907614755847b2630561a1e5949b2b416600d97Sungsoo Lim    public static final String ACTION_PLAY = "com.example.android.supportv4.media.play";
54a907614755847b2630561a1e5949b2b416600d97Sungsoo Lim    public static final String ACTION_PREV = "com.example.android.supportv4.media.prev";
55a907614755847b2630561a1e5949b2b416600d97Sungsoo Lim    public static final String ACTION_NEXT = "com.example.android.supportv4.media.next";
56a907614755847b2630561a1e5949b2b416600d97Sungsoo Lim
57a907614755847b2630561a1e5949b2b416600d97Sungsoo Lim    private final MediaBrowserServiceSupport mService;
58a907614755847b2630561a1e5949b2b416600d97Sungsoo Lim    private MediaSessionCompat.Token mSessionToken;
59a907614755847b2630561a1e5949b2b416600d97Sungsoo Lim    private MediaControllerCompat mController;
60a907614755847b2630561a1e5949b2b416600d97Sungsoo Lim    private MediaControllerCompat.TransportControls mTransportControls;
61a907614755847b2630561a1e5949b2b416600d97Sungsoo Lim
62a907614755847b2630561a1e5949b2b416600d97Sungsoo Lim    private PlaybackStateCompat mPlaybackState;
63a907614755847b2630561a1e5949b2b416600d97Sungsoo Lim    private MediaMetadataCompat mMetadata;
64a907614755847b2630561a1e5949b2b416600d97Sungsoo Lim
65a907614755847b2630561a1e5949b2b416600d97Sungsoo Lim    private NotificationManagerCompat mNotificationManager;
66a907614755847b2630561a1e5949b2b416600d97Sungsoo Lim
67a907614755847b2630561a1e5949b2b416600d97Sungsoo Lim    private PendingIntent mPauseIntent;
68a907614755847b2630561a1e5949b2b416600d97Sungsoo Lim    private PendingIntent mPlayIntent;
69a907614755847b2630561a1e5949b2b416600d97Sungsoo Lim    private PendingIntent mPreviousIntent;
70a907614755847b2630561a1e5949b2b416600d97Sungsoo Lim    private PendingIntent mNextIntent;
71a907614755847b2630561a1e5949b2b416600d97Sungsoo Lim
72a907614755847b2630561a1e5949b2b416600d97Sungsoo Lim    private int mNotificationColor;
73a907614755847b2630561a1e5949b2b416600d97Sungsoo Lim
74a907614755847b2630561a1e5949b2b416600d97Sungsoo Lim    private boolean mStarted = false;
75a907614755847b2630561a1e5949b2b416600d97Sungsoo Lim
76a907614755847b2630561a1e5949b2b416600d97Sungsoo Lim    public MediaNotificationManager(MediaBrowserServiceSupport service) {
77a907614755847b2630561a1e5949b2b416600d97Sungsoo Lim        mService = service;
78a907614755847b2630561a1e5949b2b416600d97Sungsoo Lim        updateSessionToken();
79a907614755847b2630561a1e5949b2b416600d97Sungsoo Lim
80a907614755847b2630561a1e5949b2b416600d97Sungsoo Lim        mNotificationColor = ResourceHelper.getThemeColor(mService,
81a907614755847b2630561a1e5949b2b416600d97Sungsoo Lim            android.R.attr.colorPrimary, Color.DKGRAY);
82a907614755847b2630561a1e5949b2b416600d97Sungsoo Lim
83a907614755847b2630561a1e5949b2b416600d97Sungsoo Lim        mNotificationManager = NotificationManagerCompat.from(mService);
84a907614755847b2630561a1e5949b2b416600d97Sungsoo Lim
85a907614755847b2630561a1e5949b2b416600d97Sungsoo Lim        String pkg = mService.getPackageName();
86a907614755847b2630561a1e5949b2b416600d97Sungsoo Lim        mPauseIntent = PendingIntent.getBroadcast(mService, REQUEST_CODE,
87a907614755847b2630561a1e5949b2b416600d97Sungsoo Lim                new Intent(ACTION_PAUSE).setPackage(pkg), PendingIntent.FLAG_CANCEL_CURRENT);
88a907614755847b2630561a1e5949b2b416600d97Sungsoo Lim        mPlayIntent = PendingIntent.getBroadcast(mService, REQUEST_CODE,
89a907614755847b2630561a1e5949b2b416600d97Sungsoo Lim                new Intent(ACTION_PLAY).setPackage(pkg), PendingIntent.FLAG_CANCEL_CURRENT);
90a907614755847b2630561a1e5949b2b416600d97Sungsoo Lim        mPreviousIntent = PendingIntent.getBroadcast(mService, REQUEST_CODE,
91a907614755847b2630561a1e5949b2b416600d97Sungsoo Lim                new Intent(ACTION_PREV).setPackage(pkg), PendingIntent.FLAG_CANCEL_CURRENT);
92a907614755847b2630561a1e5949b2b416600d97Sungsoo Lim        mNextIntent = PendingIntent.getBroadcast(mService, REQUEST_CODE,
93a907614755847b2630561a1e5949b2b416600d97Sungsoo Lim                new Intent(ACTION_NEXT).setPackage(pkg), PendingIntent.FLAG_CANCEL_CURRENT);
94a907614755847b2630561a1e5949b2b416600d97Sungsoo Lim
95a907614755847b2630561a1e5949b2b416600d97Sungsoo Lim        // Cancel all notifications to handle the case where the Service was killed and
96a907614755847b2630561a1e5949b2b416600d97Sungsoo Lim        // restarted by the system.
97a907614755847b2630561a1e5949b2b416600d97Sungsoo Lim        mNotificationManager.cancelAll();
98a907614755847b2630561a1e5949b2b416600d97Sungsoo Lim    }
99a907614755847b2630561a1e5949b2b416600d97Sungsoo Lim
100a907614755847b2630561a1e5949b2b416600d97Sungsoo Lim    /**
101a907614755847b2630561a1e5949b2b416600d97Sungsoo Lim     * Posts the notification and starts tracking the session to keep it
102a907614755847b2630561a1e5949b2b416600d97Sungsoo Lim     * updated. The notification will automatically be removed if the session is
103a907614755847b2630561a1e5949b2b416600d97Sungsoo Lim     * destroyed before {@link #stopNotification} is called.
104a907614755847b2630561a1e5949b2b416600d97Sungsoo Lim     */
105a907614755847b2630561a1e5949b2b416600d97Sungsoo Lim    public void startNotification() {
106a907614755847b2630561a1e5949b2b416600d97Sungsoo Lim        if (!mStarted) {
107a907614755847b2630561a1e5949b2b416600d97Sungsoo Lim            mMetadata = mController.getMetadata();
108a907614755847b2630561a1e5949b2b416600d97Sungsoo Lim            mPlaybackState = mController.getPlaybackState();
109a907614755847b2630561a1e5949b2b416600d97Sungsoo Lim
110a907614755847b2630561a1e5949b2b416600d97Sungsoo Lim            // The notification must be updated after setting started to true
111a907614755847b2630561a1e5949b2b416600d97Sungsoo Lim            Notification notification = createNotification();
112a907614755847b2630561a1e5949b2b416600d97Sungsoo Lim            if (notification != null) {
113a907614755847b2630561a1e5949b2b416600d97Sungsoo Lim                mController.registerCallback(mCb);
114a907614755847b2630561a1e5949b2b416600d97Sungsoo Lim                IntentFilter filter = new IntentFilter();
115a907614755847b2630561a1e5949b2b416600d97Sungsoo Lim                filter.addAction(ACTION_NEXT);
116a907614755847b2630561a1e5949b2b416600d97Sungsoo Lim                filter.addAction(ACTION_PAUSE);
117a907614755847b2630561a1e5949b2b416600d97Sungsoo Lim                filter.addAction(ACTION_PLAY);
118a907614755847b2630561a1e5949b2b416600d97Sungsoo Lim                filter.addAction(ACTION_PREV);
119a907614755847b2630561a1e5949b2b416600d97Sungsoo Lim                mService.registerReceiver(this, filter);
120a907614755847b2630561a1e5949b2b416600d97Sungsoo Lim
121a907614755847b2630561a1e5949b2b416600d97Sungsoo Lim                mService.startForeground(NOTIFICATION_ID, notification);
122a907614755847b2630561a1e5949b2b416600d97Sungsoo Lim                mStarted = true;
123a907614755847b2630561a1e5949b2b416600d97Sungsoo Lim            }
124a907614755847b2630561a1e5949b2b416600d97Sungsoo Lim        }
125a907614755847b2630561a1e5949b2b416600d97Sungsoo Lim    }
126a907614755847b2630561a1e5949b2b416600d97Sungsoo Lim
127a907614755847b2630561a1e5949b2b416600d97Sungsoo Lim    /**
128a907614755847b2630561a1e5949b2b416600d97Sungsoo Lim     * Removes the notification and stops tracking the session. If the session
129a907614755847b2630561a1e5949b2b416600d97Sungsoo Lim     * was destroyed this has no effect.
130a907614755847b2630561a1e5949b2b416600d97Sungsoo Lim     */
131a907614755847b2630561a1e5949b2b416600d97Sungsoo Lim    public void stopNotification() {
132a907614755847b2630561a1e5949b2b416600d97Sungsoo Lim        if (mStarted) {
133a907614755847b2630561a1e5949b2b416600d97Sungsoo Lim            mStarted = false;
134a907614755847b2630561a1e5949b2b416600d97Sungsoo Lim            mController.unregisterCallback(mCb);
135a907614755847b2630561a1e5949b2b416600d97Sungsoo Lim            try {
136a907614755847b2630561a1e5949b2b416600d97Sungsoo Lim                mNotificationManager.cancel(NOTIFICATION_ID);
137a907614755847b2630561a1e5949b2b416600d97Sungsoo Lim                mService.unregisterReceiver(this);
138a907614755847b2630561a1e5949b2b416600d97Sungsoo Lim            } catch (IllegalArgumentException ex) {
139a907614755847b2630561a1e5949b2b416600d97Sungsoo Lim                // ignore if the receiver is not registered.
140a907614755847b2630561a1e5949b2b416600d97Sungsoo Lim            }
141a907614755847b2630561a1e5949b2b416600d97Sungsoo Lim            mService.stopForeground(true);
142a907614755847b2630561a1e5949b2b416600d97Sungsoo Lim        }
143a907614755847b2630561a1e5949b2b416600d97Sungsoo Lim    }
144a907614755847b2630561a1e5949b2b416600d97Sungsoo Lim
145a907614755847b2630561a1e5949b2b416600d97Sungsoo Lim    @Override
146a907614755847b2630561a1e5949b2b416600d97Sungsoo Lim    public void onReceive(Context context, Intent intent) {
147a907614755847b2630561a1e5949b2b416600d97Sungsoo Lim        final String action = intent.getAction();
148a907614755847b2630561a1e5949b2b416600d97Sungsoo Lim        Log.d(TAG, "Received intent with action " + action);
149a907614755847b2630561a1e5949b2b416600d97Sungsoo Lim        switch (action) {
150a907614755847b2630561a1e5949b2b416600d97Sungsoo Lim            case ACTION_PAUSE:
151a907614755847b2630561a1e5949b2b416600d97Sungsoo Lim                mTransportControls.pause();
152a907614755847b2630561a1e5949b2b416600d97Sungsoo Lim                break;
153a907614755847b2630561a1e5949b2b416600d97Sungsoo Lim            case ACTION_PLAY:
154a907614755847b2630561a1e5949b2b416600d97Sungsoo Lim                mTransportControls.play();
155a907614755847b2630561a1e5949b2b416600d97Sungsoo Lim                break;
156a907614755847b2630561a1e5949b2b416600d97Sungsoo Lim            case ACTION_NEXT:
157a907614755847b2630561a1e5949b2b416600d97Sungsoo Lim                mTransportControls.skipToNext();
158a907614755847b2630561a1e5949b2b416600d97Sungsoo Lim                break;
159a907614755847b2630561a1e5949b2b416600d97Sungsoo Lim            case ACTION_PREV:
160a907614755847b2630561a1e5949b2b416600d97Sungsoo Lim                mTransportControls.skipToPrevious();
161a907614755847b2630561a1e5949b2b416600d97Sungsoo Lim                break;
162a907614755847b2630561a1e5949b2b416600d97Sungsoo Lim            default:
163a907614755847b2630561a1e5949b2b416600d97Sungsoo Lim                Log.w(TAG, "Unknown intent ignored. Action=" + action);
164a907614755847b2630561a1e5949b2b416600d97Sungsoo Lim        }
165a907614755847b2630561a1e5949b2b416600d97Sungsoo Lim    }
166a907614755847b2630561a1e5949b2b416600d97Sungsoo Lim
167a907614755847b2630561a1e5949b2b416600d97Sungsoo Lim    /**
168a907614755847b2630561a1e5949b2b416600d97Sungsoo Lim     * Update the state based on a change on the session token. Called either when
169a907614755847b2630561a1e5949b2b416600d97Sungsoo Lim     * we are running for the first time or when the media session owner has destroyed the session
170a907614755847b2630561a1e5949b2b416600d97Sungsoo Lim     * (see {@link android.media.session.MediaController.Callback#onSessionDestroyed()})
171a907614755847b2630561a1e5949b2b416600d97Sungsoo Lim     */
172a907614755847b2630561a1e5949b2b416600d97Sungsoo Lim    private void updateSessionToken() {
173a907614755847b2630561a1e5949b2b416600d97Sungsoo Lim        MediaSessionCompat.Token freshToken = mService.getSessionToken();
174a907614755847b2630561a1e5949b2b416600d97Sungsoo Lim        if (mSessionToken == null || !mSessionToken.equals(freshToken)) {
175a907614755847b2630561a1e5949b2b416600d97Sungsoo Lim            if (mController != null) {
176a907614755847b2630561a1e5949b2b416600d97Sungsoo Lim                mController.unregisterCallback(mCb);
177a907614755847b2630561a1e5949b2b416600d97Sungsoo Lim            }
178a907614755847b2630561a1e5949b2b416600d97Sungsoo Lim            mSessionToken = freshToken;
179a907614755847b2630561a1e5949b2b416600d97Sungsoo Lim            try {
180a907614755847b2630561a1e5949b2b416600d97Sungsoo Lim                mController = new MediaControllerCompat(mService, mSessionToken);
181a907614755847b2630561a1e5949b2b416600d97Sungsoo Lim            } catch (RemoteException e) {
182a907614755847b2630561a1e5949b2b416600d97Sungsoo Lim                Log.e(TAG, "Failed to create MediaControllerCompat.", e);
183a907614755847b2630561a1e5949b2b416600d97Sungsoo Lim            }
184a907614755847b2630561a1e5949b2b416600d97Sungsoo Lim            mTransportControls = mController.getTransportControls();
185a907614755847b2630561a1e5949b2b416600d97Sungsoo Lim            if (mStarted) {
186a907614755847b2630561a1e5949b2b416600d97Sungsoo Lim                mController.registerCallback(mCb);
187a907614755847b2630561a1e5949b2b416600d97Sungsoo Lim            }
188a907614755847b2630561a1e5949b2b416600d97Sungsoo Lim        }
189a907614755847b2630561a1e5949b2b416600d97Sungsoo Lim    }
190a907614755847b2630561a1e5949b2b416600d97Sungsoo Lim
191a907614755847b2630561a1e5949b2b416600d97Sungsoo Lim    private PendingIntent createContentIntent() {
192a907614755847b2630561a1e5949b2b416600d97Sungsoo Lim        Intent openUI = new Intent(mService, MediaBrowserSupport.class);
193a907614755847b2630561a1e5949b2b416600d97Sungsoo Lim        openUI.setFlags(Intent.FLAG_ACTIVITY_SINGLE_TOP);
194a907614755847b2630561a1e5949b2b416600d97Sungsoo Lim        return PendingIntent.getActivity(mService, REQUEST_CODE, openUI,
195a907614755847b2630561a1e5949b2b416600d97Sungsoo Lim                PendingIntent.FLAG_CANCEL_CURRENT);
196a907614755847b2630561a1e5949b2b416600d97Sungsoo Lim    }
197a907614755847b2630561a1e5949b2b416600d97Sungsoo Lim
198a907614755847b2630561a1e5949b2b416600d97Sungsoo Lim    private final MediaControllerCompat.Callback mCb = new MediaControllerCompat.Callback() {
199a907614755847b2630561a1e5949b2b416600d97Sungsoo Lim        @Override
200a907614755847b2630561a1e5949b2b416600d97Sungsoo Lim        public void onPlaybackStateChanged(PlaybackStateCompat state) {
201a907614755847b2630561a1e5949b2b416600d97Sungsoo Lim            mPlaybackState = state;
202a907614755847b2630561a1e5949b2b416600d97Sungsoo Lim            Log.d(TAG, "Received new playback state " + state);
203a907614755847b2630561a1e5949b2b416600d97Sungsoo Lim            if (state != null && (state.getState() == PlaybackStateCompat.STATE_STOPPED ||
204a907614755847b2630561a1e5949b2b416600d97Sungsoo Lim                    state.getState() == PlaybackStateCompat.STATE_NONE)) {
205a907614755847b2630561a1e5949b2b416600d97Sungsoo Lim                stopNotification();
206a907614755847b2630561a1e5949b2b416600d97Sungsoo Lim            } else {
207a907614755847b2630561a1e5949b2b416600d97Sungsoo Lim                Notification notification = createNotification();
208a907614755847b2630561a1e5949b2b416600d97Sungsoo Lim                if (notification != null) {
209a907614755847b2630561a1e5949b2b416600d97Sungsoo Lim                    mNotificationManager.notify(NOTIFICATION_ID, notification);
210a907614755847b2630561a1e5949b2b416600d97Sungsoo Lim                }
211a907614755847b2630561a1e5949b2b416600d97Sungsoo Lim            }
212a907614755847b2630561a1e5949b2b416600d97Sungsoo Lim        }
213a907614755847b2630561a1e5949b2b416600d97Sungsoo Lim
214a907614755847b2630561a1e5949b2b416600d97Sungsoo Lim        @Override
215a907614755847b2630561a1e5949b2b416600d97Sungsoo Lim        public void onMetadataChanged(MediaMetadataCompat metadata) {
216a907614755847b2630561a1e5949b2b416600d97Sungsoo Lim            mMetadata = metadata;
217a907614755847b2630561a1e5949b2b416600d97Sungsoo Lim            Log.d(TAG, "Received new metadata " + metadata);
218a907614755847b2630561a1e5949b2b416600d97Sungsoo Lim            Notification notification = createNotification();
219a907614755847b2630561a1e5949b2b416600d97Sungsoo Lim            if (notification != null) {
220a907614755847b2630561a1e5949b2b416600d97Sungsoo Lim                mNotificationManager.notify(NOTIFICATION_ID, notification);
221a907614755847b2630561a1e5949b2b416600d97Sungsoo Lim            }
222a907614755847b2630561a1e5949b2b416600d97Sungsoo Lim        }
223a907614755847b2630561a1e5949b2b416600d97Sungsoo Lim
224a907614755847b2630561a1e5949b2b416600d97Sungsoo Lim        @Override
225a907614755847b2630561a1e5949b2b416600d97Sungsoo Lim        public void onSessionDestroyed() {
226a907614755847b2630561a1e5949b2b416600d97Sungsoo Lim            super.onSessionDestroyed();
227a907614755847b2630561a1e5949b2b416600d97Sungsoo Lim            Log.d(TAG, "Session was destroyed, resetting to the new session token");
228a907614755847b2630561a1e5949b2b416600d97Sungsoo Lim            updateSessionToken();
229a907614755847b2630561a1e5949b2b416600d97Sungsoo Lim        }
230a907614755847b2630561a1e5949b2b416600d97Sungsoo Lim    };
231a907614755847b2630561a1e5949b2b416600d97Sungsoo Lim
232a907614755847b2630561a1e5949b2b416600d97Sungsoo Lim    private Notification createNotification() {
233a907614755847b2630561a1e5949b2b416600d97Sungsoo Lim        Log.d(TAG, "updateNotificationMetadata. mMetadata=" + mMetadata);
234a907614755847b2630561a1e5949b2b416600d97Sungsoo Lim        if (mMetadata == null || mPlaybackState == null) {
235a907614755847b2630561a1e5949b2b416600d97Sungsoo Lim            return null;
236a907614755847b2630561a1e5949b2b416600d97Sungsoo Lim        }
237a907614755847b2630561a1e5949b2b416600d97Sungsoo Lim
238a907614755847b2630561a1e5949b2b416600d97Sungsoo Lim        NotificationCompat.Builder notificationBuilder = new NotificationCompat.Builder(mService);
239a907614755847b2630561a1e5949b2b416600d97Sungsoo Lim
240a907614755847b2630561a1e5949b2b416600d97Sungsoo Lim        // If skip to previous action is enabled
241a907614755847b2630561a1e5949b2b416600d97Sungsoo Lim        if ((mPlaybackState.getActions() & PlaybackStateCompat.ACTION_SKIP_TO_PREVIOUS) != 0) {
242a907614755847b2630561a1e5949b2b416600d97Sungsoo Lim            notificationBuilder.addAction(R.drawable.ic_skip_previous_white_24dp,
243a907614755847b2630561a1e5949b2b416600d97Sungsoo Lim                        mService.getString(R.string.label_previous), mPreviousIntent);
244a907614755847b2630561a1e5949b2b416600d97Sungsoo Lim        }
245a907614755847b2630561a1e5949b2b416600d97Sungsoo Lim
246a907614755847b2630561a1e5949b2b416600d97Sungsoo Lim        addPlayPauseAction(notificationBuilder);
247a907614755847b2630561a1e5949b2b416600d97Sungsoo Lim
248a907614755847b2630561a1e5949b2b416600d97Sungsoo Lim        // If skip to next action is enabled
249a907614755847b2630561a1e5949b2b416600d97Sungsoo Lim        if ((mPlaybackState.getActions() & PlaybackStateCompat.ACTION_SKIP_TO_NEXT) != 0) {
250a907614755847b2630561a1e5949b2b416600d97Sungsoo Lim            notificationBuilder.addAction(R.drawable.ic_skip_next_white_24dp,
251a907614755847b2630561a1e5949b2b416600d97Sungsoo Lim                mService.getString(R.string.label_next), mNextIntent);
252a907614755847b2630561a1e5949b2b416600d97Sungsoo Lim        }
253a907614755847b2630561a1e5949b2b416600d97Sungsoo Lim
254a907614755847b2630561a1e5949b2b416600d97Sungsoo Lim        MediaDescriptionCompat description = mMetadata.getDescription();
255a907614755847b2630561a1e5949b2b416600d97Sungsoo Lim
256a907614755847b2630561a1e5949b2b416600d97Sungsoo Lim        String fetchArtUrl = null;
257a907614755847b2630561a1e5949b2b416600d97Sungsoo Lim        Bitmap art = null;
258a907614755847b2630561a1e5949b2b416600d97Sungsoo Lim        if (description.getIconUri() != null) {
259a907614755847b2630561a1e5949b2b416600d97Sungsoo Lim            // This sample assumes the iconUri will be a valid URL formatted String, but
260a907614755847b2630561a1e5949b2b416600d97Sungsoo Lim            // it can actually be any valid Android Uri formatted String.
261a907614755847b2630561a1e5949b2b416600d97Sungsoo Lim            // async fetch the album art icon
262a907614755847b2630561a1e5949b2b416600d97Sungsoo Lim            String artUrl = description.getIconUri().toString();
263a907614755847b2630561a1e5949b2b416600d97Sungsoo Lim            art = AlbumArtCache.getInstance().getBigImage(artUrl);
264a907614755847b2630561a1e5949b2b416600d97Sungsoo Lim            if (art == null) {
265a907614755847b2630561a1e5949b2b416600d97Sungsoo Lim                fetchArtUrl = artUrl;
266a907614755847b2630561a1e5949b2b416600d97Sungsoo Lim                // use a placeholder art while the remote art is being downloaded
267a907614755847b2630561a1e5949b2b416600d97Sungsoo Lim                art = BitmapFactory.decodeResource(mService.getResources(),
268a907614755847b2630561a1e5949b2b416600d97Sungsoo Lim                    R.drawable.ic_default_art);
269a907614755847b2630561a1e5949b2b416600d97Sungsoo Lim            }
270a907614755847b2630561a1e5949b2b416600d97Sungsoo Lim        }
271a907614755847b2630561a1e5949b2b416600d97Sungsoo Lim
272a907614755847b2630561a1e5949b2b416600d97Sungsoo Lim        notificationBuilder
273a907614755847b2630561a1e5949b2b416600d97Sungsoo Lim                .setColor(mNotificationColor)
274a907614755847b2630561a1e5949b2b416600d97Sungsoo Lim                .setSmallIcon(R.drawable.ic_notification)
275a907614755847b2630561a1e5949b2b416600d97Sungsoo Lim                .setVisibility(NotificationCompat.VISIBILITY_PUBLIC)
276a907614755847b2630561a1e5949b2b416600d97Sungsoo Lim                .setUsesChronometer(true)
277a907614755847b2630561a1e5949b2b416600d97Sungsoo Lim                .setContentIntent(createContentIntent())
278a907614755847b2630561a1e5949b2b416600d97Sungsoo Lim                .setContentTitle(description.getTitle())
279a907614755847b2630561a1e5949b2b416600d97Sungsoo Lim                .setContentText(description.getSubtitle())
280a907614755847b2630561a1e5949b2b416600d97Sungsoo Lim                .setLargeIcon(art);
281a907614755847b2630561a1e5949b2b416600d97Sungsoo Lim
282a907614755847b2630561a1e5949b2b416600d97Sungsoo Lim        setNotificationPlaybackState(notificationBuilder);
283a907614755847b2630561a1e5949b2b416600d97Sungsoo Lim        if (fetchArtUrl != null) {
284a907614755847b2630561a1e5949b2b416600d97Sungsoo Lim            fetchBitmapFromURLAsync(fetchArtUrl, notificationBuilder);
285a907614755847b2630561a1e5949b2b416600d97Sungsoo Lim        }
286a907614755847b2630561a1e5949b2b416600d97Sungsoo Lim
287a907614755847b2630561a1e5949b2b416600d97Sungsoo Lim        return notificationBuilder.build();
288a907614755847b2630561a1e5949b2b416600d97Sungsoo Lim    }
289a907614755847b2630561a1e5949b2b416600d97Sungsoo Lim
290a907614755847b2630561a1e5949b2b416600d97Sungsoo Lim    private void addPlayPauseAction(NotificationCompat.Builder builder) {
291a907614755847b2630561a1e5949b2b416600d97Sungsoo Lim        Log.d(TAG, "updatePlayPauseAction");
292a907614755847b2630561a1e5949b2b416600d97Sungsoo Lim        String label;
293a907614755847b2630561a1e5949b2b416600d97Sungsoo Lim        int icon;
294a907614755847b2630561a1e5949b2b416600d97Sungsoo Lim        PendingIntent intent;
295a907614755847b2630561a1e5949b2b416600d97Sungsoo Lim        if (mPlaybackState.getState() == PlaybackStateCompat.STATE_PLAYING) {
296a907614755847b2630561a1e5949b2b416600d97Sungsoo Lim            label = mService.getString(R.string.label_pause);
297a907614755847b2630561a1e5949b2b416600d97Sungsoo Lim            icon = R.drawable.ic_pause_white_24dp;
298a907614755847b2630561a1e5949b2b416600d97Sungsoo Lim            intent = mPauseIntent;
299a907614755847b2630561a1e5949b2b416600d97Sungsoo Lim        } else {
300a907614755847b2630561a1e5949b2b416600d97Sungsoo Lim            label = mService.getString(R.string.label_play);
301a907614755847b2630561a1e5949b2b416600d97Sungsoo Lim            icon = R.drawable.ic_play_arrow_white_24dp;
302a907614755847b2630561a1e5949b2b416600d97Sungsoo Lim            intent = mPlayIntent;
303a907614755847b2630561a1e5949b2b416600d97Sungsoo Lim        }
304a907614755847b2630561a1e5949b2b416600d97Sungsoo Lim        builder.addAction(new NotificationCompat.Action(icon, label, intent));
305a907614755847b2630561a1e5949b2b416600d97Sungsoo Lim    }
306a907614755847b2630561a1e5949b2b416600d97Sungsoo Lim
307a907614755847b2630561a1e5949b2b416600d97Sungsoo Lim    private void setNotificationPlaybackState(NotificationCompat.Builder builder) {
308a907614755847b2630561a1e5949b2b416600d97Sungsoo Lim        Log.d(TAG, "updateNotificationPlaybackState. mPlaybackState=" + mPlaybackState);
309a907614755847b2630561a1e5949b2b416600d97Sungsoo Lim        if (mPlaybackState == null || !mStarted) {
310a907614755847b2630561a1e5949b2b416600d97Sungsoo Lim            Log.d(TAG, "updateNotificationPlaybackState. cancelling notification!");
311a907614755847b2630561a1e5949b2b416600d97Sungsoo Lim            mService.stopForeground(true);
312a907614755847b2630561a1e5949b2b416600d97Sungsoo Lim            return;
313a907614755847b2630561a1e5949b2b416600d97Sungsoo Lim        }
314a907614755847b2630561a1e5949b2b416600d97Sungsoo Lim        if (mPlaybackState.getState() == PlaybackStateCompat.STATE_PLAYING
315a907614755847b2630561a1e5949b2b416600d97Sungsoo Lim                && mPlaybackState.getPosition() >= 0) {
316a907614755847b2630561a1e5949b2b416600d97Sungsoo Lim            Log.d(TAG, "updateNotificationPlaybackState. updating playback position to "
317a907614755847b2630561a1e5949b2b416600d97Sungsoo Lim                    + (System.currentTimeMillis() - mPlaybackState.getPosition()) / 1000
318a907614755847b2630561a1e5949b2b416600d97Sungsoo Lim                    + " seconds");
319a907614755847b2630561a1e5949b2b416600d97Sungsoo Lim            builder
320a907614755847b2630561a1e5949b2b416600d97Sungsoo Lim                .setWhen(System.currentTimeMillis() - mPlaybackState.getPosition())
321a907614755847b2630561a1e5949b2b416600d97Sungsoo Lim                .setShowWhen(true)
322a907614755847b2630561a1e5949b2b416600d97Sungsoo Lim                .setUsesChronometer(true);
323a907614755847b2630561a1e5949b2b416600d97Sungsoo Lim        } else {
324a907614755847b2630561a1e5949b2b416600d97Sungsoo Lim            Log.d(TAG, "updateNotificationPlaybackState. hiding playback position");
325a907614755847b2630561a1e5949b2b416600d97Sungsoo Lim            builder
326a907614755847b2630561a1e5949b2b416600d97Sungsoo Lim                .setWhen(0)
327a907614755847b2630561a1e5949b2b416600d97Sungsoo Lim                .setShowWhen(false)
328a907614755847b2630561a1e5949b2b416600d97Sungsoo Lim                .setUsesChronometer(false);
329a907614755847b2630561a1e5949b2b416600d97Sungsoo Lim        }
330a907614755847b2630561a1e5949b2b416600d97Sungsoo Lim
331a907614755847b2630561a1e5949b2b416600d97Sungsoo Lim        // Make sure that the notification can be dismissed by the user when we are not playing:
332a907614755847b2630561a1e5949b2b416600d97Sungsoo Lim        builder.setOngoing(mPlaybackState.getState() == PlaybackStateCompat.STATE_PLAYING);
333a907614755847b2630561a1e5949b2b416600d97Sungsoo Lim    }
334a907614755847b2630561a1e5949b2b416600d97Sungsoo Lim
335a907614755847b2630561a1e5949b2b416600d97Sungsoo Lim    private void fetchBitmapFromURLAsync(final String bitmapUrl,
336a907614755847b2630561a1e5949b2b416600d97Sungsoo Lim                                         final NotificationCompat.Builder builder) {
337a907614755847b2630561a1e5949b2b416600d97Sungsoo Lim        AlbumArtCache.getInstance().fetch(bitmapUrl, new AlbumArtCache.FetchListener() {
338a907614755847b2630561a1e5949b2b416600d97Sungsoo Lim            @Override
339a907614755847b2630561a1e5949b2b416600d97Sungsoo Lim            public void onFetched(String artUrl, Bitmap bitmap, Bitmap icon) {
340a907614755847b2630561a1e5949b2b416600d97Sungsoo Lim                if (mMetadata != null && mMetadata.getDescription() != null &&
341a907614755847b2630561a1e5949b2b416600d97Sungsoo Lim                    artUrl.equals(mMetadata.getDescription().getIconUri().toString())) {
342a907614755847b2630561a1e5949b2b416600d97Sungsoo Lim                    // If the media is still the same, update the notification:
343a907614755847b2630561a1e5949b2b416600d97Sungsoo Lim                    Log.d(TAG, "fetchBitmapFromURLAsync: set bitmap to " + artUrl);
344a907614755847b2630561a1e5949b2b416600d97Sungsoo Lim                    builder.setLargeIcon(bitmap);
345a907614755847b2630561a1e5949b2b416600d97Sungsoo Lim                    mNotificationManager.notify(NOTIFICATION_ID, builder.build());
346a907614755847b2630561a1e5949b2b416600d97Sungsoo Lim                }
347a907614755847b2630561a1e5949b2b416600d97Sungsoo Lim            }
348a907614755847b2630561a1e5949b2b416600d97Sungsoo Lim        });
349a907614755847b2630561a1e5949b2b416600d97Sungsoo Lim    }
350a907614755847b2630561a1e5949b2b416600d97Sungsoo Lim}
351