diff options
32 files changed, 650 insertions, 155 deletions
diff --git a/packages/SystemUI/src/com/android/systemui/flags/FeatureFlags.java b/packages/SystemUI/src/com/android/systemui/flags/FeatureFlags.java index 947a39a32365..48bb281429c2 100644 --- a/packages/SystemUI/src/com/android/systemui/flags/FeatureFlags.java +++ b/packages/SystemUI/src/com/android/systemui/flags/FeatureFlags.java @@ -18,6 +18,8 @@ package com.android.systemui.flags; import android.content.Context; import android.util.FeatureFlagUtils; +import android.util.Log; +import android.widget.Toast; import com.android.internal.annotations.VisibleForTesting; import com.android.systemui.R; @@ -86,6 +88,22 @@ public class FeatureFlags { } } + public void assertLegacyPipelineEnabled() { + if (isNewNotifPipelineRenderingEnabled()) { + throw new IllegalStateException("Old pipeline code running w/ new pipeline enabled"); + } + } + + public boolean checkLegacyPipelineEnabled() { + if (!isNewNotifPipelineRenderingEnabled()) { + return true; + } + Log.d("NotifPipeline", "Old pipeline code running w/ new pipeline enabled", + new Exception()); + Toast.makeText(mContext, "Old pipeline code running!", Toast.LENGTH_SHORT).show(); + return false; + } + public boolean isNewNotifPipelineEnabled() { return isEnabled(Flags.NEW_NOTIFICATION_PIPELINE); } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationViewHierarchyManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationViewHierarchyManager.java index 396d86bab825..464b2b69c58e 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationViewHierarchyManager.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationViewHierarchyManager.java @@ -27,6 +27,7 @@ import android.view.ViewGroup; import com.android.systemui.R; import com.android.systemui.dagger.qualifiers.Main; +import com.android.systemui.flags.FeatureFlags; import com.android.systemui.plugins.statusbar.StatusBarStateController; import com.android.systemui.statusbar.dagger.StatusBarModule; import com.android.systemui.statusbar.notification.AssistantFeedbackController; @@ -72,6 +73,7 @@ public class NotificationViewHierarchyManager implements DynamicPrivacyControlle // Dependencies: private final DynamicChildBindController mDynamicChildBindController; + private final FeatureFlags mFeatureFlags; protected final NotificationLockscreenUserManager mLockscreenUserManager; protected final NotificationGroupManagerLegacy mGroupManager; protected final VisualStabilityManager mVisualStabilityManager; @@ -107,6 +109,7 @@ public class NotificationViewHierarchyManager implements DynamicPrivacyControlle public NotificationViewHierarchyManager( Context context, @Main Handler mainHandler, + FeatureFlags featureFlags, NotificationLockscreenUserManager notificationLockscreenUserManager, NotificationGroupManagerLegacy groupManager, VisualStabilityManager visualStabilityManager, @@ -121,6 +124,7 @@ public class NotificationViewHierarchyManager implements DynamicPrivacyControlle AssistantFeedbackController assistantFeedbackController) { mContext = context; mHandler = mainHandler; + mFeatureFlags = featureFlags; mLockscreenUserManager = notificationLockscreenUserManager; mBypassController = bypassController; mGroupManager = groupManager; @@ -142,7 +146,9 @@ public class NotificationViewHierarchyManager implements DynamicPrivacyControlle NotificationListContainer listContainer) { mPresenter = presenter; mListContainer = listContainer; - mDynamicPrivacyController.addListener(this); + if (!mFeatureFlags.isNewNotifPipelineRenderingEnabled()) { + mDynamicPrivacyController.addListener(this); + } } /** @@ -151,6 +157,10 @@ public class NotificationViewHierarchyManager implements DynamicPrivacyControlle //TODO: Rewrite this to focus on Entries, or some other data object instead of views public void updateNotificationViews() { Assert.isMainThread(); + if (!mFeatureFlags.checkLegacyPipelineEnabled()) { + return; + } + beginUpdate(); List<NotificationEntry> activeNotifications = mEntryManager.getVisibleNotifications(); @@ -425,6 +435,10 @@ public class NotificationViewHierarchyManager implements DynamicPrivacyControlle */ public void updateRowStates() { Assert.isMainThread(); + if (!mFeatureFlags.checkLegacyPipelineEnabled()) { + return; + } + beginUpdate(); updateRowStatesInternal(); endUpdate(); @@ -510,6 +524,7 @@ public class NotificationViewHierarchyManager implements DynamicPrivacyControlle @Override public void onDynamicPrivacyChanged() { + mFeatureFlags.assertLegacyPipelineEnabled(); if (mPerformingUpdate) { Log.w(TAG, "onDynamicPrivacyChanged made a re-entrant call"); } 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 d297d9581d6a..1c9174a33bbc 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/dagger/StatusBarDependenciesModule.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/dagger/StatusBarDependenciesModule.java @@ -184,6 +184,7 @@ public interface StatusBarDependenciesModule { static NotificationViewHierarchyManager provideNotificationViewHierarchyManager( Context context, @Main Handler mainHandler, + FeatureFlags featureFlags, NotificationLockscreenUserManager notificationLockscreenUserManager, NotificationGroupManagerLegacy groupManager, VisualStabilityManager visualStabilityManager, @@ -199,6 +200,7 @@ public interface StatusBarDependenciesModule { return new NotificationViewHierarchyManager( context, mainHandler, + featureFlags, notificationLockscreenUserManager, groupManager, visualStabilityManager, diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotifPipeline.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotifPipeline.java index 47939f0579f5..577792547c23 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotifPipeline.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotifPipeline.java @@ -23,6 +23,7 @@ import com.android.systemui.statusbar.notification.collection.listbuilder.OnBefo import com.android.systemui.statusbar.notification.collection.listbuilder.OnBeforeRenderListListener; import com.android.systemui.statusbar.notification.collection.listbuilder.OnBeforeSortListener; import com.android.systemui.statusbar.notification.collection.listbuilder.OnBeforeTransformGroupsListener; +import com.android.systemui.statusbar.notification.collection.listbuilder.pluggable.Invalidator; import com.android.systemui.statusbar.notification.collection.listbuilder.pluggable.NotifComparator; import com.android.systemui.statusbar.notification.collection.listbuilder.pluggable.NotifFilter; import com.android.systemui.statusbar.notification.collection.listbuilder.pluggable.NotifPromoter; @@ -216,6 +217,11 @@ public class NotifPipeline implements CommonNotifCollection { mShadeListBuilder.addOnBeforeRenderListListener(listener); } + /** Registers an invalidator that can be used to invalidate the entire notif list. */ + public void addPreRenderInvalidator(Invalidator invalidator) { + mShadeListBuilder.addPreRenderInvalidator(invalidator); + } + /** * Returns a read-only view in to the current shade list, i.e. the list of notifications that * are currently present in the shade. If this method is called during pipeline execution it diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/ShadeListBuilder.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/ShadeListBuilder.java index 3730524353ec..9faec7bac40f 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/ShadeListBuilder.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/ShadeListBuilder.java @@ -45,6 +45,7 @@ import com.android.systemui.statusbar.notification.collection.listbuilder.OnBefo import com.android.systemui.statusbar.notification.collection.listbuilder.OnBeforeTransformGroupsListener; import com.android.systemui.statusbar.notification.collection.listbuilder.PipelineState; import com.android.systemui.statusbar.notification.collection.listbuilder.ShadeListBuilderLogger; +import com.android.systemui.statusbar.notification.collection.listbuilder.pluggable.Invalidator; import com.android.systemui.statusbar.notification.collection.listbuilder.pluggable.NotifComparator; import com.android.systemui.statusbar.notification.collection.listbuilder.pluggable.NotifFilter; import com.android.systemui.statusbar.notification.collection.listbuilder.pluggable.NotifPromoter; @@ -174,6 +175,13 @@ public class ShadeListBuilder implements Dumpable { mOnBeforeRenderListListeners.add(listener); } + void addPreRenderInvalidator(Invalidator invalidator) { + Assert.isMainThread(); + + mPipelineState.requireState(STATE_IDLE); + invalidator.setInvalidationListener(this::onPreRenderInvalidated); + } + void addPreGroupFilter(NotifFilter filter) { Assert.isMainThread(); mPipelineState.requireState(STATE_IDLE); @@ -256,6 +264,14 @@ public class ShadeListBuilder implements Dumpable { } }; + private void onPreRenderInvalidated(Invalidator invalidator) { + Assert.isMainThread(); + + mLogger.logPreRenderInvalidated(invalidator.getName(), mPipelineState.getState()); + + rebuildListIfBefore(STATE_FINALIZING); + } + private void onPreGroupFilterInvalidated(NotifFilter filter) { Assert.isMainThread(); diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/AppOpsCoordinator.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/AppOpsCoordinator.java index 301b185c7373..3a39c39cfb20 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/AppOpsCoordinator.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/AppOpsCoordinator.java @@ -23,11 +23,11 @@ import android.service.notification.StatusBarNotification; import com.android.systemui.ForegroundServiceController; import com.android.systemui.appops.AppOpsController; -import com.android.systemui.dagger.SysUISingleton; import com.android.systemui.dagger.qualifiers.Main; import com.android.systemui.statusbar.notification.collection.ListEntry; import com.android.systemui.statusbar.notification.collection.NotifPipeline; import com.android.systemui.statusbar.notification.collection.NotificationEntry; +import com.android.systemui.statusbar.notification.collection.coordinator.dagger.CoordinatorScope; import com.android.systemui.statusbar.notification.collection.listbuilder.pluggable.NotifFilter; import com.android.systemui.statusbar.notification.collection.listbuilder.pluggable.NotifSectioner; import com.android.systemui.statusbar.notification.stack.NotificationPriorityBucketKt; @@ -48,7 +48,7 @@ import javax.inject.Inject; * frameworks/base/packages/SystemUI/src/com/android/systemui/ForegroundServiceNotificationListener * frameworks/base/packages/SystemUI/src/com/android/systemui/ForegroundServiceLifetimeExtender */ -@SysUISingleton +@CoordinatorScope public class AppOpsCoordinator implements Coordinator { private static final String TAG = "AppOpsCoordinator"; diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/BubbleCoordinator.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/BubbleCoordinator.java index 29a030f910a4..15f0d885c2fa 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/BubbleCoordinator.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/BubbleCoordinator.java @@ -16,10 +16,10 @@ package com.android.systemui.statusbar.notification.collection.coordinator; -import com.android.systemui.dagger.SysUISingleton; import com.android.systemui.statusbar.notification.collection.NotifCollection; import com.android.systemui.statusbar.notification.collection.NotifPipeline; import com.android.systemui.statusbar.notification.collection.NotificationEntry; +import com.android.systemui.statusbar.notification.collection.coordinator.dagger.CoordinatorScope; import com.android.systemui.statusbar.notification.collection.listbuilder.pluggable.NotifFilter; import com.android.systemui.statusbar.notification.collection.notifcollection.DismissedByUserStats; import com.android.systemui.statusbar.notification.collection.notifcollection.NotifDismissInterceptor; @@ -53,7 +53,7 @@ import javax.inject.Inject; * respond to app-cancellations (ie: remove the bubble if the app cancels the notification). * */ -@SysUISingleton +@CoordinatorScope public class BubbleCoordinator implements Coordinator { private static final String TAG = "BubbleCoordinator"; diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/ConversationCoordinator.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/ConversationCoordinator.kt index c38583679b4f..e59f4a62f9b7 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/ConversationCoordinator.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/ConversationCoordinator.kt @@ -16,10 +16,10 @@ package com.android.systemui.statusbar.notification.collection.coordinator -import com.android.systemui.dagger.SysUISingleton import com.android.systemui.statusbar.notification.collection.ListEntry import com.android.systemui.statusbar.notification.collection.NotifPipeline import com.android.systemui.statusbar.notification.collection.NotificationEntry +import com.android.systemui.statusbar.notification.collection.coordinator.dagger.CoordinatorScope import com.android.systemui.statusbar.notification.collection.listbuilder.pluggable.NotifPromoter import com.android.systemui.statusbar.notification.collection.listbuilder.pluggable.NotifSectioner import com.android.systemui.statusbar.notification.collection.render.NodeController @@ -34,7 +34,7 @@ import javax.inject.Inject * - Elevates important conversation notifications * - Puts conversations into its own people section. @see [NotifCoordinators] for section ordering. */ -@SysUISingleton +@CoordinatorScope class ConversationCoordinator @Inject constructor( private val peopleNotificationIdentifier: PeopleNotificationIdentifier, @PeopleHeader peopleHeaderController: NodeController diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/DeviceProvisionedCoordinator.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/DeviceProvisionedCoordinator.java index 47928b42ed5e..e8652493da6b 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/DeviceProvisionedCoordinator.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/DeviceProvisionedCoordinator.java @@ -23,9 +23,9 @@ import android.content.pm.PackageManager; import android.os.RemoteException; import android.service.notification.StatusBarNotification; -import com.android.systemui.dagger.SysUISingleton; import com.android.systemui.statusbar.notification.collection.NotifPipeline; import com.android.systemui.statusbar.notification.collection.NotificationEntry; +import com.android.systemui.statusbar.notification.collection.coordinator.dagger.CoordinatorScope; import com.android.systemui.statusbar.notification.collection.listbuilder.pluggable.NotifFilter; import com.android.systemui.statusbar.policy.DeviceProvisionedController; @@ -36,7 +36,7 @@ import javax.inject.Inject; * Special notifications with extra permissions and tags won't be filtered out even when the * device is unprovisioned. */ -@SysUISingleton +@CoordinatorScope public class DeviceProvisionedCoordinator implements Coordinator { private static final String TAG = "DeviceProvisionedCoordinator"; diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/GutsCoordinator.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/GutsCoordinator.kt index 8948969120ac..dbecf1cc28d7 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/GutsCoordinator.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/GutsCoordinator.kt @@ -17,17 +17,17 @@ package com.android.systemui.statusbar.notification.collection.coordinator import android.util.ArraySet import com.android.systemui.Dumpable -import com.android.systemui.dagger.SysUISingleton import com.android.systemui.dump.DumpManager import com.android.systemui.statusbar.notification.collection.ListEntry import com.android.systemui.statusbar.notification.collection.NotifPipeline import com.android.systemui.statusbar.notification.collection.NotificationEntry +import com.android.systemui.statusbar.notification.collection.coordinator.dagger.CoordinatorScope import com.android.systemui.statusbar.notification.collection.notifcollection.NotifLifetimeExtender import com.android.systemui.statusbar.notification.collection.notifcollection.NotifLifetimeExtender.OnEndLifetimeExtensionCallback -import com.android.systemui.statusbar.notification.row.NotificationGuts -import com.android.systemui.statusbar.notification.row.NotificationGutsManager import com.android.systemui.statusbar.notification.collection.render.NotifGutsViewListener import com.android.systemui.statusbar.notification.collection.render.NotifGutsViewManager +import com.android.systemui.statusbar.notification.row.NotificationGuts +import com.android.systemui.statusbar.notification.row.NotificationGutsManager import java.io.FileDescriptor import java.io.PrintWriter import javax.inject.Inject @@ -38,7 +38,7 @@ private const val TAG = "GutsCoordinator" * Coordinates the guts displayed by the [NotificationGutsManager] with the pipeline. * Specifically, this just adds the lifetime extension necessary to keep guts from disappearing. */ -@SysUISingleton +@CoordinatorScope class GutsCoordinator @Inject constructor( private val notifGutsViewManager: NotifGutsViewManager, private val logger: GutsCoordinatorLogger, diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/HeadsUpCoordinator.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/HeadsUpCoordinator.java index 8c8a8a977d1c..f8b4274188f5 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/HeadsUpCoordinator.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/HeadsUpCoordinator.java @@ -22,11 +22,11 @@ import static com.android.systemui.statusbar.notification.interruption.HeadsUpCo import androidx.annotation.NonNull; import androidx.annotation.Nullable; -import com.android.systemui.dagger.SysUISingleton; import com.android.systemui.statusbar.NotificationRemoteInputManager; import com.android.systemui.statusbar.notification.collection.ListEntry; import com.android.systemui.statusbar.notification.collection.NotifPipeline; import com.android.systemui.statusbar.notification.collection.NotificationEntry; +import com.android.systemui.statusbar.notification.collection.coordinator.dagger.CoordinatorScope; import com.android.systemui.statusbar.notification.collection.listbuilder.pluggable.NotifPromoter; import com.android.systemui.statusbar.notification.collection.listbuilder.pluggable.NotifSectioner; import com.android.systemui.statusbar.notification.collection.notifcollection.NotifCollectionListener; @@ -57,7 +57,7 @@ import javax.inject.Inject; * * Note: The inflation callback in {@link PreparationCoordinator} handles showing HUNs. */ -@SysUISingleton +@CoordinatorScope public class HeadsUpCoordinator implements Coordinator { private static final String TAG = "HeadsUpCoordinator"; diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/HideLocallyDismissedNotifsCoordinator.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/HideLocallyDismissedNotifsCoordinator.java index 0059e7baa3c2..6684237c4ebf 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/HideLocallyDismissedNotifsCoordinator.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/HideLocallyDismissedNotifsCoordinator.java @@ -20,13 +20,21 @@ import static com.android.systemui.statusbar.notification.collection.Notificatio import com.android.systemui.statusbar.notification.collection.NotifPipeline; import com.android.systemui.statusbar.notification.collection.NotificationEntry; +import com.android.systemui.statusbar.notification.collection.coordinator.dagger.CoordinatorScope; import com.android.systemui.statusbar.notification.collection.listbuilder.pluggable.NotifFilter; +import javax.inject.Inject; + /** * Filters out notifications that have been dismissed locally (by the user) but that system server * hasn't yet confirmed the removal of. */ +@CoordinatorScope public class HideLocallyDismissedNotifsCoordinator implements Coordinator { + + @Inject + HideLocallyDismissedNotifsCoordinator() { } + @Override public void attach(NotifPipeline pipeline) { pipeline.addPreGroupFilter(mFilter); diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/HideNotifsForOtherUsersCoordinator.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/HideNotifsForOtherUsersCoordinator.java index e595dd4a2f71..7b5cf8511900 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/HideNotifsForOtherUsersCoordinator.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/HideNotifsForOtherUsersCoordinator.java @@ -23,6 +23,7 @@ import com.android.systemui.statusbar.NotificationLockscreenUserManager; import com.android.systemui.statusbar.NotificationLockscreenUserManager.UserChangedListener; import com.android.systemui.statusbar.notification.collection.NotifPipeline; import com.android.systemui.statusbar.notification.collection.NotificationEntry; +import com.android.systemui.statusbar.notification.collection.coordinator.dagger.CoordinatorScope; import com.android.systemui.statusbar.notification.collection.listbuilder.pluggable.NotifFilter; import javax.inject.Inject; @@ -37,6 +38,7 @@ import javax.inject.Inject; * TODO: The NotificationLockscreenUserManager currently maintains the list of active user profiles. * We should spin that off into a standalone section at some point. */ +@CoordinatorScope public class HideNotifsForOtherUsersCoordinator implements Coordinator { private final NotificationLockscreenUserManager mLockscreenUserManager; private final SharedCoordinatorLogger mLogger; diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/KeyguardCoordinator.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/KeyguardCoordinator.java index 23d5369833c5..fe1cd7b98cf9 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/KeyguardCoordinator.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/KeyguardCoordinator.java @@ -34,13 +34,13 @@ import androidx.annotation.MainThread; import com.android.keyguard.KeyguardUpdateMonitor; import com.android.keyguard.KeyguardUpdateMonitorCallback; import com.android.systemui.broadcast.BroadcastDispatcher; -import com.android.systemui.dagger.SysUISingleton; import com.android.systemui.plugins.statusbar.StatusBarStateController; import com.android.systemui.statusbar.NotificationLockscreenUserManager; import com.android.systemui.statusbar.notification.collection.GroupEntry; import com.android.systemui.statusbar.notification.collection.ListEntry; import com.android.systemui.statusbar.notification.collection.NotifPipeline; import com.android.systemui.statusbar.notification.collection.NotificationEntry; +import com.android.systemui.statusbar.notification.collection.coordinator.dagger.CoordinatorScope; import com.android.systemui.statusbar.notification.collection.listbuilder.pluggable.NotifFilter; import com.android.systemui.statusbar.notification.collection.provider.HighPriorityProvider; import com.android.systemui.statusbar.policy.KeyguardStateController; @@ -50,7 +50,7 @@ import javax.inject.Inject; /** * Filters low priority and privacy-sensitive notifications from the lockscreen. */ -@SysUISingleton +@CoordinatorScope public class KeyguardCoordinator implements Coordinator { private static final String TAG = "KeyguardCoordinator"; diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/MediaCoordinator.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/MediaCoordinator.java index 026a3ffb73cd..8769969834c8 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/MediaCoordinator.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/MediaCoordinator.java @@ -21,6 +21,7 @@ import static com.android.systemui.media.MediaDataManagerKt.isMediaNotification; import com.android.systemui.media.MediaFeatureFlag; import com.android.systemui.statusbar.notification.collection.NotifPipeline; import com.android.systemui.statusbar.notification.collection.NotificationEntry; +import com.android.systemui.statusbar.notification.collection.coordinator.dagger.CoordinatorScope; import com.android.systemui.statusbar.notification.collection.listbuilder.pluggable.NotifFilter; import javax.inject.Inject; @@ -28,6 +29,7 @@ import javax.inject.Inject; /** * Coordinates hiding (filtering) of media notifications. */ +@CoordinatorScope public class MediaCoordinator implements Coordinator { private static final String TAG = "MediaCoordinator"; diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/NotifCoordinators.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/NotifCoordinators.java deleted file mode 100644 index 93059009a56e..000000000000 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/NotifCoordinators.java +++ /dev/null @@ -1,128 +0,0 @@ -/* - * Copyright (C) 2019 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.android.systemui.statusbar.notification.collection.coordinator; - -import com.android.systemui.Dumpable; -import com.android.systemui.dagger.SysUISingleton; -import com.android.systemui.dump.DumpManager; -import com.android.systemui.flags.FeatureFlags; -import com.android.systemui.statusbar.notification.collection.NotifPipeline; -import com.android.systemui.statusbar.notification.collection.listbuilder.pluggable.NotifSectioner; -import com.android.systemui.statusbar.notification.collection.listbuilder.pluggable.Pluggable; -import com.android.systemui.statusbar.notification.collection.notifcollection.NotifCollectionListener; -import com.android.systemui.statusbar.notification.collection.notifcollection.NotifLifetimeExtender; - -import java.io.FileDescriptor; -import java.io.PrintWriter; -import java.util.ArrayList; -import java.util.List; - -import javax.inject.Inject; - -/** - * Handles the attachment of {@link Coordinator}s to the {@link NotifPipeline} so that the - * Coordinators can register their respective callbacks. - */ -@SysUISingleton -public class NotifCoordinators implements Dumpable { - private static final String TAG = "NotifCoordinators"; - private final List<Coordinator> mCoordinators = new ArrayList<>(); - private final List<NotifSectioner> mOrderedSections = new ArrayList<>(); - - /** - * Creates all the coordinators. - */ - @Inject - public NotifCoordinators( - DumpManager dumpManager, - FeatureFlags featureFlags, - HideNotifsForOtherUsersCoordinator hideNotifsForOtherUsersCoordinator, - KeyguardCoordinator keyguardCoordinator, - RankingCoordinator rankingCoordinator, - AppOpsCoordinator appOpsCoordinator, - DeviceProvisionedCoordinator deviceProvisionedCoordinator, - BubbleCoordinator bubbleCoordinator, - HeadsUpCoordinator headsUpCoordinator, - GutsCoordinator gutsCoordinator, - ConversationCoordinator conversationCoordinator, - PreparationCoordinator preparationCoordinator, - MediaCoordinator mediaCoordinator, - ShadeEventCoordinator shadeEventCoordinator, - SmartspaceDedupingCoordinator smartspaceDedupingCoordinator, - ViewConfigCoordinator viewConfigCoordinator, - VisualStabilityCoordinator visualStabilityCoordinator) { - dumpManager.registerDumpable(TAG, this); - - mCoordinators.add(new HideLocallyDismissedNotifsCoordinator()); - mCoordinators.add(hideNotifsForOtherUsersCoordinator); - mCoordinators.add(keyguardCoordinator); - mCoordinators.add(rankingCoordinator); - mCoordinators.add(appOpsCoordinator); - mCoordinators.add(deviceProvisionedCoordinator); - mCoordinators.add(bubbleCoordinator); - mCoordinators.add(conversationCoordinator); - mCoordinators.add(mediaCoordinator); - mCoordinators.add(shadeEventCoordinator); - mCoordinators.add(viewConfigCoordinator); - mCoordinators.add(visualStabilityCoordinator); - - if (featureFlags.isSmartspaceDedupingEnabled()) { - mCoordinators.add(smartspaceDedupingCoordinator); - } - - if (featureFlags.isNewNotifPipelineRenderingEnabled()) { - mCoordinators.add(headsUpCoordinator); - mCoordinators.add(gutsCoordinator); - mCoordinators.add(preparationCoordinator); - } - - // Manually add Ordered Sections - // HeadsUp > FGS > People > Alerting > Silent > Unknown/Default - if (featureFlags.isNewNotifPipelineRenderingEnabled()) { - mOrderedSections.add(headsUpCoordinator.getSectioner()); // HeadsUp - } - mOrderedSections.add(appOpsCoordinator.getSectioner()); // ForegroundService - mOrderedSections.add(conversationCoordinator.getSectioner()); // People - mOrderedSections.add(rankingCoordinator.getAlertingSectioner()); // Alerting - mOrderedSections.add(rankingCoordinator.getSilentSectioner()); // Silent - } - - /** - * Sends the pipeline to each coordinator when the pipeline is ready to accept - * {@link Pluggable}s, {@link NotifCollectionListener}s and {@link NotifLifetimeExtender}s. - */ - public void attach(NotifPipeline pipeline) { - for (Coordinator c : mCoordinators) { - c.attach(pipeline); - } - - pipeline.setSections(mOrderedSections); - } - - @Override - public void dump(FileDescriptor fd, PrintWriter pw, String[] args) { - pw.println(); - pw.println(TAG + ":"); - for (Coordinator c : mCoordinators) { - pw.println("\t" + c.getClass()); - } - - for (NotifSectioner s : mOrderedSections) { - pw.println("\t" + s.getName()); - } - } -} diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/NotifCoordinators.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/NotifCoordinators.kt new file mode 100644 index 000000000000..66290bb3aba6 --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/NotifCoordinators.kt @@ -0,0 +1,124 @@ +/* + * Copyright (C) 2019 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.android.systemui.statusbar.notification.collection.coordinator + +import com.android.systemui.Dumpable +import com.android.systemui.dump.DumpManager +import com.android.systemui.flags.FeatureFlags +import com.android.systemui.statusbar.notification.collection.NotifPipeline +import com.android.systemui.statusbar.notification.collection.coordinator.dagger.CoordinatorScope +import com.android.systemui.statusbar.notification.collection.listbuilder.pluggable.NotifSectioner +import java.io.FileDescriptor +import java.io.PrintWriter +import java.util.ArrayList +import javax.inject.Inject + +/** + * Handles the attachment of [Coordinator]s to the [NotifPipeline] so that the + * Coordinators can register their respective callbacks. + */ +interface NotifCoordinators : Coordinator, Dumpable + +@CoordinatorScope +class NotifCoordinatorsImpl @Inject constructor( + dumpManager: DumpManager, + featureFlags: FeatureFlags, + hideLocallyDismissedNotifsCoordinator: HideLocallyDismissedNotifsCoordinator, + hideNotifsForOtherUsersCoordinator: HideNotifsForOtherUsersCoordinator, + keyguardCoordinator: KeyguardCoordinator, + rankingCoordinator: RankingCoordinator, + appOpsCoordinator: AppOpsCoordinator, + deviceProvisionedCoordinator: DeviceProvisionedCoordinator, + bubbleCoordinator: BubbleCoordinator, + headsUpCoordinator: HeadsUpCoordinator, + gutsCoordinator: GutsCoordinator, + conversationCoordinator: ConversationCoordinator, + preparationCoordinator: PreparationCoordinator, + mediaCoordinator: MediaCoordinator, + shadeEventCoordinator: ShadeEventCoordinator, + smartspaceDedupingCoordinator: SmartspaceDedupingCoordinator, + viewConfigCoordinator: ViewConfigCoordinator, + visualStabilityCoordinator: VisualStabilityCoordinator, + sensitiveContentCoordinator: SensitiveContentCoordinator +) : NotifCoordinators { + + private val mCoordinators: MutableList<Coordinator> = ArrayList() + private val mOrderedSections: MutableList<NotifSectioner> = ArrayList() + + /** + * Creates all the coordinators. + */ + init { + dumpManager.registerDumpable(TAG, this) + mCoordinators.add(hideLocallyDismissedNotifsCoordinator) + mCoordinators.add(hideNotifsForOtherUsersCoordinator) + mCoordinators.add(keyguardCoordinator) + mCoordinators.add(rankingCoordinator) + mCoordinators.add(appOpsCoordinator) + mCoordinators.add(deviceProvisionedCoordinator) + mCoordinators.add(bubbleCoordinator) + mCoordinators.add(conversationCoordinator) + mCoordinators.add(mediaCoordinator) + mCoordinators.add(shadeEventCoordinator) + mCoordinators.add(viewConfigCoordinator) + mCoordinators.add(visualStabilityCoordinator) + mCoordinators.add(sensitiveContentCoordinator) + if (featureFlags.isSmartspaceDedupingEnabled) { + mCoordinators.add(smartspaceDedupingCoordinator) + } + if (featureFlags.isNewNotifPipelineRenderingEnabled) { + mCoordinators.add(headsUpCoordinator) + mCoordinators.add(gutsCoordinator) + mCoordinators.add(preparationCoordinator) + } + + // Manually add Ordered Sections + // HeadsUp > FGS > People > Alerting > Silent > Unknown/Default + if (featureFlags.isNewNotifPipelineRenderingEnabled) { + mOrderedSections.add(headsUpCoordinator.sectioner) // HeadsUp + } + mOrderedSections.add(appOpsCoordinator.sectioner) // ForegroundService + mOrderedSections.add(conversationCoordinator.sectioner) // People + mOrderedSections.add(rankingCoordinator.alertingSectioner) // Alerting + mOrderedSections.add(rankingCoordinator.silentSectioner) // Silent + } + + /** + * Sends the pipeline to each coordinator when the pipeline is ready to accept + * [Pluggable]s, [NotifCollectionListener]s and [NotifLifetimeExtender]s. + */ + override fun attach(pipeline: NotifPipeline) { + for (c in mCoordinators) { + c.attach(pipeline) + } + pipeline.setSections(mOrderedSections) + } + + override fun dump(fd: FileDescriptor, pw: PrintWriter, args: Array<String>) { + pw.println() + pw.println("$TAG:") + for (c in mCoordinators) { + pw.println("\t${c.javaClass}") + } + for (s in mOrderedSections) { + pw.println("\t${s.name}") + } + } + + companion object { + private const val TAG = "NotifCoordinators" + } +}
\ No newline at end of file diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/PreparationCoordinator.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/PreparationCoordinator.java index 31826c7219de..afdfb3bdeef6 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/PreparationCoordinator.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/PreparationCoordinator.java @@ -57,6 +57,7 @@ import javax.inject.Inject; * If a notification was uninflated, this coordinator will filter the notification out from the * {@link ShadeListBuilder} until it is inflated. */ +// TODO(b/204468557): Move to @CoordinatorScope @SysUISingleton public class PreparationCoordinator implements Coordinator { private static final String TAG = "PreparationCoordinator"; diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/RankingCoordinator.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/RankingCoordinator.java index 1a6a63a9ccb6..2ab2dd0b1273 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/RankingCoordinator.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/RankingCoordinator.java @@ -19,11 +19,11 @@ package com.android.systemui.statusbar.notification.collection.coordinator; import android.annotation.NonNull; import android.annotation.Nullable; -import com.android.systemui.dagger.SysUISingleton; import com.android.systemui.plugins.statusbar.StatusBarStateController; import com.android.systemui.statusbar.notification.collection.ListEntry; import com.android.systemui.statusbar.notification.collection.NotifPipeline; import com.android.systemui.statusbar.notification.collection.NotificationEntry; +import com.android.systemui.statusbar.notification.collection.coordinator.dagger.CoordinatorScope; import com.android.systemui.statusbar.notification.collection.listbuilder.pluggable.NotifFilter; import com.android.systemui.statusbar.notification.collection.listbuilder.pluggable.NotifSectioner; import com.android.systemui.statusbar.notification.collection.provider.HighPriorityProvider; @@ -44,7 +44,7 @@ import javax.inject.Inject; * - whether the notification's app is suspended or hiding its notifications * - whether DND settings are hiding notifications from ambient display or the notification list */ -@SysUISingleton +@CoordinatorScope public class RankingCoordinator implements Coordinator { public static final boolean SHOW_ALL_SECTIONS = false; private final StatusBarStateController mStatusBarStateController; diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/SensitiveContentCoordinator.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/SensitiveContentCoordinator.kt new file mode 100644 index 000000000000..a115e0400de3 --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/SensitiveContentCoordinator.kt @@ -0,0 +1,105 @@ +/* + * Copyright (C) 2021 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.systemui.statusbar.notification.collection.coordinator + +import android.os.UserHandle +import com.android.systemui.statusbar.NotificationLockscreenUserManager +import com.android.systemui.statusbar.notification.DynamicPrivacyController +import com.android.systemui.statusbar.notification.collection.GroupEntry +import com.android.systemui.statusbar.notification.collection.ListEntry +import com.android.systemui.statusbar.notification.collection.NotifPipeline +import com.android.systemui.statusbar.notification.collection.NotificationEntry +import com.android.systemui.statusbar.notification.collection.coordinator.dagger.CoordinatorScope +import com.android.systemui.statusbar.notification.collection.listbuilder.OnBeforeRenderListListener +import com.android.systemui.statusbar.notification.collection.listbuilder.pluggable.Invalidator +import dagger.Module +import dagger.Provides + +@Module +object SensitiveContentCoordinatorModule { + @Provides + @JvmStatic + @CoordinatorScope + fun provideCoordinator( + dynamicPrivacyController: DynamicPrivacyController, + lockscreenUserManager: NotificationLockscreenUserManager + ): SensitiveContentCoordinator = + SensitiveContentCoordinatorImpl(dynamicPrivacyController, lockscreenUserManager) +} + +/** Coordinates re-inflation and post-processing of sensitive notification content. */ +interface SensitiveContentCoordinator : Coordinator + +private class SensitiveContentCoordinatorImpl( + private val dynamicPrivacyController: DynamicPrivacyController, + private val lockscreenUserManager: NotificationLockscreenUserManager +) : Invalidator("SensitiveContentInvalidator"), + SensitiveContentCoordinator, + DynamicPrivacyController.Listener, + OnBeforeRenderListListener { + + override fun attach(pipeline: NotifPipeline) { + dynamicPrivacyController.addListener(this) + pipeline.addOnBeforeRenderListListener(this) + pipeline.addPreRenderInvalidator(this) + } + + override fun onDynamicPrivacyChanged(): Unit = invalidateList() + + override fun onBeforeRenderList(entries: List<ListEntry>) { + val currentUserId = lockscreenUserManager.currentUserId + val devicePublic = lockscreenUserManager.isLockscreenPublicMode(currentUserId) + val deviceSensitive = devicePublic && + !lockscreenUserManager.userAllowsPrivateNotificationsInPublic(currentUserId) + val dynamicallyUnlocked = dynamicPrivacyController.isDynamicallyUnlocked + for (entry in extractAllRepresentativeEntries(entries).filter { it.rowExists() }) { + val notifUserId = entry.sbn.user.identifier + val userLockscreen = devicePublic || + lockscreenUserManager.isLockscreenPublicMode(notifUserId) + val userPublic = when { + // if we're not on the lockscreen, we're definitely private + !userLockscreen -> false + // we are on the lockscreen, so unless we're dynamically unlocked, we're + // definitely public + !dynamicallyUnlocked -> true + // we're dynamically unlocked, but check if the notification needs + // a separate challenge if it's from a work profile + else -> when (notifUserId) { + currentUserId -> false + UserHandle.USER_ALL -> false + else -> lockscreenUserManager.needsSeparateWorkChallenge(notifUserId) + } + } + val needsRedaction = lockscreenUserManager.needsRedaction(entry) + val isSensitive = userPublic && needsRedaction + entry.setSensitive(isSensitive, deviceSensitive) + } + } +} + +private fun extractAllRepresentativeEntries( + entries: List<ListEntry> +): Sequence<NotificationEntry> = + entries.asSequence().flatMap(::extractAllRepresentativeEntries) + +private fun extractAllRepresentativeEntries(listEntry: ListEntry): Sequence<NotificationEntry> = + sequence { + listEntry.representativeEntry?.let { yield(it) } + if (listEntry is GroupEntry) { + yieldAll(extractAllRepresentativeEntries(listEntry.children)) + } + }
\ No newline at end of file diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/ShadeEventCoordinator.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/ShadeEventCoordinator.kt index f9648a0b94a3..2d5c331e2071 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/ShadeEventCoordinator.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/ShadeEventCoordinator.kt @@ -29,6 +29,7 @@ import javax.inject.Inject * A coordinator which provides callbacks to a view surfaces for various events relevant to the * shade, such as when the user removes a notification, or when the shade is emptied. */ +// TODO(b/204468557): Move to @CoordinatorScope @SysUISingleton class ShadeEventCoordinator @Inject internal constructor( private val mLogger: ShadeEventCoordinatorLogger diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/SmartspaceDedupingCoordinator.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/SmartspaceDedupingCoordinator.kt index 442d9d2bb205..519d75ff07d0 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/SmartspaceDedupingCoordinator.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/SmartspaceDedupingCoordinator.kt @@ -18,7 +18,6 @@ package com.android.systemui.statusbar.notification.collection.coordinator import android.app.smartspace.SmartspaceTarget import android.os.Parcelable -import com.android.systemui.dagger.SysUISingleton import com.android.systemui.dagger.qualifiers.Main import com.android.systemui.plugins.statusbar.StatusBarStateController import com.android.systemui.statusbar.NotificationLockscreenUserManager @@ -28,6 +27,7 @@ import com.android.systemui.statusbar.lockscreen.LockscreenSmartspaceController 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.coordinator.dagger.CoordinatorScope import com.android.systemui.statusbar.notification.collection.listbuilder.pluggable.NotifFilter import com.android.systemui.statusbar.notification.collection.notifcollection.NotifCollectionListener import com.android.systemui.util.concurrency.DelayableExecutor @@ -45,7 +45,7 @@ import javax.inject.Inject */ // This class is a singleton so that the same instance can be accessed by both the old and new // pipelines -@SysUISingleton +@CoordinatorScope class SmartspaceDedupingCoordinator @Inject constructor( private val statusBarStateController: SysuiStatusBarStateController, private val smartspaceController: LockscreenSmartspaceController, diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/ViewConfigCoordinator.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/ViewConfigCoordinator.kt index df1132b20a4b..5b86de2a9d87 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/ViewConfigCoordinator.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/ViewConfigCoordinator.kt @@ -19,11 +19,11 @@ package com.android.systemui.statusbar.notification.collection.coordinator import com.android.internal.widget.MessagingGroup import com.android.internal.widget.MessagingMessage import com.android.keyguard.KeyguardUpdateMonitor -import com.android.systemui.dagger.SysUISingleton import com.android.systemui.flags.FeatureFlags import com.android.systemui.statusbar.NotificationLockscreenUserManager.UserChangedListener import com.android.systemui.statusbar.NotificationLockscreenUserManagerImpl import com.android.systemui.statusbar.notification.collection.NotifPipeline +import com.android.systemui.statusbar.notification.collection.coordinator.dagger.CoordinatorScope import com.android.systemui.statusbar.notification.row.NotificationGutsManager import com.android.systemui.statusbar.policy.ConfigurationController import javax.inject.Inject @@ -33,7 +33,7 @@ import javax.inject.Inject * for the current uiMode and screen properties; additionally deferring those changes when a user * change is in progress until that process has completed. */ -@SysUISingleton +@CoordinatorScope class ViewConfigCoordinator @Inject internal constructor( configurationController: ConfigurationController, lockscreenUserManager: NotificationLockscreenUserManagerImpl, diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/VisualStabilityCoordinator.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/VisualStabilityCoordinator.java index 5d6c0437ce9b..5ba4c2ff36ed 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/VisualStabilityCoordinator.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/VisualStabilityCoordinator.java @@ -50,6 +50,7 @@ import javax.inject.Inject; * This is now integrated in the data-layer via * {@link com.android.systemui.statusbar.notification.collection.ShadeListBuilder}. */ +// TODO(b/204468557): Move to @CoordinatorScope @SysUISingleton public class VisualStabilityCoordinator implements Coordinator { private final DelayableExecutor mDelayableExecutor; diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/dagger/CoordinatorsModule.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/dagger/CoordinatorsModule.kt new file mode 100644 index 000000000000..a26d50d2a059 --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/dagger/CoordinatorsModule.kt @@ -0,0 +1,65 @@ +/* + * Copyright (C) 2021 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.systemui.statusbar.notification.collection.coordinator.dagger + +import com.android.systemui.dagger.SysUISingleton +import com.android.systemui.statusbar.notification.collection.coordinator.NotifCoordinators +import com.android.systemui.statusbar.notification.collection.coordinator.NotifCoordinatorsImpl +import com.android.systemui.statusbar.notification.collection.coordinator.SensitiveContentCoordinatorModule +import dagger.Binds +import dagger.Module +import dagger.Provides +import dagger.Subcomponent +import javax.inject.Qualifier +import javax.inject.Scope + +@Module(subcomponents = [CoordinatorsSubcomponent::class]) +object CoordinatorsModule { + @SysUISingleton + @JvmStatic + @Provides + fun notifCoordinators(factory: CoordinatorsSubcomponent.Factory): NotifCoordinators = + factory.create().notifCoordinators +} + +@CoordinatorScope +@Subcomponent(modules = [InternalCoordinatorsModule::class]) +interface CoordinatorsSubcomponent { + @get:Internal val notifCoordinators: NotifCoordinators + + @Subcomponent.Factory + interface Factory { + fun create(): CoordinatorsSubcomponent + } +} + +@Module(includes = [SensitiveContentCoordinatorModule::class]) +private abstract class InternalCoordinatorsModule { + @Binds + @Internal + abstract fun bindNotifCoordinators(impl: NotifCoordinatorsImpl): NotifCoordinators +} + +@Qualifier +@MustBeDocumented +@Retention(AnnotationRetention.RUNTIME) +private annotation class Internal + +@Scope +@MustBeDocumented +@Retention(AnnotationRetention.RUNTIME) +annotation class CoordinatorScope
\ No newline at end of file diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/listbuilder/ShadeListBuilderLogger.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/listbuilder/ShadeListBuilderLogger.kt index 5a35127397b4..8fff90504798 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/listbuilder/ShadeListBuilderLogger.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/listbuilder/ShadeListBuilderLogger.kt @@ -47,6 +47,15 @@ class ShadeListBuilderLogger @Inject constructor( }) } + fun logPreRenderInvalidated(filterName: String, pipelineState: Int) { + buffer.log(TAG, DEBUG, { + str1 = filterName + int1 = pipelineState + }, { + """Pre-render Invalidator "$str1" invalidated; pipeline state is $int1""" + }) + } + fun logPreGroupFilterInvalidated(filterName: String, pipelineState: Int) { buffer.log(TAG, DEBUG, { str1 = filterName diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/listbuilder/pluggable/Invalidator.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/listbuilder/pluggable/Invalidator.java new file mode 100644 index 000000000000..d7092ecd536e --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/listbuilder/pluggable/Invalidator.java @@ -0,0 +1,24 @@ +/* + * Copyright (C) 2021 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.systemui.statusbar.notification.collection.listbuilder.pluggable; + +/** A {@link Pluggable} that can only invalidate. */ +public abstract class Invalidator extends Pluggable<Invalidator> { + protected Invalidator(String name) { + super(name); + } +} diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/dagger/NotificationsModule.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/dagger/NotificationsModule.java index 540216cc662b..1eb007e345ec 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/dagger/NotificationsModule.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/dagger/NotificationsModule.java @@ -47,6 +47,7 @@ import com.android.systemui.statusbar.notification.collection.NotifInflaterImpl; import com.android.systemui.statusbar.notification.collection.NotifPipeline; import com.android.systemui.statusbar.notification.collection.coordinator.ShadeEventCoordinator; import com.android.systemui.statusbar.notification.collection.coordinator.VisualStabilityCoordinator; +import com.android.systemui.statusbar.notification.collection.coordinator.dagger.CoordinatorsModule; import com.android.systemui.statusbar.notification.collection.inflation.NotifInflater; import com.android.systemui.statusbar.notification.collection.inflation.NotificationRowBinder; import com.android.systemui.statusbar.notification.collection.inflation.OnUserInteractionCallbackImpl; @@ -93,7 +94,10 @@ import dagger.Provides; /** * Dagger Module for classes found within the com.android.systemui.statusbar.notification package. */ -@Module(includes = {NotificationSectionHeadersModule.class}) +@Module(includes = { + NotificationSectionHeadersModule.class, + CoordinatorsModule.class +}) public interface NotificationsModule { @Binds StackScrollAlgorithm.SectionProvider bindSectionProvider( diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarNotificationPresenter.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarNotificationPresenter.java index cf9b2c6775b8..ecd5c985154c 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarNotificationPresenter.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarNotificationPresenter.java @@ -314,9 +314,7 @@ public class StatusBarNotificationPresenter implements NotificationPresenter, mShadeController.addPostCollapseAction(() -> updateNotificationViews(reason)); return; } - mViewHierarchyManager.updateNotificationViews(); - mNotificationPanel.updateNotificationViews(reason); } diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationViewHierarchyManagerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationViewHierarchyManagerTest.java index 7fb7b8667a1b..cf58c63e3d26 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationViewHierarchyManagerTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationViewHierarchyManagerTest.java @@ -36,6 +36,7 @@ import android.widget.LinearLayout; import androidx.test.filters.SmallTest; import com.android.systemui.SysuiTestCase; +import com.android.systemui.flags.FeatureFlags; import com.android.systemui.plugins.statusbar.NotificationSwipeActionHelper; import com.android.systemui.statusbar.notification.AssistantFeedbackController; import com.android.systemui.statusbar.notification.DynamicChildBindController; @@ -75,6 +76,7 @@ public class NotificationViewHierarchyManagerTest extends SysuiTestCase { @Spy private FakeListContainer mListContainer = new FakeListContainer(); // Dependency mocks: + @Mock private FeatureFlags mFeatureFlags; @Mock private NotificationEntryManager mEntryManager; @Mock private NotificationLockscreenUserManager mLockscreenUserManager; @Mock private NotificationGroupManagerLegacy mGroupManager; @@ -101,10 +103,14 @@ public class NotificationViewHierarchyManagerTest extends SysuiTestCase { when(mVisualStabilityManager.areGroupChangesAllowed()).thenReturn(true); when(mVisualStabilityManager.isReorderingAllowed()).thenReturn(true); + when(mFeatureFlags.isNewNotifPipelineEnabled()).thenReturn(false); + when(mFeatureFlags.checkLegacyPipelineEnabled()).thenReturn(true); + mHelper = new NotificationTestHelper(mContext, mDependency, TestableLooper.get(this)); mViewHierarchyManager = new NotificationViewHierarchyManager(mContext, - mHandler, mLockscreenUserManager, mGroupManager, mVisualStabilityManager, + mHandler, mFeatureFlags, mLockscreenUserManager, mGroupManager, + mVisualStabilityManager, mock(StatusBarStateControllerImpl.class), mEntryManager, mock(KeyguardBypassController.class), Optional.of(mock(Bubbles.class)), diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/ShadeListBuilderTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/ShadeListBuilderTest.java index e9e6718f5a5c..190c3521e83c 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/ShadeListBuilderTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/ShadeListBuilderTest.java @@ -56,6 +56,7 @@ import com.android.systemui.statusbar.notification.collection.listbuilder.OnBefo import com.android.systemui.statusbar.notification.collection.listbuilder.OnBeforeSortListener; import com.android.systemui.statusbar.notification.collection.listbuilder.OnBeforeTransformGroupsListener; import com.android.systemui.statusbar.notification.collection.listbuilder.ShadeListBuilderLogger; +import com.android.systemui.statusbar.notification.collection.listbuilder.pluggable.Invalidator; import com.android.systemui.statusbar.notification.collection.listbuilder.pluggable.NotifComparator; import com.android.systemui.statusbar.notification.collection.listbuilder.pluggable.NotifFilter; import com.android.systemui.statusbar.notification.collection.listbuilder.pluggable.NotifPromoter; @@ -847,11 +848,13 @@ public class ShadeListBuilderTest extends SysuiTestCase { NotifPromoter idPromoter = new IdPromoter(4); NotifSectioner section = new PackageSectioner(PACKAGE_1); NotifComparator hypeComparator = new HypeComparator(PACKAGE_2); + Invalidator preRenderInvalidator = new Invalidator("PreRenderInvalidator") {}; mListBuilder.addPreGroupFilter(packageFilter); mListBuilder.addPromoter(idPromoter); mListBuilder.setSectioners(singletonList(section)); mListBuilder.setComparators(singletonList(hypeComparator)); + mListBuilder.addPreRenderInvalidator(preRenderInvalidator); // GIVEN a set of random notifs addNotif(0, PACKAGE_1); @@ -876,6 +879,10 @@ public class ShadeListBuilderTest extends SysuiTestCase { clearInvocations(mOnRenderListListener); hypeComparator.invalidateList(); verify(mOnRenderListListener).onRenderList(anyList()); + + clearInvocations(mOnRenderListListener); + preRenderInvalidator.invalidateList(); + verify(mOnRenderListListener).onRenderList(anyList()); } @Test diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/SensitiveContentCoordinatorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/SensitiveContentCoordinatorTest.kt new file mode 100644 index 000000000000..5fd4174af164 --- /dev/null +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/SensitiveContentCoordinatorTest.kt @@ -0,0 +1,209 @@ +/* + * Copyright (C) 2021 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.systemui.statusbar.notification.collection.coordinator + +import android.os.UserHandle +import android.service.notification.StatusBarNotification +import androidx.test.filters.SmallTest +import com.android.systemui.SysuiTestCase +import com.android.systemui.statusbar.NotificationLockscreenUserManager +import com.android.systemui.statusbar.notification.DynamicPrivacyController +import com.android.systemui.statusbar.notification.collection.ListEntry +import com.android.systemui.statusbar.notification.collection.NotifPipeline +import com.android.systemui.statusbar.notification.collection.NotificationEntry +import com.android.systemui.statusbar.notification.collection.listbuilder.OnBeforeRenderListListener +import com.android.systemui.statusbar.notification.collection.listbuilder.pluggable.Invalidator +import com.android.systemui.statusbar.notification.collection.listbuilder.pluggable.Pluggable +import com.android.systemui.util.mockito.withArgCaptor +import com.android.systemui.util.mockito.mock +import org.junit.Test +import org.mockito.Mockito.verify +import org.mockito.Mockito.`when` as whenever + +@SmallTest +class SensitiveContentCoordinatorTest : SysuiTestCase() { + + val dynamicPrivacyController: DynamicPrivacyController = mock() + val lockscreenUserManager: NotificationLockscreenUserManager = mock() + val pipeline: NotifPipeline = mock() + + val coordinator: SensitiveContentCoordinator = SensitiveContentCoordinatorModule + .provideCoordinator(dynamicPrivacyController, lockscreenUserManager) + + @Test + fun onDynamicPrivacyChanged_invokeInvalidationListener() { + coordinator.attach(pipeline) + val invalidator = withArgCaptor<Invalidator> { + verify(pipeline).addPreRenderInvalidator(capture()) + } + val dynamicPrivacyListener = withArgCaptor<DynamicPrivacyController.Listener> { + verify(dynamicPrivacyController).addListener(capture()) + } + + val invalidationListener = mock<Pluggable.PluggableListener<Invalidator>>() + invalidator.setInvalidationListener(invalidationListener) + + dynamicPrivacyListener.onDynamicPrivacyChanged() + + verify(invalidationListener).onPluggableInvalidated(invalidator) + } + + @Test + fun onBeforeRenderList_deviceUnlocked_notifDoesNotNeedRedaction() { + coordinator.attach(pipeline) + val onBeforeRenderListListener = withArgCaptor<OnBeforeRenderListListener> { + verify(pipeline).addOnBeforeRenderListListener(capture()) + } + + whenever(lockscreenUserManager.currentUserId).thenReturn(1) + whenever(lockscreenUserManager.isLockscreenPublicMode(1)).thenReturn(false) + whenever(lockscreenUserManager.userAllowsPrivateNotificationsInPublic(1)).thenReturn(true) + whenever(dynamicPrivacyController.isDynamicallyUnlocked).thenReturn(false) + val entry = fakeNotification(1, false) + + onBeforeRenderListListener.onBeforeRenderList(listOf(entry)) + + verify(entry.representativeEntry!!).setSensitive(false, false) + } + + @Test + fun onBeforeRenderList_deviceUnlocked_notifWouldNeedRedaction() { + coordinator.attach(pipeline) + val onBeforeRenderListListener = withArgCaptor<OnBeforeRenderListListener> { + verify(pipeline).addOnBeforeRenderListListener(capture()) + } + + whenever(lockscreenUserManager.currentUserId).thenReturn(1) + whenever(lockscreenUserManager.isLockscreenPublicMode(1)).thenReturn(false) + whenever(lockscreenUserManager.userAllowsPrivateNotificationsInPublic(1)).thenReturn(true) + whenever(dynamicPrivacyController.isDynamicallyUnlocked).thenReturn(false) + val entry = fakeNotification(1, true) + + onBeforeRenderListListener.onBeforeRenderList(listOf(entry)) + + verify(entry.representativeEntry!!).setSensitive(false, false) + } + + @Test + fun onBeforeRenderList_deviceLocked_userAllowsPublicNotifs() { + coordinator.attach(pipeline) + val onBeforeRenderListListener = withArgCaptor<OnBeforeRenderListListener> { + verify(pipeline).addOnBeforeRenderListListener(capture()) + } + + whenever(lockscreenUserManager.currentUserId).thenReturn(1) + whenever(lockscreenUserManager.isLockscreenPublicMode(1)).thenReturn(true) + whenever(lockscreenUserManager.userAllowsPrivateNotificationsInPublic(1)).thenReturn(true) + whenever(dynamicPrivacyController.isDynamicallyUnlocked).thenReturn(false) + val entry = fakeNotification(1, false) + + onBeforeRenderListListener.onBeforeRenderList(listOf(entry)) + + verify(entry.representativeEntry!!).setSensitive(false, false) + } + + @Test + fun onBeforeRenderList_deviceLocked_userDisallowsPublicNotifs_notifDoesNotNeedRedaction() { + coordinator.attach(pipeline) + val onBeforeRenderListListener = withArgCaptor<OnBeforeRenderListListener> { + verify(pipeline).addOnBeforeRenderListListener(capture()) + } + + whenever(lockscreenUserManager.currentUserId).thenReturn(1) + whenever(lockscreenUserManager.isLockscreenPublicMode(1)).thenReturn(true) + whenever(lockscreenUserManager.userAllowsPrivateNotificationsInPublic(1)).thenReturn(false) + whenever(dynamicPrivacyController.isDynamicallyUnlocked).thenReturn(false) + val entry = fakeNotification(1, false) + + onBeforeRenderListListener.onBeforeRenderList(listOf(entry)) + + verify(entry.representativeEntry!!).setSensitive(false, true) + } + + @Test + fun onBeforeRenderList_deviceLocked_notifNeedsRedaction() { + coordinator.attach(pipeline) + val onBeforeRenderListListener = withArgCaptor<OnBeforeRenderListListener> { + verify(pipeline).addOnBeforeRenderListListener(capture()) + } + + whenever(lockscreenUserManager.currentUserId).thenReturn(1) + whenever(lockscreenUserManager.isLockscreenPublicMode(1)).thenReturn(true) + whenever(lockscreenUserManager.userAllowsPrivateNotificationsInPublic(1)).thenReturn(false) + whenever(dynamicPrivacyController.isDynamicallyUnlocked).thenReturn(false) + val entry = fakeNotification(1, true) + + onBeforeRenderListListener.onBeforeRenderList(listOf(entry)) + + verify(entry.representativeEntry!!).setSensitive(true, true) + } + + @Test + fun onBeforeRenderList_deviceDynamicallyUnlocked_notifNeedsRedaction() { + coordinator.attach(pipeline) + val onBeforeRenderListListener = withArgCaptor<OnBeforeRenderListListener> { + verify(pipeline).addOnBeforeRenderListListener(capture()) + } + + whenever(lockscreenUserManager.currentUserId).thenReturn(1) + whenever(lockscreenUserManager.isLockscreenPublicMode(1)).thenReturn(true) + whenever(lockscreenUserManager.userAllowsPrivateNotificationsInPublic(1)).thenReturn(false) + whenever(dynamicPrivacyController.isDynamicallyUnlocked).thenReturn(true) + val entry = fakeNotification(1, true) + + onBeforeRenderListListener.onBeforeRenderList(listOf(entry)) + + verify(entry.representativeEntry!!).setSensitive(false, true) + } + + @Test + fun onBeforeRenderList_deviceDynamicallyUnlocked_notifUserNeedsWorkChallenge() { + coordinator.attach(pipeline) + val onBeforeRenderListListener = withArgCaptor<OnBeforeRenderListListener> { + verify(pipeline).addOnBeforeRenderListListener(capture()) + } + + whenever(lockscreenUserManager.currentUserId).thenReturn(1) + whenever(lockscreenUserManager.isLockscreenPublicMode(1)).thenReturn(true) + whenever(lockscreenUserManager.userAllowsPrivateNotificationsInPublic(1)).thenReturn(false) + whenever(dynamicPrivacyController.isDynamicallyUnlocked).thenReturn(true) + whenever(lockscreenUserManager.needsSeparateWorkChallenge(2)).thenReturn(true) + + val entry = fakeNotification(2, true) + + onBeforeRenderListListener.onBeforeRenderList(listOf(entry)) + + verify(entry.representativeEntry!!).setSensitive(true, true) + } + + private fun fakeNotification(notifUserId: Int, needsRedaction: Boolean): ListEntry { + val mockUserHandle = mock<UserHandle>().apply { + whenever(identifier).thenReturn(notifUserId) + } + val mockSbn: StatusBarNotification = mock<StatusBarNotification>().apply { + whenever(user).thenReturn(mockUserHandle) + } + val mockEntry = mock<NotificationEntry>().apply { + whenever(sbn).thenReturn(mockSbn) + } + whenever(lockscreenUserManager.needsRedaction(mockEntry)).thenReturn(needsRedaction) + whenever(mockEntry.rowExists()).thenReturn(true) + return object : ListEntry("key", 0) { + override fun getRepresentativeEntry(): NotificationEntry = mockEntry + } + } +}
\ No newline at end of file |