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