Merge changes from topic "b204127880_pipeline_backport_2" into sc-v2-dev

* changes:
  New Pipeline: Disable legacy dynamic privacy listener
  Fix Coordinators failing to build with gradle build in sysui-studio
  Enforce Coordinator encapsulation w/ Dagger
  Add sensitive content redaction to notif pipeline
This commit is contained in:
Jeff DeCew
2021-11-01 19:21:51 +00:00
committed by Android (Google) Code Review
32 changed files with 650 additions and 155 deletions

View File

@@ -18,6 +18,8 @@ package com.android.systemui.flags;
import android.content.Context; import android.content.Context;
import android.util.FeatureFlagUtils; import android.util.FeatureFlagUtils;
import android.util.Log;
import android.widget.Toast;
import com.android.internal.annotations.VisibleForTesting; import com.android.internal.annotations.VisibleForTesting;
import com.android.systemui.R; 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() { public boolean isNewNotifPipelineEnabled() {
return isEnabled(Flags.NEW_NOTIFICATION_PIPELINE); return isEnabled(Flags.NEW_NOTIFICATION_PIPELINE);
} }

View File

@@ -27,6 +27,7 @@ import android.view.ViewGroup;
import com.android.systemui.R; import com.android.systemui.R;
import com.android.systemui.dagger.qualifiers.Main; import com.android.systemui.dagger.qualifiers.Main;
import com.android.systemui.flags.FeatureFlags;
import com.android.systemui.plugins.statusbar.StatusBarStateController; import com.android.systemui.plugins.statusbar.StatusBarStateController;
import com.android.systemui.statusbar.dagger.StatusBarModule; import com.android.systemui.statusbar.dagger.StatusBarModule;
import com.android.systemui.statusbar.notification.AssistantFeedbackController; import com.android.systemui.statusbar.notification.AssistantFeedbackController;
@@ -72,6 +73,7 @@ public class NotificationViewHierarchyManager implements DynamicPrivacyControlle
// Dependencies: // Dependencies:
private final DynamicChildBindController mDynamicChildBindController; private final DynamicChildBindController mDynamicChildBindController;
private final FeatureFlags mFeatureFlags;
protected final NotificationLockscreenUserManager mLockscreenUserManager; protected final NotificationLockscreenUserManager mLockscreenUserManager;
protected final NotificationGroupManagerLegacy mGroupManager; protected final NotificationGroupManagerLegacy mGroupManager;
protected final VisualStabilityManager mVisualStabilityManager; protected final VisualStabilityManager mVisualStabilityManager;
@@ -107,6 +109,7 @@ public class NotificationViewHierarchyManager implements DynamicPrivacyControlle
public NotificationViewHierarchyManager( public NotificationViewHierarchyManager(
Context context, Context context,
@Main Handler mainHandler, @Main Handler mainHandler,
FeatureFlags featureFlags,
NotificationLockscreenUserManager notificationLockscreenUserManager, NotificationLockscreenUserManager notificationLockscreenUserManager,
NotificationGroupManagerLegacy groupManager, NotificationGroupManagerLegacy groupManager,
VisualStabilityManager visualStabilityManager, VisualStabilityManager visualStabilityManager,
@@ -121,6 +124,7 @@ public class NotificationViewHierarchyManager implements DynamicPrivacyControlle
AssistantFeedbackController assistantFeedbackController) { AssistantFeedbackController assistantFeedbackController) {
mContext = context; mContext = context;
mHandler = mainHandler; mHandler = mainHandler;
mFeatureFlags = featureFlags;
mLockscreenUserManager = notificationLockscreenUserManager; mLockscreenUserManager = notificationLockscreenUserManager;
mBypassController = bypassController; mBypassController = bypassController;
mGroupManager = groupManager; mGroupManager = groupManager;
@@ -142,7 +146,9 @@ public class NotificationViewHierarchyManager implements DynamicPrivacyControlle
NotificationListContainer listContainer) { NotificationListContainer listContainer) {
mPresenter = presenter; mPresenter = presenter;
mListContainer = listContainer; 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 //TODO: Rewrite this to focus on Entries, or some other data object instead of views
public void updateNotificationViews() { public void updateNotificationViews() {
Assert.isMainThread(); Assert.isMainThread();
if (!mFeatureFlags.checkLegacyPipelineEnabled()) {
return;
}
beginUpdate(); beginUpdate();
List<NotificationEntry> activeNotifications = mEntryManager.getVisibleNotifications(); List<NotificationEntry> activeNotifications = mEntryManager.getVisibleNotifications();
@@ -425,6 +435,10 @@ public class NotificationViewHierarchyManager implements DynamicPrivacyControlle
*/ */
public void updateRowStates() { public void updateRowStates() {
Assert.isMainThread(); Assert.isMainThread();
if (!mFeatureFlags.checkLegacyPipelineEnabled()) {
return;
}
beginUpdate(); beginUpdate();
updateRowStatesInternal(); updateRowStatesInternal();
endUpdate(); endUpdate();
@@ -510,6 +524,7 @@ public class NotificationViewHierarchyManager implements DynamicPrivacyControlle
@Override @Override
public void onDynamicPrivacyChanged() { public void onDynamicPrivacyChanged() {
mFeatureFlags.assertLegacyPipelineEnabled();
if (mPerformingUpdate) { if (mPerformingUpdate) {
Log.w(TAG, "onDynamicPrivacyChanged made a re-entrant call"); Log.w(TAG, "onDynamicPrivacyChanged made a re-entrant call");
} }

View File

@@ -184,6 +184,7 @@ public interface StatusBarDependenciesModule {
static NotificationViewHierarchyManager provideNotificationViewHierarchyManager( static NotificationViewHierarchyManager provideNotificationViewHierarchyManager(
Context context, Context context,
@Main Handler mainHandler, @Main Handler mainHandler,
FeatureFlags featureFlags,
NotificationLockscreenUserManager notificationLockscreenUserManager, NotificationLockscreenUserManager notificationLockscreenUserManager,
NotificationGroupManagerLegacy groupManager, NotificationGroupManagerLegacy groupManager,
VisualStabilityManager visualStabilityManager, VisualStabilityManager visualStabilityManager,
@@ -199,6 +200,7 @@ public interface StatusBarDependenciesModule {
return new NotificationViewHierarchyManager( return new NotificationViewHierarchyManager(
context, context,
mainHandler, mainHandler,
featureFlags,
notificationLockscreenUserManager, notificationLockscreenUserManager,
groupManager, groupManager,
visualStabilityManager, visualStabilityManager,

View File

@@ -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.OnBeforeRenderListListener;
import com.android.systemui.statusbar.notification.collection.listbuilder.OnBeforeSortListener; 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.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.NotifComparator;
import com.android.systemui.statusbar.notification.collection.listbuilder.pluggable.NotifFilter; import com.android.systemui.statusbar.notification.collection.listbuilder.pluggable.NotifFilter;
import com.android.systemui.statusbar.notification.collection.listbuilder.pluggable.NotifPromoter; import com.android.systemui.statusbar.notification.collection.listbuilder.pluggable.NotifPromoter;
@@ -216,6 +217,11 @@ public class NotifPipeline implements CommonNotifCollection {
mShadeListBuilder.addOnBeforeRenderListListener(listener); 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 * 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 * are currently present in the shade. If this method is called during pipeline execution it

View File

@@ -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.OnBeforeTransformGroupsListener;
import com.android.systemui.statusbar.notification.collection.listbuilder.PipelineState; 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.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.NotifComparator;
import com.android.systemui.statusbar.notification.collection.listbuilder.pluggable.NotifFilter; import com.android.systemui.statusbar.notification.collection.listbuilder.pluggable.NotifFilter;
import com.android.systemui.statusbar.notification.collection.listbuilder.pluggable.NotifPromoter; import com.android.systemui.statusbar.notification.collection.listbuilder.pluggable.NotifPromoter;
@@ -174,6 +175,13 @@ public class ShadeListBuilder implements Dumpable {
mOnBeforeRenderListListeners.add(listener); mOnBeforeRenderListListeners.add(listener);
} }
void addPreRenderInvalidator(Invalidator invalidator) {
Assert.isMainThread();
mPipelineState.requireState(STATE_IDLE);
invalidator.setInvalidationListener(this::onPreRenderInvalidated);
}
void addPreGroupFilter(NotifFilter filter) { void addPreGroupFilter(NotifFilter filter) {
Assert.isMainThread(); Assert.isMainThread();
mPipelineState.requireState(STATE_IDLE); 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) { private void onPreGroupFilterInvalidated(NotifFilter filter) {
Assert.isMainThread(); Assert.isMainThread();

View File

@@ -23,11 +23,11 @@ import android.service.notification.StatusBarNotification;
import com.android.systemui.ForegroundServiceController; import com.android.systemui.ForegroundServiceController;
import com.android.systemui.appops.AppOpsController; import com.android.systemui.appops.AppOpsController;
import com.android.systemui.dagger.SysUISingleton;
import com.android.systemui.dagger.qualifiers.Main; import com.android.systemui.dagger.qualifiers.Main;
import com.android.systemui.statusbar.notification.collection.ListEntry; import com.android.systemui.statusbar.notification.collection.ListEntry;
import com.android.systemui.statusbar.notification.collection.NotifPipeline; import com.android.systemui.statusbar.notification.collection.NotifPipeline;
import com.android.systemui.statusbar.notification.collection.NotificationEntry; 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.NotifFilter;
import com.android.systemui.statusbar.notification.collection.listbuilder.pluggable.NotifSectioner; import com.android.systemui.statusbar.notification.collection.listbuilder.pluggable.NotifSectioner;
import com.android.systemui.statusbar.notification.stack.NotificationPriorityBucketKt; 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/ForegroundServiceNotificationListener
* frameworks/base/packages/SystemUI/src/com/android/systemui/ForegroundServiceLifetimeExtender * frameworks/base/packages/SystemUI/src/com/android/systemui/ForegroundServiceLifetimeExtender
*/ */
@SysUISingleton @CoordinatorScope
public class AppOpsCoordinator implements Coordinator { public class AppOpsCoordinator implements Coordinator {
private static final String TAG = "AppOpsCoordinator"; private static final String TAG = "AppOpsCoordinator";

View File

@@ -16,10 +16,10 @@
package com.android.systemui.statusbar.notification.collection.coordinator; 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.NotifCollection;
import com.android.systemui.statusbar.notification.collection.NotifPipeline; import com.android.systemui.statusbar.notification.collection.NotifPipeline;
import com.android.systemui.statusbar.notification.collection.NotificationEntry; 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.NotifFilter;
import com.android.systemui.statusbar.notification.collection.notifcollection.DismissedByUserStats; import com.android.systemui.statusbar.notification.collection.notifcollection.DismissedByUserStats;
import com.android.systemui.statusbar.notification.collection.notifcollection.NotifDismissInterceptor; 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). * respond to app-cancellations (ie: remove the bubble if the app cancels the notification).
* *
*/ */
@SysUISingleton @CoordinatorScope
public class BubbleCoordinator implements Coordinator { public class BubbleCoordinator implements Coordinator {
private static final String TAG = "BubbleCoordinator"; private static final String TAG = "BubbleCoordinator";

View File

@@ -16,10 +16,10 @@
package com.android.systemui.statusbar.notification.collection.coordinator 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.ListEntry
import com.android.systemui.statusbar.notification.collection.NotifPipeline import com.android.systemui.statusbar.notification.collection.NotifPipeline
import com.android.systemui.statusbar.notification.collection.NotificationEntry 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.NotifPromoter
import com.android.systemui.statusbar.notification.collection.listbuilder.pluggable.NotifSectioner import com.android.systemui.statusbar.notification.collection.listbuilder.pluggable.NotifSectioner
import com.android.systemui.statusbar.notification.collection.render.NodeController import com.android.systemui.statusbar.notification.collection.render.NodeController
@@ -34,7 +34,7 @@ import javax.inject.Inject
* - Elevates important conversation notifications * - Elevates important conversation notifications
* - Puts conversations into its own people section. @see [NotifCoordinators] for section ordering. * - Puts conversations into its own people section. @see [NotifCoordinators] for section ordering.
*/ */
@SysUISingleton @CoordinatorScope
class ConversationCoordinator @Inject constructor( class ConversationCoordinator @Inject constructor(
private val peopleNotificationIdentifier: PeopleNotificationIdentifier, private val peopleNotificationIdentifier: PeopleNotificationIdentifier,
@PeopleHeader peopleHeaderController: NodeController @PeopleHeader peopleHeaderController: NodeController

View File

@@ -23,9 +23,9 @@ import android.content.pm.PackageManager;
import android.os.RemoteException; import android.os.RemoteException;
import android.service.notification.StatusBarNotification; 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.NotifPipeline;
import com.android.systemui.statusbar.notification.collection.NotificationEntry; 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.NotifFilter;
import com.android.systemui.statusbar.policy.DeviceProvisionedController; 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 * Special notifications with extra permissions and tags won't be filtered out even when the
* device is unprovisioned. * device is unprovisioned.
*/ */
@SysUISingleton @CoordinatorScope
public class DeviceProvisionedCoordinator implements Coordinator { public class DeviceProvisionedCoordinator implements Coordinator {
private static final String TAG = "DeviceProvisionedCoordinator"; private static final String TAG = "DeviceProvisionedCoordinator";

View File

@@ -17,17 +17,17 @@ package com.android.systemui.statusbar.notification.collection.coordinator
import android.util.ArraySet import android.util.ArraySet
import com.android.systemui.Dumpable import com.android.systemui.Dumpable
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.dump.DumpManager import com.android.systemui.dump.DumpManager
import com.android.systemui.statusbar.notification.collection.ListEntry import com.android.systemui.statusbar.notification.collection.ListEntry
import com.android.systemui.statusbar.notification.collection.NotifPipeline import com.android.systemui.statusbar.notification.collection.NotifPipeline
import com.android.systemui.statusbar.notification.collection.NotificationEntry 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
import com.android.systemui.statusbar.notification.collection.notifcollection.NotifLifetimeExtender.OnEndLifetimeExtensionCallback 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.NotifGutsViewListener
import com.android.systemui.statusbar.notification.collection.render.NotifGutsViewManager 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.FileDescriptor
import java.io.PrintWriter import java.io.PrintWriter
import javax.inject.Inject import javax.inject.Inject
@@ -38,7 +38,7 @@ private const val TAG = "GutsCoordinator"
* Coordinates the guts displayed by the [NotificationGutsManager] with the pipeline. * Coordinates the guts displayed by the [NotificationGutsManager] with the pipeline.
* Specifically, this just adds the lifetime extension necessary to keep guts from disappearing. * Specifically, this just adds the lifetime extension necessary to keep guts from disappearing.
*/ */
@SysUISingleton @CoordinatorScope
class GutsCoordinator @Inject constructor( class GutsCoordinator @Inject constructor(
private val notifGutsViewManager: NotifGutsViewManager, private val notifGutsViewManager: NotifGutsViewManager,
private val logger: GutsCoordinatorLogger, private val logger: GutsCoordinatorLogger,

View File

@@ -22,11 +22,11 @@ import static com.android.systemui.statusbar.notification.interruption.HeadsUpCo
import androidx.annotation.NonNull; import androidx.annotation.NonNull;
import androidx.annotation.Nullable; import androidx.annotation.Nullable;
import com.android.systemui.dagger.SysUISingleton;
import com.android.systemui.statusbar.NotificationRemoteInputManager; import com.android.systemui.statusbar.NotificationRemoteInputManager;
import com.android.systemui.statusbar.notification.collection.ListEntry; import com.android.systemui.statusbar.notification.collection.ListEntry;
import com.android.systemui.statusbar.notification.collection.NotifPipeline; import com.android.systemui.statusbar.notification.collection.NotifPipeline;
import com.android.systemui.statusbar.notification.collection.NotificationEntry; 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.NotifPromoter;
import com.android.systemui.statusbar.notification.collection.listbuilder.pluggable.NotifSectioner; import com.android.systemui.statusbar.notification.collection.listbuilder.pluggable.NotifSectioner;
import com.android.systemui.statusbar.notification.collection.notifcollection.NotifCollectionListener; 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. * Note: The inflation callback in {@link PreparationCoordinator} handles showing HUNs.
*/ */
@SysUISingleton @CoordinatorScope
public class HeadsUpCoordinator implements Coordinator { public class HeadsUpCoordinator implements Coordinator {
private static final String TAG = "HeadsUpCoordinator"; private static final String TAG = "HeadsUpCoordinator";

View File

@@ -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.NotifPipeline;
import com.android.systemui.statusbar.notification.collection.NotificationEntry; 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.NotifFilter;
import javax.inject.Inject;
/** /**
* Filters out notifications that have been dismissed locally (by the user) but that system server * Filters out notifications that have been dismissed locally (by the user) but that system server
* hasn't yet confirmed the removal of. * hasn't yet confirmed the removal of.
*/ */
@CoordinatorScope
public class HideLocallyDismissedNotifsCoordinator implements Coordinator { public class HideLocallyDismissedNotifsCoordinator implements Coordinator {
@Inject
HideLocallyDismissedNotifsCoordinator() { }
@Override @Override
public void attach(NotifPipeline pipeline) { public void attach(NotifPipeline pipeline) {
pipeline.addPreGroupFilter(mFilter); pipeline.addPreGroupFilter(mFilter);

View File

@@ -23,6 +23,7 @@ import com.android.systemui.statusbar.NotificationLockscreenUserManager;
import com.android.systemui.statusbar.NotificationLockscreenUserManager.UserChangedListener; import com.android.systemui.statusbar.NotificationLockscreenUserManager.UserChangedListener;
import com.android.systemui.statusbar.notification.collection.NotifPipeline; import com.android.systemui.statusbar.notification.collection.NotifPipeline;
import com.android.systemui.statusbar.notification.collection.NotificationEntry; 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.NotifFilter;
import javax.inject.Inject; import javax.inject.Inject;
@@ -37,6 +38,7 @@ import javax.inject.Inject;
* TODO: The NotificationLockscreenUserManager currently maintains the list of active user profiles. * TODO: The NotificationLockscreenUserManager currently maintains the list of active user profiles.
* We should spin that off into a standalone section at some point. * We should spin that off into a standalone section at some point.
*/ */
@CoordinatorScope
public class HideNotifsForOtherUsersCoordinator implements Coordinator { public class HideNotifsForOtherUsersCoordinator implements Coordinator {
private final NotificationLockscreenUserManager mLockscreenUserManager; private final NotificationLockscreenUserManager mLockscreenUserManager;
private final SharedCoordinatorLogger mLogger; private final SharedCoordinatorLogger mLogger;

View File

@@ -34,13 +34,13 @@ import androidx.annotation.MainThread;
import com.android.keyguard.KeyguardUpdateMonitor; import com.android.keyguard.KeyguardUpdateMonitor;
import com.android.keyguard.KeyguardUpdateMonitorCallback; import com.android.keyguard.KeyguardUpdateMonitorCallback;
import com.android.systemui.broadcast.BroadcastDispatcher; import com.android.systemui.broadcast.BroadcastDispatcher;
import com.android.systemui.dagger.SysUISingleton;
import com.android.systemui.plugins.statusbar.StatusBarStateController; import com.android.systemui.plugins.statusbar.StatusBarStateController;
import com.android.systemui.statusbar.NotificationLockscreenUserManager; import com.android.systemui.statusbar.NotificationLockscreenUserManager;
import com.android.systemui.statusbar.notification.collection.GroupEntry; import com.android.systemui.statusbar.notification.collection.GroupEntry;
import com.android.systemui.statusbar.notification.collection.ListEntry; import com.android.systemui.statusbar.notification.collection.ListEntry;
import com.android.systemui.statusbar.notification.collection.NotifPipeline; import com.android.systemui.statusbar.notification.collection.NotifPipeline;
import com.android.systemui.statusbar.notification.collection.NotificationEntry; 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.NotifFilter;
import com.android.systemui.statusbar.notification.collection.provider.HighPriorityProvider; import com.android.systemui.statusbar.notification.collection.provider.HighPriorityProvider;
import com.android.systemui.statusbar.policy.KeyguardStateController; 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. * Filters low priority and privacy-sensitive notifications from the lockscreen.
*/ */
@SysUISingleton @CoordinatorScope
public class KeyguardCoordinator implements Coordinator { public class KeyguardCoordinator implements Coordinator {
private static final String TAG = "KeyguardCoordinator"; private static final String TAG = "KeyguardCoordinator";

View File

@@ -21,6 +21,7 @@ import static com.android.systemui.media.MediaDataManagerKt.isMediaNotification;
import com.android.systemui.media.MediaFeatureFlag; import com.android.systemui.media.MediaFeatureFlag;
import com.android.systemui.statusbar.notification.collection.NotifPipeline; import com.android.systemui.statusbar.notification.collection.NotifPipeline;
import com.android.systemui.statusbar.notification.collection.NotificationEntry; 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.NotifFilter;
import javax.inject.Inject; import javax.inject.Inject;
@@ -28,6 +29,7 @@ import javax.inject.Inject;
/** /**
* Coordinates hiding (filtering) of media notifications. * Coordinates hiding (filtering) of media notifications.
*/ */
@CoordinatorScope
public class MediaCoordinator implements Coordinator { public class MediaCoordinator implements Coordinator {
private static final String TAG = "MediaCoordinator"; private static final String TAG = "MediaCoordinator";

View File

@@ -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());
}
}
}

View File

@@ -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"
}
}

View File

@@ -57,6 +57,7 @@ import javax.inject.Inject;
* If a notification was uninflated, this coordinator will filter the notification out from the * If a notification was uninflated, this coordinator will filter the notification out from the
* {@link ShadeListBuilder} until it is inflated. * {@link ShadeListBuilder} until it is inflated.
*/ */
// TODO(b/204468557): Move to @CoordinatorScope
@SysUISingleton @SysUISingleton
public class PreparationCoordinator implements Coordinator { public class PreparationCoordinator implements Coordinator {
private static final String TAG = "PreparationCoordinator"; private static final String TAG = "PreparationCoordinator";

View File

@@ -19,11 +19,11 @@ package com.android.systemui.statusbar.notification.collection.coordinator;
import android.annotation.NonNull; import android.annotation.NonNull;
import android.annotation.Nullable; import android.annotation.Nullable;
import com.android.systemui.dagger.SysUISingleton;
import com.android.systemui.plugins.statusbar.StatusBarStateController; import com.android.systemui.plugins.statusbar.StatusBarStateController;
import com.android.systemui.statusbar.notification.collection.ListEntry; import com.android.systemui.statusbar.notification.collection.ListEntry;
import com.android.systemui.statusbar.notification.collection.NotifPipeline; import com.android.systemui.statusbar.notification.collection.NotifPipeline;
import com.android.systemui.statusbar.notification.collection.NotificationEntry; 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.NotifFilter;
import com.android.systemui.statusbar.notification.collection.listbuilder.pluggable.NotifSectioner; import com.android.systemui.statusbar.notification.collection.listbuilder.pluggable.NotifSectioner;
import com.android.systemui.statusbar.notification.collection.provider.HighPriorityProvider; 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 the notification's app is suspended or hiding its notifications
* - whether DND settings are hiding notifications from ambient display or the notification list * - whether DND settings are hiding notifications from ambient display or the notification list
*/ */
@SysUISingleton @CoordinatorScope
public class RankingCoordinator implements Coordinator { public class RankingCoordinator implements Coordinator {
public static final boolean SHOW_ALL_SECTIONS = false; public static final boolean SHOW_ALL_SECTIONS = false;
private final StatusBarStateController mStatusBarStateController; private final StatusBarStateController mStatusBarStateController;

View File

@@ -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))
}
}

View File

@@ -29,6 +29,7 @@ import javax.inject.Inject
* A coordinator which provides callbacks to a view surfaces for various events relevant to the * 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. * shade, such as when the user removes a notification, or when the shade is emptied.
*/ */
// TODO(b/204468557): Move to @CoordinatorScope
@SysUISingleton @SysUISingleton
class ShadeEventCoordinator @Inject internal constructor( class ShadeEventCoordinator @Inject internal constructor(
private val mLogger: ShadeEventCoordinatorLogger private val mLogger: ShadeEventCoordinatorLogger

View File

@@ -18,7 +18,6 @@ package com.android.systemui.statusbar.notification.collection.coordinator
import android.app.smartspace.SmartspaceTarget import android.app.smartspace.SmartspaceTarget
import android.os.Parcelable import android.os.Parcelable
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.dagger.qualifiers.Main import com.android.systemui.dagger.qualifiers.Main
import com.android.systemui.plugins.statusbar.StatusBarStateController import com.android.systemui.plugins.statusbar.StatusBarStateController
import com.android.systemui.statusbar.NotificationLockscreenUserManager 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.NotificationEntryManager
import com.android.systemui.statusbar.notification.collection.NotifPipeline import com.android.systemui.statusbar.notification.collection.NotifPipeline
import com.android.systemui.statusbar.notification.collection.NotificationEntry 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.NotifFilter
import com.android.systemui.statusbar.notification.collection.notifcollection.NotifCollectionListener import com.android.systemui.statusbar.notification.collection.notifcollection.NotifCollectionListener
import com.android.systemui.util.concurrency.DelayableExecutor 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 // This class is a singleton so that the same instance can be accessed by both the old and new
// pipelines // pipelines
@SysUISingleton @CoordinatorScope
class SmartspaceDedupingCoordinator @Inject constructor( class SmartspaceDedupingCoordinator @Inject constructor(
private val statusBarStateController: SysuiStatusBarStateController, private val statusBarStateController: SysuiStatusBarStateController,
private val smartspaceController: LockscreenSmartspaceController, private val smartspaceController: LockscreenSmartspaceController,

View File

@@ -19,11 +19,11 @@ package com.android.systemui.statusbar.notification.collection.coordinator
import com.android.internal.widget.MessagingGroup import com.android.internal.widget.MessagingGroup
import com.android.internal.widget.MessagingMessage import com.android.internal.widget.MessagingMessage
import com.android.keyguard.KeyguardUpdateMonitor import com.android.keyguard.KeyguardUpdateMonitor
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.flags.FeatureFlags import com.android.systemui.flags.FeatureFlags
import com.android.systemui.statusbar.NotificationLockscreenUserManager.UserChangedListener import com.android.systemui.statusbar.NotificationLockscreenUserManager.UserChangedListener
import com.android.systemui.statusbar.NotificationLockscreenUserManagerImpl import com.android.systemui.statusbar.NotificationLockscreenUserManagerImpl
import com.android.systemui.statusbar.notification.collection.NotifPipeline 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.notification.row.NotificationGutsManager
import com.android.systemui.statusbar.policy.ConfigurationController import com.android.systemui.statusbar.policy.ConfigurationController
import javax.inject.Inject 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 * for the current uiMode and screen properties; additionally deferring those changes when a user
* change is in progress until that process has completed. * change is in progress until that process has completed.
*/ */
@SysUISingleton @CoordinatorScope
class ViewConfigCoordinator @Inject internal constructor( class ViewConfigCoordinator @Inject internal constructor(
configurationController: ConfigurationController, configurationController: ConfigurationController,
lockscreenUserManager: NotificationLockscreenUserManagerImpl, lockscreenUserManager: NotificationLockscreenUserManagerImpl,

View File

@@ -50,6 +50,7 @@ import javax.inject.Inject;
* This is now integrated in the data-layer via * This is now integrated in the data-layer via
* {@link com.android.systemui.statusbar.notification.collection.ShadeListBuilder}. * {@link com.android.systemui.statusbar.notification.collection.ShadeListBuilder}.
*/ */
// TODO(b/204468557): Move to @CoordinatorScope
@SysUISingleton @SysUISingleton
public class VisualStabilityCoordinator implements Coordinator { public class VisualStabilityCoordinator implements Coordinator {
private final DelayableExecutor mDelayableExecutor; private final DelayableExecutor mDelayableExecutor;

View File

@@ -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

View File

@@ -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) { fun logPreGroupFilterInvalidated(filterName: String, pipelineState: Int) {
buffer.log(TAG, DEBUG, { buffer.log(TAG, DEBUG, {
str1 = filterName str1 = filterName

View File

@@ -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);
}
}

View File

@@ -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.NotifPipeline;
import com.android.systemui.statusbar.notification.collection.coordinator.ShadeEventCoordinator; 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.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.NotifInflater;
import com.android.systemui.statusbar.notification.collection.inflation.NotificationRowBinder; import com.android.systemui.statusbar.notification.collection.inflation.NotificationRowBinder;
import com.android.systemui.statusbar.notification.collection.inflation.OnUserInteractionCallbackImpl; 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. * 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 { public interface NotificationsModule {
@Binds @Binds
StackScrollAlgorithm.SectionProvider bindSectionProvider( StackScrollAlgorithm.SectionProvider bindSectionProvider(

View File

@@ -314,9 +314,7 @@ public class StatusBarNotificationPresenter implements NotificationPresenter,
mShadeController.addPostCollapseAction(() -> updateNotificationViews(reason)); mShadeController.addPostCollapseAction(() -> updateNotificationViews(reason));
return; return;
} }
mViewHierarchyManager.updateNotificationViews(); mViewHierarchyManager.updateNotificationViews();
mNotificationPanel.updateNotificationViews(reason); mNotificationPanel.updateNotificationViews(reason);
} }

View File

@@ -36,6 +36,7 @@ import android.widget.LinearLayout;
import androidx.test.filters.SmallTest; import androidx.test.filters.SmallTest;
import com.android.systemui.SysuiTestCase; import com.android.systemui.SysuiTestCase;
import com.android.systemui.flags.FeatureFlags;
import com.android.systemui.plugins.statusbar.NotificationSwipeActionHelper; import com.android.systemui.plugins.statusbar.NotificationSwipeActionHelper;
import com.android.systemui.statusbar.notification.AssistantFeedbackController; import com.android.systemui.statusbar.notification.AssistantFeedbackController;
import com.android.systemui.statusbar.notification.DynamicChildBindController; import com.android.systemui.statusbar.notification.DynamicChildBindController;
@@ -75,6 +76,7 @@ public class NotificationViewHierarchyManagerTest extends SysuiTestCase {
@Spy private FakeListContainer mListContainer = new FakeListContainer(); @Spy private FakeListContainer mListContainer = new FakeListContainer();
// Dependency mocks: // Dependency mocks:
@Mock private FeatureFlags mFeatureFlags;
@Mock private NotificationEntryManager mEntryManager; @Mock private NotificationEntryManager mEntryManager;
@Mock private NotificationLockscreenUserManager mLockscreenUserManager; @Mock private NotificationLockscreenUserManager mLockscreenUserManager;
@Mock private NotificationGroupManagerLegacy mGroupManager; @Mock private NotificationGroupManagerLegacy mGroupManager;
@@ -101,10 +103,14 @@ public class NotificationViewHierarchyManagerTest extends SysuiTestCase {
when(mVisualStabilityManager.areGroupChangesAllowed()).thenReturn(true); when(mVisualStabilityManager.areGroupChangesAllowed()).thenReturn(true);
when(mVisualStabilityManager.isReorderingAllowed()).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)); mHelper = new NotificationTestHelper(mContext, mDependency, TestableLooper.get(this));
mViewHierarchyManager = new NotificationViewHierarchyManager(mContext, mViewHierarchyManager = new NotificationViewHierarchyManager(mContext,
mHandler, mLockscreenUserManager, mGroupManager, mVisualStabilityManager, mHandler, mFeatureFlags, mLockscreenUserManager, mGroupManager,
mVisualStabilityManager,
mock(StatusBarStateControllerImpl.class), mEntryManager, mock(StatusBarStateControllerImpl.class), mEntryManager,
mock(KeyguardBypassController.class), mock(KeyguardBypassController.class),
Optional.of(mock(Bubbles.class)), Optional.of(mock(Bubbles.class)),

View File

@@ -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.OnBeforeSortListener;
import com.android.systemui.statusbar.notification.collection.listbuilder.OnBeforeTransformGroupsListener; 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.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.NotifComparator;
import com.android.systemui.statusbar.notification.collection.listbuilder.pluggable.NotifFilter; import com.android.systemui.statusbar.notification.collection.listbuilder.pluggable.NotifFilter;
import com.android.systemui.statusbar.notification.collection.listbuilder.pluggable.NotifPromoter; import com.android.systemui.statusbar.notification.collection.listbuilder.pluggable.NotifPromoter;
@@ -847,11 +848,13 @@ public class ShadeListBuilderTest extends SysuiTestCase {
NotifPromoter idPromoter = new IdPromoter(4); NotifPromoter idPromoter = new IdPromoter(4);
NotifSectioner section = new PackageSectioner(PACKAGE_1); NotifSectioner section = new PackageSectioner(PACKAGE_1);
NotifComparator hypeComparator = new HypeComparator(PACKAGE_2); NotifComparator hypeComparator = new HypeComparator(PACKAGE_2);
Invalidator preRenderInvalidator = new Invalidator("PreRenderInvalidator") {};
mListBuilder.addPreGroupFilter(packageFilter); mListBuilder.addPreGroupFilter(packageFilter);
mListBuilder.addPromoter(idPromoter); mListBuilder.addPromoter(idPromoter);
mListBuilder.setSectioners(singletonList(section)); mListBuilder.setSectioners(singletonList(section));
mListBuilder.setComparators(singletonList(hypeComparator)); mListBuilder.setComparators(singletonList(hypeComparator));
mListBuilder.addPreRenderInvalidator(preRenderInvalidator);
// GIVEN a set of random notifs // GIVEN a set of random notifs
addNotif(0, PACKAGE_1); addNotif(0, PACKAGE_1);
@@ -876,6 +879,10 @@ public class ShadeListBuilderTest extends SysuiTestCase {
clearInvocations(mOnRenderListListener); clearInvocations(mOnRenderListListener);
hypeComparator.invalidateList(); hypeComparator.invalidateList();
verify(mOnRenderListListener).onRenderList(anyList()); verify(mOnRenderListListener).onRenderList(anyList());
clearInvocations(mOnRenderListListener);
preRenderInvalidator.invalidateList();
verify(mOnRenderListListener).onRenderList(anyList());
} }
@Test @Test

View File

@@ -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
}
}
}