diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationMediaManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationMediaManager.java index 8bf134d9c5b91..5bf1d9d2da1d2 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationMediaManager.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationMediaManager.java @@ -40,6 +40,7 @@ import android.os.Trace; import android.os.UserHandle; import android.provider.DeviceConfig; import android.provider.DeviceConfig.Properties; +import android.service.notification.StatusBarNotification; import android.util.ArraySet; import android.util.Log; import android.view.View; @@ -57,6 +58,7 @@ import com.android.systemui.plugins.statusbar.StatusBarStateController; import com.android.systemui.statusbar.dagger.StatusBarModule; import com.android.systemui.statusbar.notification.NotificationEntryListener; import com.android.systemui.statusbar.notification.NotificationEntryManager; +import com.android.systemui.statusbar.notification.collection.NotifPipeline; import com.android.systemui.statusbar.notification.collection.NotificationEntry; import com.android.systemui.statusbar.notification.collection.notifcollection.NotifCollectionListener; import com.android.systemui.statusbar.phone.BiometricUnlockController; @@ -77,6 +79,7 @@ import java.util.ArrayList; import java.util.Collection; import java.util.HashSet; import java.util.List; +import java.util.Objects; import java.util.Set; import dagger.Lazy; @@ -106,6 +109,8 @@ public class NotificationMediaManager implements Dumpable { private final NotificationEntryManager mEntryManager; private final MediaDataManager mMediaDataManager; + private final NotifPipeline mNotifPipeline; + private final boolean mUsingNotifPipeline; @Nullable private Lazy mNotificationShadeWindowController; @@ -189,6 +194,8 @@ public class NotificationMediaManager implements Dumpable { NotificationEntryManager notificationEntryManager, MediaArtworkProcessor mediaArtworkProcessor, KeyguardBypassController keyguardBypassController, + NotifPipeline notifPipeline, + FeatureFlags featureFlags, @Main DelayableExecutor mainExecutor, DeviceConfigProxy deviceConfig, MediaDataManager mediaDataManager) { @@ -206,17 +213,65 @@ public class NotificationMediaManager implements Dumpable { mEntryManager = notificationEntryManager; mMainExecutor = mainExecutor; mMediaDataManager = mediaDataManager; + mNotifPipeline = notifPipeline; - notificationEntryManager.addNotificationEntryListener(new NotificationEntryListener() { + if (!featureFlags.isNewNotifPipelineRenderingEnabled()) { + setupNEM(); + mUsingNotifPipeline = false; + } else { + setupNotifPipeline(); + mUsingNotifPipeline = true; + } + + mShowCompactMediaSeekbar = "true".equals( + DeviceConfig.getProperty(DeviceConfig.NAMESPACE_SYSTEMUI, + SystemUiDeviceConfigFlags.COMPACT_MEDIA_SEEKBAR_ENABLED)); + + deviceConfig.addOnPropertiesChangedListener(DeviceConfig.NAMESPACE_SYSTEMUI, + mContext.getMainExecutor(), + mPropertiesChangedListener); + } + + private void setupNotifPipeline() { + mNotifPipeline.addCollectionListener(new NotifCollectionListener() { + @Override + public void onEntryAdded(@NonNull NotificationEntry entry) { + mMediaDataManager.onNotificationAdded(entry.getKey(), entry.getSbn()); + } + + @Override + public void onEntryUpdated(NotificationEntry entry) { + mMediaDataManager.onNotificationAdded(entry.getKey(), entry.getSbn()); + } + + @Override + public void onEntryBind(NotificationEntry entry, StatusBarNotification sbn) { + findAndUpdateMediaNotifications(); + } + + @Override + public void onEntryRemoved(@NonNull NotificationEntry entry, int reason) { + removeEntry(entry); + } + + @Override + public void onEntryCleanUp(@NonNull NotificationEntry entry) { + removeEntry(entry); + } + }); + } + + private void setupNEM() { + mEntryManager.addNotificationEntryListener(new NotificationEntryListener() { @Override public void onPendingEntryAdded(NotificationEntry entry) { - mediaDataManager.onNotificationAdded(entry.getKey(), entry.getSbn()); + mMediaDataManager.onNotificationAdded(entry.getKey(), entry.getSbn()); } @Override public void onPreEntryUpdated(NotificationEntry entry) { - mediaDataManager.onNotificationAdded(entry.getKey(), entry.getSbn()); + mMediaDataManager.onNotificationAdded(entry.getKey(), entry.getSbn()); } @Override @@ -231,8 +286,8 @@ public class NotificationMediaManager implements Dumpable { @Override public void onEntryRemoved( - NotificationEntry entry, - NotificationVisibility visibility, + @NonNull NotificationEntry entry, + @Nullable NotificationVisibility visibility, boolean removedByUser, int reason) { removeEntry(entry); @@ -242,20 +297,12 @@ public class NotificationMediaManager implements Dumpable { // Pending entries are never inflated, and will never generate a call to onEntryRemoved(). // This can happen when notifications are added and canceled before inflation. Add this // separate listener for cleanup, since media inflation occurs onPendingEntryAdded(). - notificationEntryManager.addCollectionListener(new NotifCollectionListener() { + mEntryManager.addCollectionListener(new NotifCollectionListener() { @Override public void onEntryCleanUp(@NonNull NotificationEntry entry) { removeEntry(entry); } }); - - mShowCompactMediaSeekbar = "true".equals( - DeviceConfig.getProperty(DeviceConfig.NAMESPACE_SYSTEMUI, - SystemUiDeviceConfigFlags.COMPACT_MEDIA_SEEKBAR_ENABLED)); - - deviceConfig.addOnPropertiesChangedListener(DeviceConfig.NAMESPACE_SYSTEMUI, - mContext.getMainExecutor(), - mPropertiesChangedListener); } private void removeEntry(NotificationEntry entry) { @@ -299,14 +346,24 @@ public class NotificationMediaManager implements Dumpable { if (mMediaNotificationKey == null) { return null; } - synchronized (mEntryManager) { - NotificationEntry entry = mEntryManager + if (mUsingNotifPipeline) { + // TODO(b/169655596): Either add O(1) lookup, or cache this icon? + return mNotifPipeline.getAllNotifs().stream() + .filter(entry -> Objects.equals(entry.getKey(), mMediaNotificationKey)) + .findAny() + .map(entry -> entry.getIcons().getShelfIcon()) + .map(StatusBarIconView::getSourceIcon) + .orElse(null); + } else { + synchronized (mEntryManager) { + NotificationEntry entry = mEntryManager .getActiveNotificationUnfiltered(mMediaNotificationKey); - if (entry == null || entry.getIcons().getShelfIcon() == null) { - return null; - } + if (entry == null || entry.getIcons().getShelfIcon() == null) { + return null; + } - return entry.getIcons().getShelfIcon().getSourceIcon(); + return entry.getIcons().getShelfIcon().getSourceIcon(); + } } } @@ -321,94 +378,110 @@ public class NotificationMediaManager implements Dumpable { } public void findAndUpdateMediaNotifications() { + boolean metaDataChanged; + if (mUsingNotifPipeline) { + // TODO(b/169655907): get the semi-filtered notifications for current user + Collection allNotifications = mNotifPipeline.getAllNotifs(); + metaDataChanged = findPlayingMediaNotification(allNotifications); + } else { + synchronized (mEntryManager) { + Collection allNotifications = mEntryManager.getAllNotifs(); + metaDataChanged = findPlayingMediaNotification(allNotifications); + } + + if (metaDataChanged) { + mEntryManager.updateNotifications("NotificationMediaManager - metaDataChanged"); + } + + } + dispatchUpdateMediaMetaData(metaDataChanged, true /* allowEnterAnimation */); + } + + /** + * Find a notification and media controller associated with the playing media session, and + * update this manager's internal state. + * @return whether the current MediaMetadata changed (and needs to be announced to listeners). + */ + private boolean findPlayingMediaNotification( + @NonNull Collection allNotifications) { boolean metaDataChanged = false; + // Promote the media notification with a controller in 'playing' state, if any. + NotificationEntry mediaNotification = null; + MediaController controller = null; + for (NotificationEntry entry : allNotifications) { + if (entry.isMediaNotification()) { + final MediaSession.Token token = + entry.getSbn().getNotification().extras.getParcelable( + Notification.EXTRA_MEDIA_SESSION); + if (token != null) { + MediaController aController = new MediaController(mContext, token); + if (PlaybackState.STATE_PLAYING + == getMediaControllerPlaybackState(aController)) { + if (DEBUG_MEDIA) { + Log.v(TAG, "DEBUG_MEDIA: found mediastyle controller matching " + + entry.getSbn().getKey()); + } + mediaNotification = entry; + controller = aController; + break; + } + } + } + } + if (mediaNotification == null) { + // Still nothing? OK, let's just look for live media sessions and see if they match + // one of our notifications. This will catch apps that aren't (yet!) using media + // notifications. - synchronized (mEntryManager) { - Collection allNotifications = mEntryManager.getAllNotifs(); + if (mMediaSessionManager != null) { + // TODO: Should this really be for all users? It appears that inactive users + // can't have active sessions, which would mean it is fine. + final List sessions = + mMediaSessionManager.getActiveSessionsForUser(null, UserHandle.USER_ALL); - // Promote the media notification with a controller in 'playing' state, if any. - NotificationEntry mediaNotification = null; - MediaController controller = null; - for (NotificationEntry entry : allNotifications) { - if (entry.isMediaNotification()) { - final MediaSession.Token token = - entry.getSbn().getNotification().extras.getParcelable( - Notification.EXTRA_MEDIA_SESSION); - if (token != null) { - MediaController aController = new MediaController(mContext, token); - if (PlaybackState.STATE_PLAYING == - getMediaControllerPlaybackState(aController)) { + for (MediaController aController : sessions) { + // now to see if we have one like this + final String pkg = aController.getPackageName(); + + for (NotificationEntry entry : allNotifications) { + if (entry.getSbn().getPackageName().equals(pkg)) { if (DEBUG_MEDIA) { - Log.v(TAG, "DEBUG_MEDIA: found mediastyle controller matching " + Log.v(TAG, "DEBUG_MEDIA: found controller matching " + entry.getSbn().getKey()); } - mediaNotification = entry; controller = aController; + mediaNotification = entry; break; } } } } - if (mediaNotification == null) { - // Still nothing? OK, let's just look for live media sessions and see if they match - // one of our notifications. This will catch apps that aren't (yet!) using media - // notifications. + } - if (mMediaSessionManager != null) { - // TODO: Should this really be for all users? - final List sessions - = mMediaSessionManager.getActiveSessionsForUser( - null, - UserHandle.USER_ALL); - - for (MediaController aController : sessions) { - // now to see if we have one like this - final String pkg = aController.getPackageName(); - - for (NotificationEntry entry : allNotifications) { - if (entry.getSbn().getPackageName().equals(pkg)) { - if (DEBUG_MEDIA) { - Log.v(TAG, "DEBUG_MEDIA: found controller matching " - + entry.getSbn().getKey()); - } - controller = aController; - mediaNotification = entry; - break; - } - } - } - } + if (controller != null && !sameSessions(mMediaController, controller)) { + // We have a new media session + clearCurrentMediaNotificationSession(); + mMediaController = controller; + mMediaController.registerCallback(mMediaListener); + mMediaMetadata = mMediaController.getMetadata(); + if (DEBUG_MEDIA) { + Log.v(TAG, "DEBUG_MEDIA: insert listener, found new controller: " + + mMediaController + ", receive metadata: " + mMediaMetadata); } - if (controller != null && !sameSessions(mMediaController, controller)) { - // We have a new media session - clearCurrentMediaNotificationSession(); - mMediaController = controller; - mMediaController.registerCallback(mMediaListener); - mMediaMetadata = mMediaController.getMetadata(); - if (DEBUG_MEDIA) { - Log.v(TAG, "DEBUG_MEDIA: insert listener, found new controller: " - + mMediaController + ", receive metadata: " + mMediaMetadata); - } + metaDataChanged = true; + } - metaDataChanged = true; - } - - if (mediaNotification != null - && !mediaNotification.getSbn().getKey().equals(mMediaNotificationKey)) { - mMediaNotificationKey = mediaNotification.getSbn().getKey(); - if (DEBUG_MEDIA) { - Log.v(TAG, "DEBUG_MEDIA: Found new media notification: key=" - + mMediaNotificationKey); - } + if (mediaNotification != null + && !mediaNotification.getSbn().getKey().equals(mMediaNotificationKey)) { + mMediaNotificationKey = mediaNotification.getSbn().getKey(); + if (DEBUG_MEDIA) { + Log.v(TAG, "DEBUG_MEDIA: Found new media notification: key=" + + mMediaNotificationKey); } } - if (metaDataChanged) { - mEntryManager.updateNotifications("NotificationMediaManager - metaDataChanged"); - } - - dispatchUpdateMediaMetaData(metaDataChanged, true /* allowEnterAnimation */); + return metaDataChanged; } public void clearCurrentMediaNotification() { @@ -428,7 +501,7 @@ public class NotificationMediaManager implements Dumpable { } @Override - public void dump(FileDescriptor fd, PrintWriter pw, String[] args) { + public void dump(@NonNull FileDescriptor fd, @NonNull PrintWriter pw, @NonNull String[] args) { pw.print(" mMediaSessionManager="); pw.println(mMediaSessionManager); pw.print(" mMediaNotificationKey="); diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/dagger/StatusBarDependenciesModule.java b/packages/SystemUI/src/com/android/systemui/statusbar/dagger/StatusBarDependenciesModule.java index d15b8476b3c5e..22b3b85cf8971 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/dagger/StatusBarDependenciesModule.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/dagger/StatusBarDependenciesModule.java @@ -28,6 +28,7 @@ import com.android.systemui.media.MediaDataManager; import com.android.systemui.plugins.statusbar.StatusBarStateController; import com.android.systemui.statusbar.ActionClickLogger; import com.android.systemui.statusbar.CommandQueue; +import com.android.systemui.statusbar.FeatureFlags; import com.android.systemui.statusbar.MediaArtworkProcessor; import com.android.systemui.statusbar.NotificationClickNotifier; import com.android.systemui.statusbar.NotificationListener; @@ -43,6 +44,7 @@ import com.android.systemui.statusbar.notification.AssistantFeedbackController; import com.android.systemui.statusbar.notification.DynamicChildBindController; import com.android.systemui.statusbar.notification.DynamicPrivacyController; import com.android.systemui.statusbar.notification.NotificationEntryManager; +import com.android.systemui.statusbar.notification.collection.NotifPipeline; import com.android.systemui.statusbar.notification.collection.inflation.LowPriorityInflationHelper; import com.android.systemui.statusbar.notification.collection.legacy.NotificationGroupManagerLegacy; import com.android.systemui.statusbar.notification.collection.legacy.VisualStabilityManager; @@ -108,6 +110,8 @@ public interface StatusBarDependenciesModule { NotificationEntryManager notificationEntryManager, MediaArtworkProcessor mediaArtworkProcessor, KeyguardBypassController keyguardBypassController, + NotifPipeline notifPipeline, + FeatureFlags featureFlags, @Main DelayableExecutor mainExecutor, DeviceConfigProxy deviceConfigProxy, MediaDataManager mediaDataManager) { @@ -118,6 +122,8 @@ public interface StatusBarDependenciesModule { notificationEntryManager, mediaArtworkProcessor, keyguardBypassController, + notifPipeline, + featureFlags, mainExecutor, deviceConfigProxy, mediaDataManager);