blob: a59d590c971912d2ba5b591561607c04b3ff4cd1 [file] [log] [blame]
Eliot Courtney09322282017-11-09 15:31:19 +09001/*
2 * Copyright (C) 2017 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License
15 */
Eliot Courtney3ebbccd2017-11-08 09:59:38 +090016package com.android.systemui.statusbar;
17
Jason Monk297c04e2018-08-23 17:16:59 -040018import static com.android.systemui.Dependency.MAIN_HANDLER;
19import static com.android.systemui.statusbar.StatusBarState.KEYGUARD;
20import static com.android.systemui.statusbar.phone.StatusBar.DEBUG_MEDIA_FAKE_ARTWORK;
21import static com.android.systemui.statusbar.phone.StatusBar.ENABLE_LOCKSCREEN_WALLPAPER;
22import static com.android.systemui.statusbar.phone.StatusBar.SHOW_LOCKSCREEN_MEDIA_ARTWORK;
23
Robert Snoebergerfb1e9262019-05-01 11:39:09 -040024import android.annotation.MainThread;
Jason Monk297c04e2018-08-23 17:16:59 -040025import android.annotation.Nullable;
Eliot Courtney3ebbccd2017-11-08 09:59:38 +090026import android.app.Notification;
27import android.content.Context;
Jason Monk297c04e2018-08-23 17:16:59 -040028import android.graphics.Bitmap;
Jason Monk297c04e2018-08-23 17:16:59 -040029import android.graphics.drawable.BitmapDrawable;
30import android.graphics.drawable.ColorDrawable;
31import android.graphics.drawable.Drawable;
Lucas Dupin6a03a9f2018-12-20 17:13:52 -080032import android.graphics.drawable.Icon;
Eliot Courtney3ebbccd2017-11-08 09:59:38 +090033import android.media.MediaMetadata;
34import android.media.session.MediaController;
35import android.media.session.MediaSession;
36import android.media.session.MediaSessionManager;
37import android.media.session.PlaybackState;
Robert Snoebergerfb1e9262019-05-01 11:39:09 -040038import android.os.AsyncTask;
Jason Monk297c04e2018-08-23 17:16:59 -040039import android.os.Handler;
40import android.os.Trace;
Eliot Courtney3ebbccd2017-11-08 09:59:38 +090041import android.os.UserHandle;
Beth Thibodeaueab4dde2019-02-07 11:37:02 -050042import android.provider.DeviceConfig;
Matt Pape15769e22019-04-19 12:31:24 -070043import android.provider.DeviceConfig.Properties;
Robert Snoebergerfb1e9262019-05-01 11:39:09 -040044import android.util.ArraySet;
Eliot Courtney3ebbccd2017-11-08 09:59:38 +090045import android.util.Log;
Jason Monk297c04e2018-08-23 17:16:59 -040046import android.view.View;
47import android.widget.ImageView;
Eliot Courtney3ebbccd2017-11-08 09:59:38 +090048
Beth Thibodeaueab4dde2019-02-07 11:37:02 -050049import com.android.internal.config.sysui.SystemUiDeviceConfigFlags;
Gus Prevasca1b6f72018-12-28 10:53:11 -050050import com.android.internal.statusbar.NotificationVisibility;
Jason Monk297c04e2018-08-23 17:16:59 -040051import com.android.systemui.Dependency;
Eliot Courtney3ebbccd2017-11-08 09:59:38 +090052import com.android.systemui.Dumpable;
Jason Monk297c04e2018-08-23 17:16:59 -040053import com.android.systemui.Interpolators;
54import com.android.systemui.colorextraction.SysuiColorExtractor;
Beverly8fdb5332019-02-04 14:29:49 -050055import com.android.systemui.plugins.statusbar.StatusBarStateController;
Gus Prevas2d5a1e92018-12-21 15:36:06 -050056import com.android.systemui.statusbar.notification.NotificationEntryListener;
Rohan Shah20790b82018-07-02 17:21:04 -070057import com.android.systemui.statusbar.notification.NotificationEntryManager;
Ned Burnsf81c4c42019-01-07 14:10:43 -050058import com.android.systemui.statusbar.notification.collection.NotificationEntry;
Jason Monk297c04e2018-08-23 17:16:59 -040059import com.android.systemui.statusbar.phone.BiometricUnlockController;
Lucas Dupinab88bda2019-06-12 15:52:31 -070060import com.android.systemui.statusbar.phone.KeyguardBypassController;
Jason Monk297c04e2018-08-23 17:16:59 -040061import com.android.systemui.statusbar.phone.LockscreenWallpaper;
62import com.android.systemui.statusbar.phone.ScrimController;
63import com.android.systemui.statusbar.phone.ScrimState;
64import com.android.systemui.statusbar.phone.ShadeController;
65import com.android.systemui.statusbar.phone.StatusBarWindowController;
66import com.android.systemui.statusbar.policy.KeyguardMonitor;
Eliot Courtney3ebbccd2017-11-08 09:59:38 +090067
68import java.io.FileDescriptor;
69import java.io.PrintWriter;
Robert Snoebergerfb1e9262019-05-01 11:39:09 -040070import java.lang.ref.WeakReference;
Eliot Courtney3ebbccd2017-11-08 09:59:38 +090071import java.util.ArrayList;
72import java.util.List;
Robert Snoebergerfb1e9262019-05-01 11:39:09 -040073import java.util.Set;
Eliot Courtney3ebbccd2017-11-08 09:59:38 +090074
Jason Monk27d01a622018-12-10 15:57:09 -050075import javax.inject.Inject;
76import javax.inject.Singleton;
77
Gus Prevas2d5a1e92018-12-21 15:36:06 -050078import dagger.Lazy;
79
Eliot Courtney3ebbccd2017-11-08 09:59:38 +090080/**
81 * Handles tasks and state related to media notifications. For example, there is a 'current' media
82 * notification, which this class keeps track of.
83 */
Jason Monk27d01a622018-12-10 15:57:09 -050084@Singleton
Eliot Courtney3ebbccd2017-11-08 09:59:38 +090085public class NotificationMediaManager implements Dumpable {
86 private static final String TAG = "NotificationMediaManager";
87 public static final boolean DEBUG_MEDIA = false;
88
Jason Monk297c04e2018-08-23 17:16:59 -040089 private final StatusBarStateController mStatusBarStateController
90 = Dependency.get(StatusBarStateController.class);
91 private final SysuiColorExtractor mColorExtractor = Dependency.get(SysuiColorExtractor.class);
92 private final KeyguardMonitor mKeyguardMonitor = Dependency.get(KeyguardMonitor.class);
Lucas Dupinab88bda2019-06-12 15:52:31 -070093 private final KeyguardBypassController mKeyguardBypassController;
Jason Monk297c04e2018-08-23 17:16:59 -040094
95 // Late binding
96 private NotificationEntryManager mEntryManager;
97
98 // Late binding, also @Nullable due to being in com.android.systemui.statusbar.phone package
99 @Nullable
Gus Prevas2d5a1e92018-12-21 15:36:06 -0500100 private Lazy<ShadeController> mShadeController;
Jason Monk297c04e2018-08-23 17:16:59 -0400101 @Nullable
Gus Prevas2d5a1e92018-12-21 15:36:06 -0500102 private Lazy<StatusBarWindowController> mStatusBarWindowController;
Jason Monk297c04e2018-08-23 17:16:59 -0400103
104 @Nullable
105 private BiometricUnlockController mBiometricUnlockController;
106 @Nullable
107 private ScrimController mScrimController;
108 @Nullable
109 private LockscreenWallpaper mLockscreenWallpaper;
110
Jason Monk297c04e2018-08-23 17:16:59 -0400111 private final Handler mHandler = Dependency.get(MAIN_HANDLER);
112
Eliot Courtney3ebbccd2017-11-08 09:59:38 +0900113 private final Context mContext;
114 private final MediaSessionManager mMediaSessionManager;
Lucas Dupin6a03a9f2018-12-20 17:13:52 -0800115 private final ArrayList<MediaListener> mMediaListeners;
Lucas Dupinba57b4a2019-01-21 14:55:57 -0800116 private final MediaArtworkProcessor mMediaArtworkProcessor;
Robert Snoebergerfb1e9262019-05-01 11:39:09 -0400117 private final Set<AsyncTask<?, ?, ?>> mProcessArtworkTasks = new ArraySet<>();
Eliot Courtneya6d8cf22017-10-20 13:26:58 +0900118
Eliot Courtney4a96b362017-12-14 19:38:52 +0900119 protected NotificationPresenter mPresenter;
Eliot Courtney3ebbccd2017-11-08 09:59:38 +0900120 private MediaController mMediaController;
121 private String mMediaNotificationKey;
122 private MediaMetadata mMediaMetadata;
123
Jason Monk297c04e2018-08-23 17:16:59 -0400124 private BackDropView mBackdrop;
125 private ImageView mBackdropFront;
126 private ImageView mBackdropBack;
127
Beth Thibodeaueab4dde2019-02-07 11:37:02 -0500128 private boolean mShowCompactMediaSeekbar;
Matt Pape15769e22019-04-19 12:31:24 -0700129 private final DeviceConfig.OnPropertiesChangedListener mPropertiesChangedListener =
130 new DeviceConfig.OnPropertiesChangedListener() {
Beth Thibodeaueab4dde2019-02-07 11:37:02 -0500131 @Override
Matt Pape15769e22019-04-19 12:31:24 -0700132 public void onPropertiesChanged(Properties properties) {
133 for (String name : properties.getKeyset()) {
134 if (SystemUiDeviceConfigFlags.COMPACT_MEDIA_SEEKBAR_ENABLED.equals(name)) {
135 String value = properties.getString(name, null);
136 if (DEBUG_MEDIA) {
137 Log.v(TAG, "DEBUG_MEDIA: compact media seekbar flag updated: " + value);
138 }
139 mShowCompactMediaSeekbar = "true".equals(value);
Beth Thibodeaueab4dde2019-02-07 11:37:02 -0500140 }
Beth Thibodeaueab4dde2019-02-07 11:37:02 -0500141 }
142 }
143 };
144
Eliot Courtney3ebbccd2017-11-08 09:59:38 +0900145 private final MediaController.Callback mMediaListener = new MediaController.Callback() {
146 @Override
147 public void onPlaybackStateChanged(PlaybackState state) {
148 super.onPlaybackStateChanged(state);
149 if (DEBUG_MEDIA) {
150 Log.v(TAG, "DEBUG_MEDIA: onPlaybackStateChanged: " + state);
151 }
152 if (state != null) {
153 if (!isPlaybackActive(state.getState())) {
154 clearCurrentMediaNotification();
Eliot Courtney3ebbccd2017-11-08 09:59:38 +0900155 }
Lucas Dupin6b40d5e2019-05-15 19:47:11 -0700156 dispatchUpdateMediaMetaData(true /* changed */, true /* allowAnimation */);
Eliot Courtney3ebbccd2017-11-08 09:59:38 +0900157 }
158 }
159
160 @Override
161 public void onMetadataChanged(MediaMetadata metadata) {
162 super.onMetadataChanged(metadata);
163 if (DEBUG_MEDIA) {
164 Log.v(TAG, "DEBUG_MEDIA: onMetadataChanged: " + metadata);
165 }
Lucas Dupinba57b4a2019-01-21 14:55:57 -0800166 mMediaArtworkProcessor.clearCache();
Eliot Courtney3ebbccd2017-11-08 09:59:38 +0900167 mMediaMetadata = metadata;
Lucas Dupin6a03a9f2018-12-20 17:13:52 -0800168 dispatchUpdateMediaMetaData(true /* changed */, true /* allowAnimation */);
Eliot Courtney3ebbccd2017-11-08 09:59:38 +0900169 }
170 };
171
Jason Monk27d01a622018-12-10 15:57:09 -0500172 @Inject
Gus Prevas2d5a1e92018-12-21 15:36:06 -0500173 public NotificationMediaManager(
174 Context context,
175 Lazy<ShadeController> shadeController,
176 Lazy<StatusBarWindowController> statusBarWindowController,
Lucas Dupinba57b4a2019-01-21 14:55:57 -0800177 NotificationEntryManager notificationEntryManager,
Lucas Dupinab88bda2019-06-12 15:52:31 -0700178 MediaArtworkProcessor mediaArtworkProcessor,
179 KeyguardBypassController keyguardBypassController) {
Eliot Courtney3ebbccd2017-11-08 09:59:38 +0900180 mContext = context;
Lucas Dupinba57b4a2019-01-21 14:55:57 -0800181 mMediaArtworkProcessor = mediaArtworkProcessor;
Lucas Dupinab88bda2019-06-12 15:52:31 -0700182 mKeyguardBypassController = keyguardBypassController;
Lucas Dupin6a03a9f2018-12-20 17:13:52 -0800183 mMediaListeners = new ArrayList<>();
Eliot Courtney3ebbccd2017-11-08 09:59:38 +0900184 mMediaSessionManager
185 = (MediaSessionManager) mContext.getSystemService(Context.MEDIA_SESSION_SERVICE);
186 // TODO: use MediaSessionManager.SessionListener to hook us up to future updates
187 // in session state
Gus Prevas2d5a1e92018-12-21 15:36:06 -0500188 mShadeController = shadeController;
189 mStatusBarWindowController = statusBarWindowController;
190 mEntryManager = notificationEntryManager;
191 notificationEntryManager.addNotificationEntryListener(new NotificationEntryListener() {
192 @Override
Gus Prevas772e5322018-12-21 16:22:16 -0500193 public void onEntryRemoved(
Ned Burnsf81c4c42019-01-07 14:10:43 -0500194 NotificationEntry entry,
Gus Prevasca1b6f72018-12-28 10:53:11 -0500195 NotificationVisibility visibility,
Gus Prevas772e5322018-12-21 16:22:16 -0500196 boolean removedByUser) {
Ned Burnsef2ef6c2019-01-02 16:48:08 -0500197 onNotificationRemoved(entry.key);
Gus Prevas2d5a1e92018-12-21 15:36:06 -0500198 }
199 });
Beth Thibodeaueab4dde2019-02-07 11:37:02 -0500200
201 mShowCompactMediaSeekbar = "true".equals(
202 DeviceConfig.getProperty(DeviceConfig.NAMESPACE_SYSTEMUI,
203 SystemUiDeviceConfigFlags.COMPACT_MEDIA_SEEKBAR_ENABLED));
204
Matt Pape15769e22019-04-19 12:31:24 -0700205 DeviceConfig.addOnPropertiesChangedListener(DeviceConfig.NAMESPACE_SYSTEMUI,
Beth Thibodeaueab4dde2019-02-07 11:37:02 -0500206 mContext.getMainExecutor(),
Matt Pape15769e22019-04-19 12:31:24 -0700207 mPropertiesChangedListener);
Eliot Courtney3ebbccd2017-11-08 09:59:38 +0900208 }
209
Jason Monk297c04e2018-08-23 17:16:59 -0400210 public void setUpWithPresenter(NotificationPresenter presenter) {
Eliot Courtneya6d8cf22017-10-20 13:26:58 +0900211 mPresenter = presenter;
212 }
213
Eliot Courtney3ebbccd2017-11-08 09:59:38 +0900214 public void onNotificationRemoved(String key) {
215 if (key.equals(mMediaNotificationKey)) {
216 clearCurrentMediaNotification();
Lucas Dupin6a03a9f2018-12-20 17:13:52 -0800217 dispatchUpdateMediaMetaData(true /* changed */, true /* allowEnterAnimation */);
Eliot Courtney3ebbccd2017-11-08 09:59:38 +0900218 }
219 }
220
221 public String getMediaNotificationKey() {
222 return mMediaNotificationKey;
223 }
224
225 public MediaMetadata getMediaMetadata() {
226 return mMediaMetadata;
227 }
228
Beth Thibodeaueab4dde2019-02-07 11:37:02 -0500229 public boolean getShowCompactMediaSeekbar() {
230 return mShowCompactMediaSeekbar;
231 }
232
Lucas Dupin6a03a9f2018-12-20 17:13:52 -0800233 public Icon getMediaIcon() {
234 if (mMediaNotificationKey == null) {
235 return null;
236 }
Gus Prevas2d5a1e92018-12-21 15:36:06 -0500237 synchronized (mEntryManager.getNotificationData()) {
Ned Burnsf81c4c42019-01-07 14:10:43 -0500238 NotificationEntry entry = mEntryManager.getNotificationData().get(mMediaNotificationKey);
Lucas Dupin6a03a9f2018-12-20 17:13:52 -0800239 if (entry == null || entry.expandedIcon == null) {
240 return null;
241 }
242
243 return entry.expandedIcon.getSourceIcon();
244 }
245 }
246
247 public void addCallback(MediaListener callback) {
248 mMediaListeners.add(callback);
Lucas Dupin6b40d5e2019-05-15 19:47:11 -0700249 callback.onMetadataOrStateChanged(mMediaMetadata,
250 getMediaControllerPlaybackState(mMediaController));
Lucas Dupin6a03a9f2018-12-20 17:13:52 -0800251 }
252
253 public void removeCallback(MediaListener callback) {
254 mMediaListeners.remove(callback);
255 }
256
Eliot Courtney3ebbccd2017-11-08 09:59:38 +0900257 public void findAndUpdateMediaNotifications() {
258 boolean metaDataChanged = false;
259
Gus Prevas2d5a1e92018-12-21 15:36:06 -0500260 synchronized (mEntryManager.getNotificationData()) {
Ned Burnsf81c4c42019-01-07 14:10:43 -0500261 ArrayList<NotificationEntry> activeNotifications =
Gus Prevas2d5a1e92018-12-21 15:36:06 -0500262 mEntryManager.getNotificationData().getActiveNotifications();
Eliot Courtney3ebbccd2017-11-08 09:59:38 +0900263 final int N = activeNotifications.size();
264
265 // Promote the media notification with a controller in 'playing' state, if any.
Ned Burnsf81c4c42019-01-07 14:10:43 -0500266 NotificationEntry mediaNotification = null;
Eliot Courtney3ebbccd2017-11-08 09:59:38 +0900267 MediaController controller = null;
268 for (int i = 0; i < N; i++) {
Ned Burnsf81c4c42019-01-07 14:10:43 -0500269 final NotificationEntry entry = activeNotifications.get(i);
Eliot Courtney3ebbccd2017-11-08 09:59:38 +0900270
Evan Laird94492852018-10-25 13:43:01 -0400271 if (entry.isMediaNotification()) {
Eliot Courtney3ebbccd2017-11-08 09:59:38 +0900272 final MediaSession.Token token =
273 entry.notification.getNotification().extras.getParcelable(
274 Notification.EXTRA_MEDIA_SESSION);
275 if (token != null) {
276 MediaController aController = new MediaController(mContext, token);
277 if (PlaybackState.STATE_PLAYING ==
278 getMediaControllerPlaybackState(aController)) {
279 if (DEBUG_MEDIA) {
280 Log.v(TAG, "DEBUG_MEDIA: found mediastyle controller matching "
281 + entry.notification.getKey());
282 }
283 mediaNotification = entry;
284 controller = aController;
285 break;
286 }
287 }
288 }
289 }
290 if (mediaNotification == null) {
291 // Still nothing? OK, let's just look for live media sessions and see if they match
292 // one of our notifications. This will catch apps that aren't (yet!) using media
293 // notifications.
294
295 if (mMediaSessionManager != null) {
296 // TODO: Should this really be for all users?
297 final List<MediaController> sessions
298 = mMediaSessionManager.getActiveSessionsForUser(
299 null,
300 UserHandle.USER_ALL);
301
302 for (MediaController aController : sessions) {
303 if (PlaybackState.STATE_PLAYING ==
304 getMediaControllerPlaybackState(aController)) {
305 // now to see if we have one like this
306 final String pkg = aController.getPackageName();
307
308 for (int i = 0; i < N; i++) {
Ned Burnsf81c4c42019-01-07 14:10:43 -0500309 final NotificationEntry entry = activeNotifications.get(i);
Eliot Courtney3ebbccd2017-11-08 09:59:38 +0900310 if (entry.notification.getPackageName().equals(pkg)) {
311 if (DEBUG_MEDIA) {
312 Log.v(TAG, "DEBUG_MEDIA: found controller matching "
313 + entry.notification.getKey());
314 }
315 controller = aController;
316 mediaNotification = entry;
317 break;
318 }
319 }
320 }
321 }
322 }
323 }
324
325 if (controller != null && !sameSessions(mMediaController, controller)) {
326 // We have a new media session
shawnlin87eae9b2018-04-20 21:42:42 +0800327 clearCurrentMediaNotificationSession();
Eliot Courtney3ebbccd2017-11-08 09:59:38 +0900328 mMediaController = controller;
329 mMediaController.registerCallback(mMediaListener);
330 mMediaMetadata = mMediaController.getMetadata();
331 if (DEBUG_MEDIA) {
shawnlin87eae9b2018-04-20 21:42:42 +0800332 Log.v(TAG, "DEBUG_MEDIA: insert listener, found new controller: "
333 + mMediaController + ", receive metadata: " + mMediaMetadata);
Eliot Courtney3ebbccd2017-11-08 09:59:38 +0900334 }
335
Eliot Courtney3ebbccd2017-11-08 09:59:38 +0900336 metaDataChanged = true;
337 }
shawnlin87eae9b2018-04-20 21:42:42 +0800338
339 if (mediaNotification != null
340 && !mediaNotification.notification.getKey().equals(mMediaNotificationKey)) {
341 mMediaNotificationKey = mediaNotification.notification.getKey();
342 if (DEBUG_MEDIA) {
343 Log.v(TAG, "DEBUG_MEDIA: Found new media notification: key="
344 + mMediaNotificationKey);
345 }
346 }
Eliot Courtney3ebbccd2017-11-08 09:59:38 +0900347 }
348
349 if (metaDataChanged) {
Gus Prevas2d5a1e92018-12-21 15:36:06 -0500350 mEntryManager.updateNotifications();
Eliot Courtney3ebbccd2017-11-08 09:59:38 +0900351 }
Evan Lairdf0348b12018-11-12 15:26:34 -0500352
Lucas Dupin6a03a9f2018-12-20 17:13:52 -0800353 dispatchUpdateMediaMetaData(metaDataChanged, true /* allowEnterAnimation */);
Eliot Courtney3ebbccd2017-11-08 09:59:38 +0900354 }
355
356 public void clearCurrentMediaNotification() {
357 mMediaNotificationKey = null;
shawnlin87eae9b2018-04-20 21:42:42 +0800358 clearCurrentMediaNotificationSession();
Eliot Courtney3ebbccd2017-11-08 09:59:38 +0900359 }
360
Lucas Dupin6a03a9f2018-12-20 17:13:52 -0800361 private void dispatchUpdateMediaMetaData(boolean changed, boolean allowEnterAnimation) {
362 if (mPresenter != null) {
363 mPresenter.updateMediaMetaData(changed, allowEnterAnimation);
364 }
Lucas Dupin6b40d5e2019-05-15 19:47:11 -0700365 @PlaybackState.State int state = getMediaControllerPlaybackState(mMediaController);
Lucas Dupin6a03a9f2018-12-20 17:13:52 -0800366 ArrayList<MediaListener> callbacks = new ArrayList<>(mMediaListeners);
367 for (int i = 0; i < callbacks.size(); i++) {
Lucas Dupin6b40d5e2019-05-15 19:47:11 -0700368 callbacks.get(i).onMetadataOrStateChanged(mMediaMetadata, state);
Lucas Dupin6a03a9f2018-12-20 17:13:52 -0800369 }
370 }
371
Eliot Courtney3ebbccd2017-11-08 09:59:38 +0900372 @Override
373 public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
374 pw.print(" mMediaSessionManager=");
375 pw.println(mMediaSessionManager);
376 pw.print(" mMediaNotificationKey=");
377 pw.println(mMediaNotificationKey);
378 pw.print(" mMediaController=");
379 pw.print(mMediaController);
380 if (mMediaController != null) {
381 pw.print(" state=" + mMediaController.getPlaybackState());
382 }
383 pw.println();
384 pw.print(" mMediaMetadata=");
385 pw.print(mMediaMetadata);
386 if (mMediaMetadata != null) {
387 pw.print(" title=" + mMediaMetadata.getText(MediaMetadata.METADATA_KEY_TITLE));
388 }
389 pw.println();
390 }
391
392 private boolean isPlaybackActive(int state) {
393 return state != PlaybackState.STATE_STOPPED && state != PlaybackState.STATE_ERROR
394 && state != PlaybackState.STATE_NONE;
395 }
396
397 private boolean sameSessions(MediaController a, MediaController b) {
398 if (a == b) {
399 return true;
400 }
401 if (a == null) {
402 return false;
403 }
404 return a.controlsSameSession(b);
405 }
406
407 private int getMediaControllerPlaybackState(MediaController controller) {
408 if (controller != null) {
409 final PlaybackState playbackState = controller.getPlaybackState();
410 if (playbackState != null) {
411 return playbackState.getState();
412 }
413 }
414 return PlaybackState.STATE_NONE;
415 }
416
shawnlin87eae9b2018-04-20 21:42:42 +0800417 private void clearCurrentMediaNotificationSession() {
Lucas Dupinba57b4a2019-01-21 14:55:57 -0800418 mMediaArtworkProcessor.clearCache();
shawnlin87eae9b2018-04-20 21:42:42 +0800419 mMediaMetadata = null;
420 if (mMediaController != null) {
421 if (DEBUG_MEDIA) {
422 Log.v(TAG, "DEBUG_MEDIA: Disconnecting from old controller: "
423 + mMediaController.getPackageName());
424 }
425 mMediaController.unregisterCallback(mMediaListener);
426 }
427 mMediaController = null;
428 }
Jason Monk297c04e2018-08-23 17:16:59 -0400429
430 /**
431 * Refresh or remove lockscreen artwork from media metadata or the lockscreen wallpaper.
432 */
433 public void updateMediaMetaData(boolean metaDataChanged, boolean allowEnterAnimation) {
434 Trace.beginSection("StatusBar#updateMediaMetaData");
435 if (!SHOW_LOCKSCREEN_MEDIA_ARTWORK) {
436 Trace.endSection();
437 return;
438 }
439
440 if (mBackdrop == null) {
441 Trace.endSection();
442 return; // called too early
443 }
444
445 boolean wakeAndUnlock = mBiometricUnlockController != null
446 && mBiometricUnlockController.isWakeAndUnlock();
447 if (mKeyguardMonitor.isLaunchTransitionFadingAway() || wakeAndUnlock) {
448 mBackdrop.setVisibility(View.INVISIBLE);
449 Trace.endSection();
450 return;
451 }
452
453 MediaMetadata mediaMetadata = getMediaMetadata();
454
455 if (DEBUG_MEDIA) {
456 Log.v(TAG, "DEBUG_MEDIA: updating album art for notification "
457 + getMediaNotificationKey()
458 + " metadata=" + mediaMetadata
459 + " metaDataChanged=" + metaDataChanged
460 + " state=" + mStatusBarStateController.getState());
461 }
462
Robert Snoebergerfb1e9262019-05-01 11:39:09 -0400463 Bitmap artworkBitmap = null;
Lucas Dupinab88bda2019-06-12 15:52:31 -0700464 if (mediaMetadata != null && !mKeyguardBypassController.getBypassEnabled()) {
Robert Snoebergerfb1e9262019-05-01 11:39:09 -0400465 artworkBitmap = mediaMetadata.getBitmap(MediaMetadata.METADATA_KEY_ART);
Jason Monk297c04e2018-08-23 17:16:59 -0400466 if (artworkBitmap == null) {
467 artworkBitmap = mediaMetadata.getBitmap(MediaMetadata.METADATA_KEY_ALBUM_ART);
Jason Monk297c04e2018-08-23 17:16:59 -0400468 }
Robert Snoebergerfb1e9262019-05-01 11:39:09 -0400469 }
470
471 // Process artwork on a background thread and send the resulting bitmap to
472 // finishUpdateMediaMetaData.
473 if (metaDataChanged) {
474 for (AsyncTask<?, ?, ?> task : mProcessArtworkTasks) {
475 task.cancel(true);
Jason Monk297c04e2018-08-23 17:16:59 -0400476 }
Robert Snoebergerfb1e9262019-05-01 11:39:09 -0400477 mProcessArtworkTasks.clear();
478 }
479 if (artworkBitmap != null) {
480 mProcessArtworkTasks.add(new ProcessArtworkTask(this, metaDataChanged,
481 allowEnterAnimation).execute(artworkBitmap));
482 } else {
483 finishUpdateMediaMetaData(metaDataChanged, allowEnterAnimation, null);
484 }
485
486 Trace.endSection();
487 }
488
489 private void finishUpdateMediaMetaData(boolean metaDataChanged, boolean allowEnterAnimation,
490 @Nullable Bitmap bmp) {
491 Drawable artworkDrawable = null;
492 if (bmp != null) {
493 artworkDrawable = new BitmapDrawable(mBackdropBack.getResources(), bmp);
Jason Monk297c04e2018-08-23 17:16:59 -0400494 }
Lucas Dupin89233c42019-06-10 18:00:22 -0700495 boolean hasMediaArtwork = artworkDrawable != null;
Jason Monk297c04e2018-08-23 17:16:59 -0400496 boolean allowWhenShade = false;
497 if (ENABLE_LOCKSCREEN_WALLPAPER && artworkDrawable == null) {
498 Bitmap lockWallpaper =
499 mLockscreenWallpaper != null ? mLockscreenWallpaper.getBitmap() : null;
500 if (lockWallpaper != null) {
501 artworkDrawable = new LockscreenWallpaper.WallpaperDrawable(
502 mBackdropBack.getResources(), lockWallpaper);
503 // We're in the SHADE mode on the SIM screen - yet we still need to show
504 // the lockscreen wallpaper in that mode.
505 allowWhenShade = mStatusBarStateController.getState() == KEYGUARD;
506 }
507 }
508
Gus Prevas2d5a1e92018-12-21 15:36:06 -0500509 ShadeController shadeController = mShadeController.get();
510 StatusBarWindowController windowController = mStatusBarWindowController.get();
511 boolean hideBecauseOccluded = shadeController != null && shadeController.isOccluded();
Jason Monk297c04e2018-08-23 17:16:59 -0400512
513 final boolean hasArtwork = artworkDrawable != null;
Lucas Dupin89233c42019-06-10 18:00:22 -0700514 mColorExtractor.setHasMediaArtwork(hasMediaArtwork);
Jason Monk297c04e2018-08-23 17:16:59 -0400515 if (mScrimController != null) {
516 mScrimController.setHasBackdrop(hasArtwork);
517 }
518
519 if ((hasArtwork || DEBUG_MEDIA_FAKE_ARTWORK)
520 && (mStatusBarStateController.getState() != StatusBarState.SHADE || allowWhenShade)
521 && mBiometricUnlockController != null && mBiometricUnlockController.getMode()
522 != BiometricUnlockController.MODE_WAKE_AND_UNLOCK_PULSING
523 && !hideBecauseOccluded) {
524 // time to show some art!
525 if (mBackdrop.getVisibility() != View.VISIBLE) {
526 mBackdrop.setVisibility(View.VISIBLE);
527 if (allowEnterAnimation) {
528 mBackdrop.setAlpha(0);
529 mBackdrop.animate().alpha(1f);
530 } else {
531 mBackdrop.animate().cancel();
532 mBackdrop.setAlpha(1f);
533 }
Gus Prevas2d5a1e92018-12-21 15:36:06 -0500534 if (windowController != null) {
535 windowController.setBackdropShowing(true);
Jason Monk297c04e2018-08-23 17:16:59 -0400536 }
537 metaDataChanged = true;
538 if (DEBUG_MEDIA) {
539 Log.v(TAG, "DEBUG_MEDIA: Fading in album artwork");
540 }
541 }
542 if (metaDataChanged) {
543 if (mBackdropBack.getDrawable() != null) {
544 Drawable drawable =
545 mBackdropBack.getDrawable().getConstantState()
546 .newDrawable(mBackdropFront.getResources()).mutate();
547 mBackdropFront.setImageDrawable(drawable);
548 mBackdropFront.setAlpha(1f);
549 mBackdropFront.setVisibility(View.VISIBLE);
550 } else {
551 mBackdropFront.setVisibility(View.INVISIBLE);
552 }
553
554 if (DEBUG_MEDIA_FAKE_ARTWORK) {
555 final int c = 0xFF000000 | (int)(Math.random() * 0xFFFFFF);
556 Log.v(TAG, String.format("DEBUG_MEDIA: setting new color: 0x%08x", c));
557 mBackdropBack.setBackgroundColor(0xFFFFFFFF);
558 mBackdropBack.setImageDrawable(new ColorDrawable(c));
559 } else {
560 mBackdropBack.setImageDrawable(artworkDrawable);
561 }
562
563 if (mBackdropFront.getVisibility() == View.VISIBLE) {
564 if (DEBUG_MEDIA) {
565 Log.v(TAG, "DEBUG_MEDIA: Crossfading album artwork from "
566 + mBackdropFront.getDrawable()
567 + " to "
568 + mBackdropBack.getDrawable());
569 }
570 mBackdropFront.animate()
571 .setDuration(250)
572 .alpha(0f).withEndAction(mHideBackdropFront);
573 }
574 }
575 } else {
576 // need to hide the album art, either because we are unlocked, on AOD
577 // or because the metadata isn't there to support it
578 if (mBackdrop.getVisibility() != View.GONE) {
579 if (DEBUG_MEDIA) {
580 Log.v(TAG, "DEBUG_MEDIA: Fading out album artwork");
581 }
Gus Prevas2d5a1e92018-12-21 15:36:06 -0500582 boolean cannotAnimateDoze = shadeController != null
583 && shadeController.isDozing()
Jason Monk297c04e2018-08-23 17:16:59 -0400584 && !ScrimState.AOD.getAnimateChange();
585 if (mBiometricUnlockController != null && mBiometricUnlockController.getMode()
586 == BiometricUnlockController.MODE_WAKE_AND_UNLOCK_PULSING
587 || hideBecauseOccluded || cannotAnimateDoze) {
588
589 // We are unlocking directly - no animation!
590 mBackdrop.setVisibility(View.GONE);
591 mBackdropBack.setImageDrawable(null);
Gus Prevas2d5a1e92018-12-21 15:36:06 -0500592 if (windowController != null) {
593 windowController.setBackdropShowing(false);
Jason Monk297c04e2018-08-23 17:16:59 -0400594 }
595 } else {
Gus Prevas2d5a1e92018-12-21 15:36:06 -0500596 if (windowController != null) {
597 windowController.setBackdropShowing(false);
Jason Monk297c04e2018-08-23 17:16:59 -0400598 }
599 mBackdrop.animate()
600 .alpha(0)
601 .setInterpolator(Interpolators.ACCELERATE_DECELERATE)
602 .setDuration(300)
603 .setStartDelay(0)
604 .withEndAction(() -> {
605 mBackdrop.setVisibility(View.GONE);
606 mBackdropFront.animate().cancel();
607 mBackdropBack.setImageDrawable(null);
608 mHandler.post(mHideBackdropFront);
609 });
610 if (mKeyguardMonitor.isKeyguardFadingAway()) {
611 mBackdrop.animate()
612 // Make it disappear faster, as the focus should be on the activity
613 // behind.
614 .setDuration(mKeyguardMonitor.getKeyguardFadingAwayDuration() / 2)
615 .setStartDelay(mKeyguardMonitor.getKeyguardFadingAwayDelay())
616 .setInterpolator(Interpolators.LINEAR)
617 .start();
618 }
619 }
620 }
621 }
Jason Monk297c04e2018-08-23 17:16:59 -0400622 }
623
624 public void setup(BackDropView backdrop, ImageView backdropFront, ImageView backdropBack,
Evan Laird0c29baf2018-10-24 18:17:58 -0400625 ScrimController scrimController, LockscreenWallpaper lockscreenWallpaper) {
Jason Monk297c04e2018-08-23 17:16:59 -0400626 mBackdrop = backdrop;
627 mBackdropFront = backdropFront;
628 mBackdropBack = backdropBack;
Jason Monk297c04e2018-08-23 17:16:59 -0400629 mScrimController = scrimController;
630 mLockscreenWallpaper = lockscreenWallpaper;
631 }
632
Evan Laird0c29baf2018-10-24 18:17:58 -0400633 public void setBiometricUnlockController(BiometricUnlockController biometricUnlockController) {
634 mBiometricUnlockController = biometricUnlockController;
635 }
636
Jason Monk297c04e2018-08-23 17:16:59 -0400637 /**
638 * Hide the album artwork that is fading out and release its bitmap.
639 */
640 protected final Runnable mHideBackdropFront = new Runnable() {
641 @Override
642 public void run() {
643 if (DEBUG_MEDIA) {
644 Log.v(TAG, "DEBUG_MEDIA: removing fade layer");
645 }
646 mBackdropFront.setVisibility(View.INVISIBLE);
647 mBackdropFront.animate().cancel();
648 mBackdropFront.setImageDrawable(null);
649 }
650 };
Lucas Dupin6a03a9f2018-12-20 17:13:52 -0800651
Robert Snoebergerfb1e9262019-05-01 11:39:09 -0400652 private Bitmap processArtwork(Bitmap artwork) {
653 return mMediaArtworkProcessor.processArtwork(mContext, artwork);
654 }
655
656 @MainThread
657 private void removeTask(AsyncTask<?, ?, ?> task) {
658 mProcessArtworkTasks.remove(task);
659 }
660
661 /**
662 * {@link AsyncTask} to prepare album art for use as backdrop on lock screen.
663 */
664 private static final class ProcessArtworkTask extends AsyncTask<Bitmap, Void, Bitmap> {
665
666 private final WeakReference<NotificationMediaManager> mManagerRef;
667 private final boolean mMetaDataChanged;
668 private final boolean mAllowEnterAnimation;
669
670 ProcessArtworkTask(NotificationMediaManager manager, boolean changed,
671 boolean allowAnimation) {
672 mManagerRef = new WeakReference<>(manager);
673 mMetaDataChanged = changed;
674 mAllowEnterAnimation = allowAnimation;
675 }
676
677 @Override
678 protected Bitmap doInBackground(Bitmap... bitmaps) {
679 NotificationMediaManager manager = mManagerRef.get();
680 if (manager == null || bitmaps.length == 0 || isCancelled()) {
681 return null;
682 }
683 return manager.processArtwork(bitmaps[0]);
684 }
685
686 @Override
687 protected void onPostExecute(@Nullable Bitmap result) {
688 NotificationMediaManager manager = mManagerRef.get();
689 if (manager != null && !isCancelled()) {
690 manager.removeTask(this);
691 manager.finishUpdateMediaMetaData(mMetaDataChanged, mAllowEnterAnimation, result);
692 }
693 }
694
695 @Override
696 protected void onCancelled(Bitmap result) {
697 if (result != null) {
698 result.recycle();
699 }
700 NotificationMediaManager manager = mManagerRef.get();
701 if (manager != null) {
702 manager.removeTask(this);
703 }
704 }
705 }
706
Lucas Dupin6a03a9f2018-12-20 17:13:52 -0800707 public interface MediaListener {
Lucas Dupin6b40d5e2019-05-15 19:47:11 -0700708 /**
709 * Called whenever there's new metadata or playback state.
710 * @param metadata Current metadata.
711 * @param state Current playback state
712 * @see PlaybackState.State
713 */
714 void onMetadataOrStateChanged(MediaMetadata metadata, @PlaybackState.State int state);
Lucas Dupin6a03a9f2018-12-20 17:13:52 -0800715 }
Eliot Courtney3ebbccd2017-11-08 09:59:38 +0900716}