From 63122cbde2b2204f8797b74d4945cef80b9f59e2 Mon Sep 17 00:00:00 2001 From: Steve Elliott Date: Tue, 15 Feb 2022 12:09:02 -0500 Subject: [PATCH] Don't show "Clear All" w/ redacted notifs The logic that determined when to show the Clear All Silent button in the Silent notification header was occurring when the Silent (and Minimized) sections update. At this time, there was no up-to-date information about whether or not the notification has senstive content (SensitiveContentCoordinator occurs later in the pipeline), and no information about whether or not sensitive content can even be shown or must be redacted (handled by NotificationStackScrollLayoutController). Additionally, the visibility of the Clear All button in the shade footer wasn't being adjusted at all under the new pipeline. To remedy this, the following changes were made in this CL: 1. `boolean NotificationEntry#isSensitive()` has been dismantled. Rather than relying on some separate code to properly set this value at some point, instead the concept of "Sensitive Content" is derived directly from the SBN + Ranking, and is kept separate from the state of the user's settings, as well as the state of the lockscreen (whether we would currently need to redact). 2. NotificationLockscreenUserMananger is the de-facto place for tracking and querying redaction state. This includes checking if the user's settings *would* require redaction (#needsRedactionInPublic), and whether a user is actually public (#isLockscreenPublicMode). 3. In order to keep clients in sync, a new observable interface is exposed on NotificationLockscreenUserManager that clients can use to be notified when to requery #needsRedactionInPublic, due to user settings changes. This replaces NotificationEntry#OnSensitivityChangedListener 4. SensitiveContentCoordinator has been removed entirely; the pipeline no longer needs to invoke NotificationEntry#setSenstive, since it no longer exists. 5. NotifStats has been updated to include additional information extracted from the NotifCollection necessary to dynamically query for redaction in the View layer, without needed to re-process the entire list; specifically, we track a Set of users that currently have sensitive notifications in the shade. This set is tested against the user's settings (tracked separately from the pipeline in NotifLockscreenUserManager). Test: atest Test: 1. Turn on notification redaction (no sensitive contents on lockscreen) 2. Receive a silent sensitive notification 3. Lock the device 4. Double tap camera 5. expand shade Observe: Clear all hidden in footer and in silent header Fixes: 213443266 Fixes: 230581048 Change-Id: Ibc66576d28a48f7c82cad875ed2dcb57a7b23f10 Merged-In: Ibc66576d28a48f7c82cad875ed2dcb57a7b23f10 --- .../statusbar/HeadsUpStatusBarView.java | 27 +- .../LockscreenShadeTransitionController.kt | 25 +- .../NotificationLockscreenUserManager.java | 13 +- ...NotificationLockscreenUserManagerImpl.java | 145 +++++----- .../NotificationViewHierarchyManager.java | 28 +- .../DynamicPrivacyController.java | 6 +- .../collection/NotificationEntry.java | 51 ++-- .../coordinator/NotifCoordinators.kt | 2 - .../coordinator/RankingCoordinator.java | 147 ++++------ .../SensitiveContentCoordinator.kt | 124 -------- .../coordinator/StackCoordinator.kt | 13 +- .../coordinator/dagger/CoordinatorsModule.kt | 2 - .../inflation/NotificationRowBinderImpl.java | 13 +- .../collection/render/NotifStackController.kt | 14 +- .../NotificationSectionHeadersModule.kt | 2 +- .../notification/icon/IconManager.kt | 31 +- .../row/ExpandableNotificationRow.java | 47 ++- .../ExpandableNotificationRowController.java | 19 +- .../row/NotificationContentView.java | 36 ++- .../row/RowContentBindParams.java | 3 +- ...tificationStackScrollLayoutController.java | 63 +++- .../phone/HeadsUpAppearanceController.java | 38 ++- .../NotificationPanelViewController.java | 5 - .../phone/StatusBarNotificationPresenter.java | 6 +- ...LockscreenShadeTransitionControllerTest.kt | 8 +- ...NotificationLockscreenUserManagerTest.java | 36 ++- .../DynamicPrivacyControllerTest.java | 4 +- .../coordinator/RankingCoordinatorTest.java | 65 ----- .../SensitiveContentCoordinatorTest.kt | 268 ------------------ .../coordinator/StackCoordinatorTest.kt | 33 ++- .../notification/icon/IconManagerTest.kt | 63 ++-- .../row/ExpandableNotificationRowTest.java | 8 +- ...NotificationEntryManagerInflationTest.java | 6 +- .../row/NotificationTestHelper.java | 4 +- .../HeadsUpAppearanceControllerTest.java | 18 +- 35 files changed, 570 insertions(+), 803 deletions(-) delete mode 100644 packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/SensitiveContentCoordinator.kt delete mode 100644 packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/SensitiveContentCoordinatorTest.kt diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/HeadsUpStatusBarView.java b/packages/SystemUI/src/com/android/systemui/statusbar/HeadsUpStatusBarView.java index 4d933d9ad21ee..e44d334c776d0 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/HeadsUpStatusBarView.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/HeadsUpStatusBarView.java @@ -29,7 +29,6 @@ import com.android.keyguard.AlphaOptimizedLinearLayout; import com.android.systemui.R; import com.android.systemui.plugins.DarkIconDispatcher; import com.android.systemui.statusbar.notification.collection.NotificationEntry; -import com.android.systemui.statusbar.notification.collection.NotificationEntry.OnSensitivityChangedListener; import java.util.ArrayList; @@ -49,6 +48,7 @@ public class HeadsUpStatusBarView extends AlphaOptimizedLinearLayout { private TextView mTextView; private NotificationEntry mShowingEntry; private Runnable mOnDrawingRectChangedListener; + private boolean mRedactSensitiveContent; public HeadsUpStatusBarView(Context context) { this(context, null); @@ -111,29 +111,28 @@ public class HeadsUpStatusBarView extends AlphaOptimizedLinearLayout { } public void setEntry(NotificationEntry entry) { - if (mShowingEntry != null) { - mShowingEntry.removeOnSensitivityChangedListener(mOnSensitivityChangedListener); - } mShowingEntry = entry; - if (mShowingEntry != null) { CharSequence text = entry.headsUpStatusBarText; - if (entry.isSensitive()) { + if (mRedactSensitiveContent && entry.hasSensitiveContents()) { text = entry.headsUpStatusBarTextPublic; } mTextView.setText(text); - mShowingEntry.addOnSensitivityChangedListener(mOnSensitivityChangedListener); } } - private final OnSensitivityChangedListener mOnSensitivityChangedListener = entry -> { - if (entry != mShowingEntry) { - throw new IllegalStateException("Got a sensitivity change for " + entry - + " but mShowingEntry is " + mShowingEntry); + public void setRedactSensitiveContent(boolean redactSensitiveContent) { + if (mRedactSensitiveContent == redactSensitiveContent) { + return; } - // Update the text - setEntry(entry); - }; + mRedactSensitiveContent = redactSensitiveContent; + if (mShowingEntry != null && mShowingEntry.hasSensitiveContents()) { + mTextView.setText( + mRedactSensitiveContent + ? mShowingEntry.headsUpStatusBarTextPublic + : mShowingEntry.headsUpStatusBarText); + } + } @Override protected void onLayout(boolean changed, int l, int t, int r, int b) { diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/LockscreenShadeTransitionController.kt b/packages/SystemUI/src/com/android/systemui/statusbar/LockscreenShadeTransitionController.kt index c1ea6bf7cec87..cbe722b6f82fd 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/LockscreenShadeTransitionController.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/LockscreenShadeTransitionController.kt @@ -386,7 +386,7 @@ class LockscreenShadeTransitionController @Inject constructor( } if (view is ExpandableNotificationRow) { // Only drag down on sensitive views, otherwise the ExpandHelper will take this - return view.entry.isSensitive + return lockScreenUserManager.notifNeedsRedactionInPublic(view.entry) } } return false @@ -552,7 +552,8 @@ class LockscreenShadeTransitionController @Inject constructor( logger.logShadeDisabledOnGoToLockedShade() return } - var userId: Int = lockScreenUserManager.getCurrentUserId() + val currentUser = lockScreenUserManager.currentUserId + var userId: Int = currentUser var entry: NotificationEntry? = null if (expandView is ExpandableNotificationRow) { entry = expandView.entry @@ -562,12 +563,18 @@ class LockscreenShadeTransitionController @Inject constructor( entry.setGroupExpansionChanging(true) userId = entry.sbn.userId } - var fullShadeNeedsBouncer = (!lockScreenUserManager.userAllowsPrivateNotificationsInPublic( - lockScreenUserManager.getCurrentUserId()) || - !lockScreenUserManager.shouldShowLockscreenNotifications() || - falsingCollector.shouldEnforceBouncer()) - if (keyguardBypassController.bypassEnabled) { - fullShadeNeedsBouncer = false + val fullShadeNeedsBouncer = when { + // No bouncer necessary if we're bypassing + keyguardBypassController.bypassEnabled -> false + // Redacted notificationss are present, bouncer should be shown before un-redacting in + // the full shade + lockScreenUserManager.sensitiveNotifsNeedRedactionInPublic(currentUser) -> true + // Notifications are hidden in public, bouncer should be shown before showing them in + // the full shade + !lockScreenUserManager.shouldShowLockscreenNotifications() -> true + // Bouncer is being enforced, so we need to show it + falsingCollector.shouldEnforceBouncer() -> true + else -> false } if (lockScreenUserManager.isLockscreenPublicMode(userId) && fullShadeNeedsBouncer) { statusBarStateController.setLeaveOpenOnKeyguardHide(true) @@ -911,4 +918,4 @@ class DragDownHelper( host.getLocationOnScreen(temp2) return expandCallback.getChildAtRawPosition(x + temp2[0], y + temp2[1]) } -} \ No newline at end of file +} diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationLockscreenUserManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationLockscreenUserManager.java index 9a1a144924e2f..5fd9671bda012 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationLockscreenUserManager.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationLockscreenUserManager.java @@ -71,17 +71,22 @@ public interface NotificationLockscreenUserManager { boolean shouldHideNotifications(String key); boolean shouldShowOnKeyguard(NotificationEntry entry); + void addOnNeedsRedactionInPublicChangedListener(Runnable listener); + + void removeOnNeedsRedactionInPublicChangedListener(Runnable listener); + boolean isAnyProfilePublicMode(); void updatePublicMode(); - boolean needsRedaction(NotificationEntry entry); + /** Does this notification require redaction if it is displayed when the device is public? */ + boolean notifNeedsRedactionInPublic(NotificationEntry entry); /** - * Has the given user chosen to allow their private (full) notifications to be shown even - * when the lockscreen is in "public" (secure & locked) mode? + * Do all sensitive notifications belonging to the given user require redaction when they are + * displayed in public? */ - boolean userAllowsPrivateNotificationsInPublic(int currentUserId); + boolean sensitiveNotifsNeedRedactionInPublic(int userId); /** * Has the given user chosen to allow notifications to be shown even when the lockscreen is in diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationLockscreenUserManagerImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationLockscreenUserManagerImpl.java index 56e09f03aa6bb..334cfe5f4c410 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationLockscreenUserManagerImpl.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationLockscreenUserManagerImpl.java @@ -24,7 +24,6 @@ import static com.android.systemui.statusbar.notification.stack.NotificationPrio import android.app.ActivityManager; import android.app.KeyguardManager; -import android.app.Notification; import android.app.NotificationManager; import android.app.admin.DevicePolicyManager; import android.content.BroadcastReceiver; @@ -45,7 +44,6 @@ import android.util.SparseBooleanArray; import com.android.internal.statusbar.NotificationVisibility; import com.android.internal.widget.LockPatternUtils; import com.android.keyguard.KeyguardUpdateMonitor; -import com.android.systemui.Dependency; import com.android.systemui.Dumpable; import com.android.systemui.broadcast.BroadcastDispatcher; import com.android.systemui.dagger.SysUISingleton; @@ -60,6 +58,7 @@ import com.android.systemui.statusbar.notification.collection.notifcollection.Co import com.android.systemui.statusbar.notification.collection.render.NotificationVisibilityProvider; import com.android.systemui.statusbar.policy.DeviceProvisionedController; import com.android.systemui.statusbar.policy.KeyguardStateController; +import com.android.systemui.util.ListenerSet; import com.android.systemui.util.settings.SecureSettings; import java.io.PrintWriter; @@ -85,13 +84,12 @@ public class NotificationLockscreenUserManagerImpl implements private final DeviceProvisionedController mDeviceProvisionedController; private final KeyguardStateController mKeyguardStateController; private final SecureSettings mSecureSettings; + private final KeyguardUpdateMonitor mKeyguardUpdateMonitor; + private final Lazy mOverviewProxyService; private final Object mLock = new Object(); - - // Lazy - private NotificationEntryManager mEntryManager; - private final Lazy mVisibilityProviderLazy; private final Lazy mCommonNotifCollectionLazy; + private final Lazy mEntryManagerLazy; private final DevicePolicyManager mDevicePolicyManager; private final SparseBooleanArray mLockscreenPublicMode = new SparseBooleanArray(); private final SparseBooleanArray mUsersWithSeparateWorkChallenge = new SparseBooleanArray(); @@ -103,13 +101,14 @@ public class NotificationLockscreenUserManagerImpl implements private final List mListeners = new ArrayList<>(); private final BroadcastDispatcher mBroadcastDispatcher; private final NotificationClickNotifier mClickNotifier; - - private boolean mShowLockscreenNotifications; - private boolean mAllowLockscreenRemoteInput; - private LockPatternUtils mLockPatternUtils; - protected KeyguardManager mKeyguardManager; - private int mState = StatusBarState.SHADE; - private List mKeyguardSuppressors = new ArrayList<>(); + private final LockPatternUtils mLockPatternUtils; + private final List mKeyguardSuppressors = new ArrayList<>(); + protected final Context mContext; + private final Handler mMainHandler; + protected final SparseArray mCurrentProfiles = new SparseArray<>(); + protected final SparseArray mCurrentManagedProfiles = new SparseArray<>(); + private final ListenerSet mOnSensitiveContentRedactionChangeListeners = + new ListenerSet<>(); protected final BroadcastReceiver mAllUsersReceiver = new BroadcastReceiver() { @Override @@ -120,7 +119,11 @@ public class NotificationLockscreenUserManagerImpl implements isCurrentProfile(getSendingUserId())) { mUsersAllowingPrivateNotifications.clear(); updateLockscreenNotificationSetting(); - getEntryManager().updateNotifications("ACTION_DEVICE_POLICY_MANAGER_STATE_CHANGED"); + for (Runnable listener : mOnSensitiveContentRedactionChangeListeners) { + listener.run(); + } + mEntryManagerLazy.get() + .updateNotifications("ACTION_DEVICE_POLICY_MANAGER_STATE_CHANGED"); } } }; @@ -142,7 +145,7 @@ public class NotificationLockscreenUserManagerImpl implements // The filtering needs to happen before the update call below in order to // make sure // the presenter has the updated notifications from the new user - getEntryManager().reapplyFilterAndSort("user switched"); + mEntryManagerLazy.get().reapplyFilterAndSort("user switched"); mPresenter.onUserSwitched(mCurrentUserId); for (UserChangedListener listener : mListeners) { @@ -156,7 +159,7 @@ public class NotificationLockscreenUserManagerImpl implements break; case Intent.ACTION_USER_UNLOCKED: // Start the overview connection to the launcher service - Dependency.get(OverviewProxyService.class).startConnectionToCurrentUser(); + mOverviewProxyService.get().startConnectionToCurrentUser(); break; case NOTIFICATION_UNLOCKED_BY_WORK_CHALLENGE_ACTION: final IntentSender intentSender = intent.getParcelableExtra( @@ -179,28 +182,26 @@ public class NotificationLockscreenUserManagerImpl implements } }; - protected final Context mContext; - private final Handler mMainHandler; - protected final SparseArray mCurrentProfiles = new SparseArray<>(); - protected final SparseArray mCurrentManagedProfiles = new SparseArray<>(); - - protected int mCurrentUserId = 0; + // Late-init protected NotificationPresenter mPresenter; protected ContentObserver mLockscreenSettingsObserver; protected ContentObserver mSettingsObserver; - private boolean mHideSilentNotificationsOnLockscreen; + protected KeyguardManager mKeyguardManager; - private NotificationEntryManager getEntryManager() { - if (mEntryManager == null) { - mEntryManager = Dependency.get(NotificationEntryManager.class); - } - return mEntryManager; - } + protected int mCurrentUserId = 0; + private int mState = StatusBarState.SHADE; + private boolean mHideSilentNotificationsOnLockscreen; + private boolean mShowLockscreenNotifications; + private boolean mAllowLockscreenRemoteInput; @Inject - public NotificationLockscreenUserManagerImpl(Context context, + public NotificationLockscreenUserManagerImpl( + Context context, BroadcastDispatcher broadcastDispatcher, DevicePolicyManager devicePolicyManager, + KeyguardUpdateMonitor keyguardUpdateMonitor, + Lazy notificationEntryManagerLazy, + Lazy overviewProxyServiceLazy, UserManager userManager, Lazy visibilityProviderLazy, Lazy commonNotifCollectionLazy, @@ -216,9 +217,11 @@ public class NotificationLockscreenUserManagerImpl implements mMainHandler = mainHandler; mDevicePolicyManager = devicePolicyManager; mUserManager = userManager; + mOverviewProxyService = overviewProxyServiceLazy; mCurrentUserId = ActivityManager.getCurrentUser(); mVisibilityProviderLazy = visibilityProviderLazy; mCommonNotifCollectionLazy = commonNotifCollectionLazy; + mEntryManagerLazy = notificationEntryManagerLazy; mClickNotifier = clickNotifier; statusBarStateController.addCallback(this); mLockPatternUtils = new LockPatternUtils(context); @@ -227,10 +230,12 @@ public class NotificationLockscreenUserManagerImpl implements mDeviceProvisionedController = deviceProvisionedController; mSecureSettings = secureSettings; mKeyguardStateController = keyguardStateController; + mKeyguardUpdateMonitor = keyguardUpdateMonitor; dumpManager.registerDumpable(this); } + @Override public void setUpWithPresenter(NotificationPresenter presenter) { mPresenter = presenter; @@ -243,7 +248,10 @@ public class NotificationLockscreenUserManagerImpl implements mUsersAllowingNotifications.clear(); // ... and refresh all the notifications updateLockscreenNotificationSetting(); - getEntryManager().updateNotifications("LOCK_SCREEN_SHOW_NOTIFICATIONS," + for (Runnable listener : mOnSensitiveContentRedactionChangeListeners) { + listener.run(); + } + mEntryManagerLazy.get().updateNotifications("LOCK_SCREEN_SHOW_NOTIFICATIONS," + " or LOCK_SCREEN_ALLOW_PRIVATE_NOTIFICATIONS change"); } }; @@ -253,7 +261,7 @@ public class NotificationLockscreenUserManagerImpl implements public void onChange(boolean selfChange) { updateLockscreenNotificationSetting(); if (mDeviceProvisionedController.isDeviceProvisioned()) { - getEntryManager().updateNotifications("LOCK_SCREEN_ALLOW_REMOTE_INPUT" + mEntryManagerLazy.get().updateNotifications("LOCK_SCREEN_ALLOW_REMOTE_INPUT" + " or ZEN_MODE change"); } } @@ -312,14 +320,17 @@ public class NotificationLockscreenUserManagerImpl implements mSettingsObserver.onChange(false); // set up } + @Override public boolean shouldShowLockscreenNotifications() { return mShowLockscreenNotifications; } + @Override public boolean shouldAllowLockscreenRemoteInput() { return mAllowLockscreenRemoteInput; } + @Override public boolean isCurrentProfile(int userId) { synchronized (mLock) { return userId == UserHandle.USER_ALL || mCurrentProfiles.get(userId) != null; @@ -334,7 +345,7 @@ public class NotificationLockscreenUserManagerImpl implements if (userId == UserHandle.USER_ALL) { userId = mCurrentUserId; } - boolean inLockdown = Dependency.get(KeyguardUpdateMonitor.class).isUserInLockdown(userId); + boolean inLockdown = mKeyguardUpdateMonitor.isUserInLockdown(userId); mUsersInLockdownLatestResult.put(userId, inLockdown); return inLockdown; } @@ -343,6 +354,7 @@ public class NotificationLockscreenUserManagerImpl implements * Returns true if we're on a secure lockscreen and the user wants to hide notification data. * If so, notifications should be hidden. */ + @Override public boolean shouldHideNotifications(int userId) { boolean hide = isLockscreenPublicMode(userId) && !userAllowsNotificationsInPublic(userId) || (userId != mCurrentUserId && shouldHideNotifications(mCurrentUserId)) @@ -355,6 +367,7 @@ public class NotificationLockscreenUserManagerImpl implements * Returns true if we're on a secure lockscreen and the user wants to hide notifications via * package-specific override. */ + @Override public boolean shouldHideNotifications(String key) { if (mCommonNotifCollectionLazy.get() == null) { Log.wtf(TAG, "mCommonNotifCollectionLazy was null!", new Throwable()); @@ -365,6 +378,7 @@ public class NotificationLockscreenUserManagerImpl implements && visibleEntry.getRanking().getLockscreenVisibilityOverride() == VISIBILITY_SECRET; } + @Override public boolean shouldShowOnKeyguard(NotificationEntry entry) { if (mCommonNotifCollectionLazy.get() == null) { Log.wtf(TAG, "mCommonNotifCollectionLazy was null!", new Throwable()); @@ -387,14 +401,6 @@ public class NotificationLockscreenUserManagerImpl implements return mShowLockscreenNotifications && exceedsPriorityThreshold; } - private void setShowLockscreenNotifications(boolean show) { - mShowLockscreenNotifications = show; - } - - private void setLockscreenAllowRemoteInput(boolean allowLockscreenRemoteInput) { - mAllowLockscreenRemoteInput = allowLockscreenRemoteInput; - } - protected void updateLockscreenNotificationSetting() { final boolean show = mSecureSettings.getIntForUser( Settings.Secure.LOCK_SCREEN_SHOW_NOTIFICATIONS, @@ -408,7 +414,7 @@ public class NotificationLockscreenUserManagerImpl implements mHideSilentNotificationsOnLockscreen = mSecureSettings.getIntForUser( Settings.Secure.LOCK_SCREEN_SHOW_SILENT_NOTIFICATIONS, 1, mCurrentUserId) == 0; - setShowLockscreenNotifications(show && allowedByDpm); + mShowLockscreenNotifications = show && allowedByDpm; if (ENABLE_LOCK_SCREEN_ALLOW_REMOTE_INPUT) { final boolean remoteInput = mSecureSettings.getIntForUser( @@ -418,9 +424,9 @@ public class NotificationLockscreenUserManagerImpl implements final boolean remoteInputDpm = (dpmFlags & DevicePolicyManager.KEYGUARD_DISABLE_REMOTE_INPUT) == 0; - setLockscreenAllowRemoteInput(remoteInput && remoteInputDpm); + mAllowLockscreenRemoteInput = remoteInput && remoteInputDpm; } else { - setLockscreenAllowRemoteInput(false); + mAllowLockscreenRemoteInput = false; } } @@ -428,7 +434,7 @@ public class NotificationLockscreenUserManagerImpl implements * Has the given user chosen to allow their private (full) notifications to be shown even * when the lockscreen is in "public" (secure & locked) mode? */ - public boolean userAllowsPrivateNotificationsInPublic(int userHandle) { + protected boolean userAllowsPrivateNotificationsInPublic(int userHandle) { if (userHandle == UserHandle.USER_ALL) { return true; } @@ -473,10 +479,12 @@ public class NotificationLockscreenUserManagerImpl implements /** * Save the current "public" (locked and secure) state of the lockscreen. */ + @Override public void setLockscreenPublicMode(boolean publicMode, int userId) { mLockscreenPublicMode.put(userId, publicMode); } + @Override public boolean isLockscreenPublicMode(int userId) { if (userId == UserHandle.USER_ALL) { return mLockscreenPublicMode.get(mCurrentUserId, false); @@ -493,6 +501,7 @@ public class NotificationLockscreenUserManagerImpl implements * Has the given user chosen to allow notifications to be shown even when the lockscreen is in * "public" (secure & locked) mode? */ + @Override public boolean userAllowsNotificationsInPublic(int userHandle) { if (isCurrentProfile(userHandle) && userHandle != mCurrentUserId) { return true; @@ -513,36 +522,37 @@ public class NotificationLockscreenUserManagerImpl implements } /** @return true if the entry needs redaction when on the lockscreen. */ - public boolean needsRedaction(NotificationEntry ent) { + @Override + public boolean notifNeedsRedactionInPublic(NotificationEntry ent) { int userId = ent.getSbn().getUserId(); + return ent.hasSensitiveContents() && sensitiveNotifsNeedRedactionInPublic(userId); + } + @Override + public boolean sensitiveNotifsNeedRedactionInPublic(int userId) { boolean isCurrentUserRedactingNotifs = !userAllowsPrivateNotificationsInPublic(mCurrentUserId); + if (userId == mCurrentUserId) { + return isCurrentUserRedactingNotifs; + } + boolean isNotifForManagedProfile = mCurrentManagedProfiles.contains(userId); boolean isNotifUserRedacted = !userAllowsPrivateNotificationsInPublic(userId); // redact notifications if the current user is redacting notifications; however if the // notification is associated with a managed profile, we rely on the managed profile // setting to determine whether to redact it - boolean isNotifRedacted = (!isNotifForManagedProfile && isCurrentUserRedactingNotifs) - || isNotifUserRedacted; - - boolean notificationRequestsRedaction = - ent.getSbn().getNotification().visibility == Notification.VISIBILITY_PRIVATE; - boolean userForcesRedaction = packageHasVisibilityOverride(ent.getSbn().getKey()); - - return userForcesRedaction || notificationRequestsRedaction && isNotifRedacted; + return (!isNotifForManagedProfile && isCurrentUserRedactingNotifs) || isNotifUserRedacted; } - private boolean packageHasVisibilityOverride(String key) { - if (mCommonNotifCollectionLazy.get() == null) { - Log.wtf(TAG, "mEntryManager was null!", new Throwable()); - return true; - } - NotificationEntry entry = mCommonNotifCollectionLazy.get().getEntry(key); - return entry != null - && entry.getRanking().getLockscreenVisibilityOverride() - == Notification.VISIBILITY_PRIVATE; + @Override + public void addOnNeedsRedactionInPublicChangedListener(Runnable listener) { + mOnSensitiveContentRedactionChangeListeners.addIfAbsent(listener); + } + + @Override + public void removeOnNeedsRedactionInPublicChangedListener(Runnable listener) { + mOnSensitiveContentRedactionChangeListeners.remove(listener); } private void updateCurrentProfilesCache() { @@ -562,12 +572,16 @@ public class NotificationLockscreenUserManagerImpl implements for (UserChangedListener listener : mListeners) { listener.onCurrentProfilesChanged(mCurrentProfiles); } + for (Runnable listener : mOnSensitiveContentRedactionChangeListeners) { + listener.run(); + } }); } /** * If any of the profiles are in public mode. */ + @Override public boolean isAnyProfilePublicMode() { synchronized (mLock) { for (int i = mCurrentProfiles.size() - 1; i >= 0; i--) { @@ -596,10 +610,12 @@ public class NotificationLockscreenUserManagerImpl implements /** * Returns the current user id. This can change if the user is switched. */ + @Override public int getCurrentUserId() { return mCurrentUserId; } + @Override public SparseArray getCurrentProfiles() { return mCurrentProfiles; } @@ -640,7 +656,8 @@ public class NotificationLockscreenUserManagerImpl implements setLockscreenPublicMode(isProfilePublic, userId); mUsersWithSeparateWorkChallenge.put(userId, needsSeparateChallenge); } - getEntryManager().updateNotifications("NotificationLockscreenUserManager.updatePublicMode"); + mEntryManagerLazy.get() + .updateNotifications("NotificationLockscreenUserManager.updatePublicMode"); } @Override diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationViewHierarchyManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationViewHierarchyManager.java index 054543c7d2b18..30cb09d56f172 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationViewHierarchyManager.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationViewHierarchyManager.java @@ -53,6 +53,7 @@ import com.android.wm.shell.bubbles.Bubbles; import java.util.ArrayList; import java.util.HashMap; +import java.util.HashSet; import java.util.List; import java.util.Optional; import java.util.Stack; @@ -207,12 +208,9 @@ public class NotificationViewHierarchyManager implements DynamicPrivacyControlle || !mLockscreenUserManager.needsSeparateWorkChallenge(userId))) { userPublic = false; } - boolean needsRedaction = mLockscreenUserManager.needsRedaction(ent); + boolean needsRedaction = mLockscreenUserManager.notifNeedsRedactionInPublic(ent); boolean sensitive = userPublic && needsRedaction; - boolean deviceSensitive = devicePublic - && !mLockscreenUserManager.userAllowsPrivateNotificationsInPublic( - currentUserId); - ent.setSensitive(sensitive, deviceSensitive); + ent.getRow().setSensitive(sensitive); ent.getRow().setNeedsRedaction(needsRedaction); mLowPriorityInflationHelper.recheckLowPriorityViewAndInflate(ent, ent.getRow()); boolean isChildInGroup = mGroupManager.isChildInGroup(ent); @@ -365,6 +363,8 @@ public class NotificationViewHierarchyManager implements DynamicPrivacyControlle boolean hasClearableAlertingNotifs = false; boolean hasNonClearableSilentNotifs = false; boolean hasClearableSilentNotifs = false; + HashSet clearableAlertingSensitiveNotifUsers = new HashSet<>(); + HashSet clearableSilentSensitiveNotifUsers = new HashSet<>(); final int childCount = mListContainer.getContainerChildCount(); int visibleTopLevelEntries = 0; for (int i = 0; i < childCount; i++) { @@ -376,10 +376,11 @@ public class NotificationViewHierarchyManager implements DynamicPrivacyControlle continue; } final ExpandableNotificationRow row = (ExpandableNotificationRow) child; - boolean isSilent = row.getEntry().getBucket() == BUCKET_SILENT; + NotificationEntry entry = row.getEntry(); + boolean isSilent = entry.getBucket() == BUCKET_SILENT; // NOTE: NotificationEntry.isClearable() will internally check group children to ensure // the group itself definitively clearable. - boolean isClearable = row.getEntry().isClearable(); + boolean isClearable = entry.isClearable(); visibleTopLevelEntries++; if (isSilent) { if (isClearable) { @@ -394,13 +395,24 @@ public class NotificationViewHierarchyManager implements DynamicPrivacyControlle hasNonClearableAlertingNotifs = true; } } + if (isClearable && entry.hasSensitiveContents()) { + int userId = entry.getSbn().getUserId(); + if (isSilent) { + clearableSilentSensitiveNotifUsers.add(userId); + } else { + clearableAlertingSensitiveNotifUsers.add(userId); + } + } } mStackController.setNotifStats(new NotifStats( visibleTopLevelEntries /* numActiveNotifs */, hasNonClearableAlertingNotifs /* hasNonClearableAlertingNotifs */, hasClearableAlertingNotifs /* hasClearableAlertingNotifs */, hasNonClearableSilentNotifs /* hasNonClearableSilentNotifs */, - hasClearableSilentNotifs /* hasClearableSilentNotifs */ + hasClearableSilentNotifs /* hasClearableSilentNotifs */, + clearableAlertingSensitiveNotifUsers /* clearableAlertingSensitiveNotifUsers */, + clearableSilentSensitiveNotifUsers /* clearableSilentSensitiveNotifUsers */ + )); Trace.endSection(); } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/DynamicPrivacyController.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/DynamicPrivacyController.java index a0ccd5726c755..d16e9e5d7faf0 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/DynamicPrivacyController.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/DynamicPrivacyController.java @@ -80,7 +80,7 @@ public class DynamicPrivacyController implements KeyguardStateController.Callbac @VisibleForTesting boolean isDynamicPrivacyEnabled() { - return !mLockscreenUserManager.userAllowsPrivateNotificationsInPublic( + return mLockscreenUserManager.sensitiveNotifsNeedRedactionInPublic( mLockscreenUserManager.getCurrentUserId()); } @@ -95,6 +95,10 @@ public class DynamicPrivacyController implements KeyguardStateController.Callbac mListeners.add(listener); } + public void removeListener(Listener listener) { + mListeners.remove(listener); + } + /** * Is the notification shade currently in a locked down mode where it's fully showing but the * contents aren't revealed yet? diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotificationEntry.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotificationEntry.java index 4fc347a09292c..e3c39ddad1450 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotificationEntry.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotificationEntry.java @@ -169,9 +169,6 @@ public final class NotificationEntry extends ListEntry { */ private boolean hasSentReply; - private boolean mSensitive = true; - private List mOnSensitivityChangedListeners = new ArrayList<>(); - private boolean mAutoHeadsUp; private boolean mPulseSupressed; private int mBucket = BUCKET_ALERTING; @@ -867,33 +864,29 @@ public final class NotificationEntry extends ListEntry { } /** - * Set this notification to be sensitive. - * - * @param sensitive true if the content of this notification is sensitive right now - * @param deviceSensitive true if the device in general is sensitive right now + * Returns the visibility of this notification on the lockscreen, taking into account both the + * notification's defined visibility, as well as the visibility override as determined by the + * device policy. */ - public void setSensitive(boolean sensitive, boolean deviceSensitive) { - getRow().setSensitive(sensitive, deviceSensitive); - if (sensitive != mSensitive) { - mSensitive = sensitive; - for (int i = 0; i < mOnSensitivityChangedListeners.size(); i++) { - mOnSensitivityChangedListeners.get(i).onSensitivityChanged(this); - } + public int getLockscreenVisibility() { + int setting = mRanking.getLockscreenVisibilityOverride(); + if (setting == Ranking.VISIBILITY_NO_OVERRIDE) { + setting = mSbn.getNotification().visibility; } + return setting; } - public boolean isSensitive() { - return mSensitive; - } - - /** Add a listener to be notified when the entry's sensitivity changes. */ - public void addOnSensitivityChangedListener(OnSensitivityChangedListener listener) { - mOnSensitivityChangedListeners.add(listener); - } - - /** Remove a listener that was registered above. */ - public void removeOnSensitivityChangedListener(OnSensitivityChangedListener listener) { - mOnSensitivityChangedListeners.remove(listener); + /** + * Does this notification contain sensitive content? If the user's settings specify, then this + * content would need to be redacted when the device this public. + * + * NOTE: If the notification's visibility setting is VISIBILITY_SECRET, then this will return + * false; SECRET notifications are omitted entirely when the device is public, so effectively + * the contents of the notification are not sensitive whenever the notification is actually + * visible. + */ + public boolean hasSensitiveContents() { + return getLockscreenVisibility() == Notification.VISIBILITY_PRIVATE; } public boolean isPulseSuppressed() { @@ -954,12 +947,6 @@ public final class NotificationEntry extends ListEntry { } } - /** Listener interface for {@link #addOnSensitivityChangedListener} */ - public interface OnSensitivityChangedListener { - /** Called when the sensitivity changes */ - void onSensitivityChanged(@NonNull NotificationEntry entry); - } - /** @see #getDismissState() */ public enum DismissState { /** User has not dismissed this notif or its parent */ 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 index 3516625cc4713..b24d2922adfbb 100644 --- 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 @@ -56,7 +56,6 @@ class NotifCoordinatorsImpl @Inject constructor( smartspaceDedupingCoordinator: SmartspaceDedupingCoordinator, viewConfigCoordinator: ViewConfigCoordinator, visualStabilityCoordinator: VisualStabilityCoordinator, - sensitiveContentCoordinator: SensitiveContentCoordinator, activityLaunchAnimCoordinator: ActivityLaunchAnimCoordinator ) : NotifCoordinators { @@ -94,7 +93,6 @@ class NotifCoordinatorsImpl @Inject constructor( mCoordinators.add(shadeEventCoordinator) mCoordinators.add(viewConfigCoordinator) mCoordinators.add(visualStabilityCoordinator) - mCoordinators.add(sensitiveContentCoordinator) mCoordinators.add(activityLaunchAnimCoordinator) if (notifPipelineFlags.isSmartspaceDedupingEnabled()) { mCoordinators.add(smartspaceDedupingCoordinator) 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 57fd1975e13ac..56484010c2134 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 @@ -16,7 +16,6 @@ package com.android.systemui.statusbar.notification.collection.coordinator; -import android.annotation.NonNull; import android.annotation.Nullable; import com.android.systemui.plugins.statusbar.StatusBarStateController; @@ -29,13 +28,11 @@ import com.android.systemui.statusbar.notification.collection.listbuilder.plugga 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.render.NodeController; -import com.android.systemui.statusbar.notification.collection.render.SectionHeaderController; import com.android.systemui.statusbar.notification.dagger.AlertingHeader; import com.android.systemui.statusbar.notification.dagger.SilentHeader; import com.android.systemui.statusbar.notification.stack.NotificationPriorityBucketKt; import java.util.Collections; -import java.util.List; import javax.inject.Inject; @@ -53,10 +50,10 @@ public class RankingCoordinator implements Coordinator { private final HighPriorityProvider mHighPriorityProvider; private final SectionClassifier mSectionClassifier; private final NodeController mSilentNodeController; - private final SectionHeaderController mSilentHeaderController; private final NodeController mAlertingHeaderController; - private boolean mHasSilentEntries; - private boolean mHasMinimizedEntries; + private final AlertingNotifSectioner mAlertingNotifSectioner = new AlertingNotifSectioner(); + private final SilentNotifSectioner mSilentNotifSectioner = new SilentNotifSectioner(); + private final MinimizedNotifSectioner mMinimizedNotifSectioner = new MinimizedNotifSectioner(); @Inject public RankingCoordinator( @@ -64,14 +61,12 @@ public class RankingCoordinator implements Coordinator { HighPriorityProvider highPriorityProvider, SectionClassifier sectionClassifier, @AlertingHeader NodeController alertingHeaderController, - @SilentHeader SectionHeaderController silentHeaderController, @SilentHeader NodeController silentNodeController) { mStatusBarStateController = statusBarStateController; mHighPriorityProvider = highPriorityProvider; mSectionClassifier = sectionClassifier; mAlertingHeaderController = alertingHeaderController; mSilentNodeController = silentNodeController; - mSilentHeaderController = silentHeaderController; } @Override @@ -95,82 +90,6 @@ public class RankingCoordinator implements Coordinator { return mMinimizedNotifSectioner; } - private final NotifSectioner mAlertingNotifSectioner = new NotifSectioner("Alerting", - NotificationPriorityBucketKt.BUCKET_ALERTING) { - @Override - public boolean isInSection(ListEntry entry) { - return mHighPriorityProvider.isHighPriority(entry); - } - - @Nullable - @Override - public NodeController getHeaderNodeController() { - // TODO: remove SHOW_ALL_SECTIONS, this redundant method, and mAlertingHeaderController - if (SHOW_ALL_SECTIONS) { - return mAlertingHeaderController; - } - return null; - } - }; - - private final NotifSectioner mSilentNotifSectioner = new NotifSectioner("Silent", - NotificationPriorityBucketKt.BUCKET_SILENT) { - @Override - public boolean isInSection(ListEntry entry) { - return !mHighPriorityProvider.isHighPriority(entry) - && !entry.getRepresentativeEntry().isAmbient(); - } - - @Nullable - @Override - public NodeController getHeaderNodeController() { - return mSilentNodeController; - } - - @Nullable - @Override - public void onEntriesUpdated(@NonNull List entries) { - mHasSilentEntries = false; - for (int i = 0; i < entries.size(); i++) { - if (entries.get(i).getRepresentativeEntry().getSbn().isClearable()) { - mHasSilentEntries = true; - break; - } - } - mSilentHeaderController.setClearSectionEnabled( - mHasSilentEntries | mHasMinimizedEntries); - } - }; - - private final NotifSectioner mMinimizedNotifSectioner = new NotifSectioner("Minimized", - NotificationPriorityBucketKt.BUCKET_SILENT) { - @Override - public boolean isInSection(ListEntry entry) { - return !mHighPriorityProvider.isHighPriority(entry) - && entry.getRepresentativeEntry().isAmbient(); - } - - @Nullable - @Override - public NodeController getHeaderNodeController() { - return mSilentNodeController; - } - - @Nullable - @Override - public void onEntriesUpdated(@NonNull List entries) { - mHasMinimizedEntries = false; - for (int i = 0; i < entries.size(); i++) { - if (entries.get(i).getRepresentativeEntry().getSbn().isClearable()) { - mHasMinimizedEntries = true; - break; - } - } - mSilentHeaderController.setClearSectionEnabled( - mHasSilentEntries | mHasMinimizedEntries); - } - }; - /** * Checks whether to filter out the given notification based the notification's Ranking object. * NotifListBuilder invalidates the notification list each time the ranking is updated, @@ -202,4 +121,64 @@ public class RankingCoordinator implements Coordinator { mDndVisualEffectsFilter.invalidateList(); } }; + + private class AlertingNotifSectioner extends NotifSectioner { + + AlertingNotifSectioner() { + super("Alerting", NotificationPriorityBucketKt.BUCKET_ALERTING); + } + + @Override + public boolean isInSection(ListEntry entry) { + return mHighPriorityProvider.isHighPriority(entry); + } + + @Nullable + @Override + public NodeController getHeaderNodeController() { + // TODO: remove SHOW_ALL_SECTIONS, this redundant method, and mAlertingHeaderController + if (SHOW_ALL_SECTIONS) { + return mAlertingHeaderController; + } + return null; + } + } + + private class SilentNotifSectioner extends NotifSectioner { + + SilentNotifSectioner() { + super("Silent", NotificationPriorityBucketKt.BUCKET_SILENT); + } + + @Override + public boolean isInSection(ListEntry entry) { + return !mHighPriorityProvider.isHighPriority(entry) + && !entry.getRepresentativeEntry().isAmbient(); + } + + @Nullable + @Override + public NodeController getHeaderNodeController() { + return mSilentNodeController; + } + } + + private class MinimizedNotifSectioner extends NotifSectioner { + + MinimizedNotifSectioner() { + super("Minimized", NotificationPriorityBucketKt.BUCKET_SILENT); + } + + @Override + public boolean isInSection(ListEntry entry) { + return !mHighPriorityProvider.isHighPriority(entry) + && entry.getRepresentativeEntry().isAmbient(); + } + + @Nullable + @Override + public NodeController getHeaderNodeController() { + return mSilentNodeController; + } + } } 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 deleted file mode 100644 index 3f8a39f62dfb3..0000000000000 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/SensitiveContentCoordinator.kt +++ /dev/null @@ -1,124 +0,0 @@ -/* - * 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.keyguard.KeyguardUpdateMonitor -import com.android.systemui.plugins.statusbar.StatusBarStateController -import com.android.systemui.statusbar.NotificationLockscreenUserManager -import com.android.systemui.statusbar.StatusBarState -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 com.android.systemui.statusbar.policy.KeyguardStateController -import dagger.Binds -import dagger.Module -import javax.inject.Inject - -@Module(includes = [PrivateSensitiveContentCoordinatorModule::class]) -interface SensitiveContentCoordinatorModule - -@Module -private interface PrivateSensitiveContentCoordinatorModule { - @Binds - fun bindCoordinator(impl: SensitiveContentCoordinatorImpl): SensitiveContentCoordinator -} - -/** Coordinates re-inflation and post-processing of sensitive notification content. */ -interface SensitiveContentCoordinator : Coordinator - -@CoordinatorScope -private class SensitiveContentCoordinatorImpl @Inject constructor( - private val dynamicPrivacyController: DynamicPrivacyController, - private val lockscreenUserManager: NotificationLockscreenUserManager, - private val keyguardUpdateMonitor: KeyguardUpdateMonitor, - private val statusBarStateController: StatusBarStateController, - private val keyguardStateController: KeyguardStateController -) : 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) { - if (keyguardStateController.isKeyguardGoingAway() || - statusBarStateController.getState() == StatusBarState.KEYGUARD && - keyguardUpdateMonitor.getUserUnlockedWithBiometricAndIsBypassing( - KeyguardUpdateMonitor.getCurrentUser())) { - // don't update yet if: - // - the keyguard is currently going away - // - LS is about to be dismissed by a biometric that bypasses LS (avoid notif flash) - - // TODO(b/206118999): merge this class with KeyguardCoordinator which ensures the - // dependent state changes invalidate the pipeline - return - } - - 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 -): Sequence = - entries.asSequence().flatMap(::extractAllRepresentativeEntries) - -private fun extractAllRepresentativeEntries(listEntry: ListEntry): Sequence = - sequence { - listEntry.representativeEntry?.let { yield(it) } - if (listEntry is GroupEntry) { - yieldAll(extractAllRepresentativeEntries(listEntry.children)) - } - } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/StackCoordinator.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/StackCoordinator.kt index 1c96e8ceb27f9..2374e9c581779 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/StackCoordinator.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/StackCoordinator.kt @@ -50,6 +50,8 @@ class StackCoordinator @Inject internal constructor( var hasClearableAlertingNotifs = false var hasNonClearableSilentNotifs = false var hasClearableSilentNotifs = false + val clearableAlertingSensitiveNotifUsers = mutableSetOf() + val clearableSilentSensitiveNotifUsers = mutableSetOf() entries.forEach { val section = checkNotNull(it.section) { "Null section for ${it.key}" } val entry = checkNotNull(it.representativeEntry) { "Null notif entry for ${it.key}" } @@ -63,13 +65,22 @@ class StackCoordinator @Inject internal constructor( !isSilent && isClearable -> hasClearableAlertingNotifs = true !isSilent && !isClearable -> hasNonClearableAlertingNotifs = true } + if (isClearable && entry.hasSensitiveContents()) { + if (isSilent) { + clearableSilentSensitiveNotifUsers.add(entry.sbn.userId) + } else { + clearableAlertingSensitiveNotifUsers.add(entry.sbn.userId) + } + } } return NotifStats( numActiveNotifs = entries.size, hasNonClearableAlertingNotifs = hasNonClearableAlertingNotifs, hasClearableAlertingNotifs = hasClearableAlertingNotifs, hasNonClearableSilentNotifs = hasNonClearableSilentNotifs, - hasClearableSilentNotifs = hasClearableSilentNotifs + hasClearableSilentNotifs = hasClearableSilentNotifs, + clearableAlertingSensitiveNotifUsers = clearableAlertingSensitiveNotifUsers, + clearableSilentSensitiveNotifUsers = clearableSilentSensitiveNotifUsers ) } } \ No newline at end of file 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 index 274affd9da437..8ecffcb7670a2 100644 --- 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 @@ -20,7 +20,6 @@ import com.android.systemui.dagger.SysUISingleton import com.android.systemui.statusbar.notification.collection.coordinator.ActivityLaunchAnimCoordinatorModule 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 @@ -50,7 +49,6 @@ interface CoordinatorsSubcomponent { @Module(includes = [ ActivityLaunchAnimCoordinatorModule::class, - SensitiveContentCoordinatorModule::class, ]) private abstract class InternalCoordinatorsModule { @Binds diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/inflation/NotificationRowBinderImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/inflation/NotificationRowBinderImpl.java index 4c7b2bbfb6d90..6e96aad776d25 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/inflation/NotificationRowBinderImpl.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/inflation/NotificationRowBinderImpl.java @@ -16,6 +16,8 @@ package com.android.systemui.statusbar.notification.collection.inflation; +import static com.android.systemui.statusbar.notification.row.NotificationRowContentBinder.FLAG_CONTENT_VIEW_PUBLIC; + import static java.util.Objects.requireNonNull; import android.annotation.Nullable; @@ -249,10 +251,13 @@ public class NotificationRowBinderImpl implements NotificationRowBinder { RowContentBindParams params = mRowContentBindStage.getStageParams(entry); params.setUseIncreasedCollapsedHeight(useIncreasedCollapsedHeight); params.setUseLowPriority(isLowPriority); - - // TODO: Replace this API with RowContentBindParams directly. Also move to a separate - // redaction controller. - row.setNeedsRedaction(mNotificationLockscreenUserManager.needsRedaction(entry)); + boolean needsRedaction = + mNotificationLockscreenUserManager.notifNeedsRedactionInPublic(entry); + if (needsRedaction) { + params.requireContentViews(FLAG_CONTENT_VIEW_PUBLIC); + } else { + params.markContentViewsFreeable(FLAG_CONTENT_VIEW_PUBLIC); + } params.rebindAllContentViews(); mRowContentBindStage.requestRebind(entry, en -> { diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/render/NotifStackController.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/render/NotifStackController.kt index b6278d1d5f01b..580d853dc5e97 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/render/NotifStackController.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/render/NotifStackController.kt @@ -28,11 +28,21 @@ data class NotifStats( val hasNonClearableAlertingNotifs: Boolean, val hasClearableAlertingNotifs: Boolean, val hasNonClearableSilentNotifs: Boolean, - val hasClearableSilentNotifs: Boolean + val hasClearableSilentNotifs: Boolean, + val clearableAlertingSensitiveNotifUsers: Set, + val clearableSilentSensitiveNotifUsers: Set ) { companion object { @JvmStatic - val empty = NotifStats(0, false, false, false, false) + val empty = NotifStats( + numActiveNotifs = 0, + hasNonClearableAlertingNotifs = false, + hasClearableAlertingNotifs = false, + hasNonClearableSilentNotifs = false, + hasClearableSilentNotifs = false, + clearableAlertingSensitiveNotifUsers = emptySet(), + clearableSilentSensitiveNotifUsers = emptySet(), + ) } } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/dagger/NotificationSectionHeadersModule.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/dagger/NotificationSectionHeadersModule.kt index 2a9cfd034dcef..f58918fe6f80f 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/dagger/NotificationSectionHeadersModule.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/dagger/NotificationSectionHeadersModule.kt @@ -182,4 +182,4 @@ annotation class HeaderClickAction @Scope @Retention(AnnotationRetention.BINARY) -annotation class SectionHeaderScope \ No newline at end of file +annotation class SectionHeaderScope diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/icon/IconManager.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/icon/IconManager.kt index d8965418b4c43..015e3d8cd553d 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/icon/IconManager.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/icon/IconManager.kt @@ -28,6 +28,7 @@ import android.widget.ImageView import com.android.internal.statusbar.StatusBarIcon import com.android.systemui.R import com.android.systemui.dagger.SysUISingleton +import com.android.systemui.statusbar.NotificationLockscreenUserManager import com.android.systemui.statusbar.StatusBarIconView import com.android.systemui.statusbar.notification.InflationException import com.android.systemui.statusbar.notification.collection.NotificationEntry @@ -49,31 +50,29 @@ import javax.inject.Inject class IconManager @Inject constructor( private val notifCollection: CommonNotifCollection, private val launcherApps: LauncherApps, - private val iconBuilder: IconBuilder + private val iconBuilder: IconBuilder, + private val notifLockscreenUserManager: NotificationLockscreenUserManager ) : ConversationIconManager { private var unimportantConversationKeys: Set = emptySet() fun attach() { notifCollection.addCollectionListener(entryListener) + notifLockscreenUserManager.addOnNeedsRedactionInPublicChangedListener(sensitivityListener) } private val entryListener = object : NotifCollectionListener { - override fun onEntryInit(entry: NotificationEntry) { - entry.addOnSensitivityChangedListener(sensitivityListener) - } - - override fun onEntryCleanUp(entry: NotificationEntry) { - entry.removeOnSensitivityChangedListener(sensitivityListener) - } - override fun onRankingApplied() { // rankings affect whether a conversation is important, which can change the icons recalculateForImportantConversationChange() } } - private val sensitivityListener = NotificationEntry.OnSensitivityChangedListener { - entry -> updateIconsSafe(entry) + private val sensitivityListener = Runnable { + for (entry in notifCollection.allNotifs) { + if (entry.hasSensitiveContents()) { + updateIconsSafe(entry) + } + } } private fun recalculateForImportantConversationChange() { @@ -182,12 +181,16 @@ class IconManager @Inject constructor( } } + private inline val NotificationEntry.needsRedactionInPublic: Boolean get() = + hasSensitiveContents() && + notifLockscreenUserManager.sensitiveNotifsNeedRedactionInPublic(sbn.userId) + @Throws(InflationException::class) private fun getIconDescriptors( entry: NotificationEntry ): Pair { val iconDescriptor = getIconDescriptor(entry, false /* redact */) - val sensitiveDescriptor = if (entry.isSensitive) { + val sensitiveDescriptor = if (entry.needsRedactionInPublic) { getIconDescriptor(entry, true /* redact */) } else { iconDescriptor @@ -310,7 +313,7 @@ class IconManager @Inject constructor( iconView === entry.icons.shelfIcon || iconView === entry.icons.aodIcon val isSmallIcon = iconDescriptor.icon.equals(entry.sbn.notification.smallIcon) return isImportantConversation(entry) && !isSmallIcon && - (!usedInSensitiveContext || !entry.isSensitive) + (!usedInSensitiveContext || !entry.needsRedactionInPublic) } private fun isImportantConversation(entry: NotificationEntry): Boolean { @@ -338,4 +341,4 @@ interface ConversationIconManager { * of a group from which the priority notification has been removed. */ fun setUnimportantConversations(keys: Collection) -} \ No newline at end of file +} diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRow.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRow.java index 0ae365352df05..b09fa721e72e6 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRow.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRow.java @@ -93,8 +93,8 @@ import com.android.systemui.statusbar.RemoteInputController; import com.android.systemui.statusbar.SmartReplyController; import com.android.systemui.statusbar.StatusBarIconView; import com.android.systemui.statusbar.notification.AboveShelfChangedListener; -import com.android.systemui.statusbar.notification.LaunchAnimationParameters; import com.android.systemui.statusbar.notification.FeedbackIcon; +import com.android.systemui.statusbar.notification.LaunchAnimationParameters; import com.android.systemui.statusbar.notification.NotificationFadeAware; import com.android.systemui.statusbar.notification.NotificationLaunchAnimatorController; import com.android.systemui.statusbar.notification.NotificationUtils; @@ -206,7 +206,6 @@ public class ExpandableNotificationRow extends ActivatableNotificationView /** Are we showing the "public" version */ private boolean mShowingPublic; private boolean mSensitive; - private boolean mSensitiveHiddenInGeneral; private boolean mShowingPublicInitialized; private boolean mHideSensitiveForIntrinsicHeight; private float mHeaderVisibleAmount = DEFAULT_HEADER_VISIBLE_AMOUNT; @@ -1560,6 +1559,7 @@ public class ExpandableNotificationRow extends ActivatableNotificationView mUseIncreasedHeadsUpHeight = use; } + // TODO: remove this method and mNeedsRedaction entirely once the old pipeline is gone public void setNeedsRedaction(boolean needsRedaction) { // TODO: Move inflation logic out of this call and remove this method if (mNeedsRedaction != needsRedaction) { @@ -2644,9 +2644,8 @@ public class ExpandableNotificationRow extends ActivatableNotificationView getShowingLayout().requestSelectLayout(needsAnimation || isUserLocked()); } - public void setSensitive(boolean sensitive, boolean hideSensitive) { + public void setSensitive(boolean sensitive) { mSensitive = sensitive; - mSensitiveHiddenInGeneral = hideSensitive; } @Override @@ -2736,7 +2735,15 @@ public class ExpandableNotificationRow extends ActivatableNotificationView * see {@link NotificationEntry#isDismissable()}. */ public boolean canViewBeDismissed() { - return mEntry.isDismissable() && (!shouldShowPublic() || !mSensitiveHiddenInGeneral); + // Entry not dismissable. + if (!mEntry.isDismissable()) { + return false; + } + // Entry shouldn't be showing the public layout, it can be dismissed. + if (!shouldShowPublic()) { + return true; + } + return false; } /** @@ -2745,7 +2752,7 @@ public class ExpandableNotificationRow extends ActivatableNotificationView * clearability see {@link NotificationEntry#isClearable()}. */ public boolean canViewBeCleared() { - return mEntry.isClearable() && (!shouldShowPublic() || !mSensitiveHiddenInGeneral); + return mEntry.isClearable() && !shouldShowPublic(); } private boolean shouldShowPublic() { @@ -3509,10 +3516,28 @@ public class ExpandableNotificationRow extends ActivatableNotificationView pw.print(", translation: " + getTranslation()); pw.print(", removed: " + isRemoved()); pw.print(", expandAnimationRunning: " + mExpandAnimationRunning); - NotificationContentView showingLayout = getShowingLayout(); - pw.print(", privateShowing: " + (showingLayout == mPrivateLayout)); - pw.println(); - showingLayout.dump(pw, args); + pw.print(", sensitive: " + mSensitive); + pw.print(", hideSensitiveForIntrinsicHeight: " + mHideSensitiveForIntrinsicHeight); + pw.println(", privateShowing: " + !shouldShowPublic()); + pw.print("privateLayout: "); + if (mPrivateLayout != null) { + pw.println(); + DumpUtilsKt.withIncreasedIndent(pw, () -> { + mPrivateLayout.dump(pw, args); + mPrivateLayout.dumpSmartReplies(pw); + }); + } else { + pw.println("null"); + } + pw.print("publicLayout: "); + if (mPublicLayout != null) { + pw.println(); + DumpUtilsKt.withIncreasedIndent(pw, () -> { + mPublicLayout.dump(pw, args); + }); + } else { + pw.println("null"); + } if (getViewState() != null) { getViewState().dump(pw, args); @@ -3538,8 +3563,6 @@ public class ExpandableNotificationRow extends ActivatableNotificationView } pw.decreaseIndent(); pw.println("}"); - } else if (mPrivateLayout != null) { - mPrivateLayout.dumpSmartReplies(pw); } }); } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRowController.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRowController.java index 599039d465561..a60026c7a97b7 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRowController.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRowController.java @@ -35,6 +35,7 @@ import com.android.systemui.plugins.FalsingManager; import com.android.systemui.plugins.statusbar.NotificationMenuRowPlugin; import com.android.systemui.plugins.statusbar.StatusBarStateController; import com.android.systemui.shared.plugins.PluginManager; +import com.android.systemui.statusbar.NotificationLockscreenUserManager; import com.android.systemui.statusbar.NotificationMediaManager; import com.android.systemui.statusbar.SmartReplyController; import com.android.systemui.statusbar.notification.FeedbackIcon; @@ -69,6 +70,7 @@ import javax.inject.Named; public class ExpandableNotificationRowController implements NotifViewController { private static final String TAG = "NotifRowController"; private final ExpandableNotificationRow mView; + private final NotificationLockscreenUserManager mLockscreenUserManager; private final NotificationListContainer mListContainer; private final RemoteInputViewSubcomponent.Factory mRemoteInputViewSubcomponentFactory; private final ActivatableNotificationViewController mActivatableNotificationViewController; @@ -86,7 +88,6 @@ public class ExpandableNotificationRowController implements NotifViewController private final ExpandableNotificationRow.OnExpandClickListener mOnExpandClickListener; private final StatusBarStateController mStatusBarStateController; private final MetricsLogger mMetricsLogger; - private final ExpandableNotificationRow.ExpansionLogger mExpansionLogger = this::logNotificationExpansion; private final ExpandableNotificationRow.CoordinateOnClickListener mOnFeedbackClickListener; @@ -100,12 +101,12 @@ public class ExpandableNotificationRowController implements NotifViewController private final Optional mBubblesManagerOptional; private final SmartReplyConstants mSmartReplyConstants; private final SmartReplyController mSmartReplyController; - private final ExpandableNotificationRowDragController mDragController; @Inject public ExpandableNotificationRowController( ExpandableNotificationRow view, + NotificationLockscreenUserManager lockscreenUserManager, ActivatableNotificationViewController activatableNotificationViewController, RemoteInputViewSubcomponent.Factory rivSubcomponentFactory, MetricsLogger metricsLogger, @@ -135,6 +136,7 @@ public class ExpandableNotificationRowController implements NotifViewController Optional bubblesManagerOptional, ExpandableNotificationRowDragController dragController) { mView = view; + mLockscreenUserManager = lockscreenUserManager; mListContainer = listContainer; mRemoteInputViewSubcomponentFactory = rivSubcomponentFactory; mActivatableNotificationViewController = activatableNotificationViewController; @@ -214,6 +216,10 @@ public class ExpandableNotificationRowController implements NotifViewController mView.setDescendantFocusability(ViewGroup.FOCUS_BEFORE_DESCENDANTS); } + mLockscreenUserManager + .addOnNeedsRedactionInPublicChangedListener(mNeedsRedactionListener); + mNeedsRedactionListener.run(); + mView.addOnAttachStateChangeListener(new View.OnAttachStateChangeListener() { @Override public void onViewAttachedToWindow(View v) { @@ -232,6 +238,14 @@ public class ExpandableNotificationRowController implements NotifViewController }); } + private final Runnable mNeedsRedactionListener = new Runnable() { + @Override + public void run() { + mView.setSensitive( + mLockscreenUserManager.notifNeedsRedactionInPublic(mView.getEntry())); + } + }; + private final StatusBarStateController.StateListener mStatusBarStateListener = new StatusBarStateController.StateListener() { @Override @@ -333,4 +347,5 @@ public class ExpandableNotificationRowController implements NotifViewController public void setFeedbackIcon(@Nullable FeedbackIcon icon) { mView.setFeedbackIcon(icon); } + } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationContentView.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationContentView.java index ba26cfaa30b43..a7c6bfb0289c1 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationContentView.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationContentView.java @@ -61,6 +61,7 @@ import com.android.systemui.statusbar.policy.SmartReplyStateInflaterKt; import com.android.systemui.statusbar.policy.SmartReplyView; import com.android.systemui.statusbar.policy.dagger.RemoteInputViewSubcomponent; import com.android.systemui.util.Compile; +import com.android.systemui.util.DumpUtilsKt; import com.android.systemui.wmshell.BubblesManager; import java.io.PrintWriter; @@ -1994,22 +1995,33 @@ public class NotificationContentView extends FrameLayout implements Notification } } - public void dump(PrintWriter pw, String[] args) { + public void dump(PrintWriter pwOriginal, String[] args) { + IndentingPrintWriter pw = DumpUtilsKt.asIndenting(pwOriginal); pw.print("contentView visibility: " + getVisibility()); pw.print(", alpha: " + getAlpha()); pw.print(", clipBounds: " + getClipBounds()); pw.print(", contentHeight: " + mContentHeight); - pw.print(", visibleType: " + mVisibleType); - View view = getViewForVisibleType(mVisibleType); - pw.print(", visibleView "); - if (view != null) { - pw.print(" visibility: " + view.getVisibility()); - pw.print(", alpha: " + view.getAlpha()); - pw.print(", clipBounds: " + view.getClipBounds()); - } else { - pw.print("null"); - } - pw.println(); + pw.println(", currentVisibleType: " + mVisibleType); + DumpUtilsKt.withIncreasedIndent(pw, () -> { + int[] visTypes = { + VISIBLE_TYPE_CONTRACTED, + VISIBLE_TYPE_EXPANDED, + VISIBLE_TYPE_HEADSUP, + VISIBLE_TYPE_SINGLELINE + }; + for (int visType : visTypes) { + pw.print("visType: " + visType + " :: "); + View view = getViewForVisibleType(visType); + if (view != null) { + pw.print("visibility: " + view.getVisibility()); + pw.print(", alpha: " + view.getAlpha()); + pw.print(", clipBounds: " + view.getClipBounds()); + } else { + pw.print("null"); + } + pw.println(); + } + }); } /** Add any existing SmartReplyView to the dump */ diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/RowContentBindParams.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/RowContentBindParams.java index f26ecc32821d4..a52f638e7c26e 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/RowContentBindParams.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/RowContentBindParams.java @@ -102,8 +102,9 @@ public final class RowContentBindParams { * @see InflationFlag */ public void markContentViewsFreeable(@InflationFlag int contentViews) { + @InflationFlag int existingContentViews = contentViews &= mContentViews; mContentViews &= ~contentViews; - mDirtyContentViews &= ~contentViews; + mDirtyContentViews |= existingContentViews; } public @InflationFlag int getContentViews() { diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutController.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutController.java index 2493ccbe5a484..6a8f4796813aa 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutController.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutController.java @@ -216,6 +216,9 @@ public class NotificationStackScrollLayoutController { mBarState = mStatusBarStateController.getState(); mStatusBarStateController.addCallback( mStateListener, SysuiStatusBarStateController.RANK_STACK_SCROLLER); + mLockscreenUserManager.addOnNeedsRedactionInPublicChangedListener( + mOnNeedsRedactionInPublicChangedListener); + updateClearButtonVisibility(); } @Override @@ -223,6 +226,8 @@ public class NotificationStackScrollLayoutController { mConfigurationController.removeCallback(mConfigurationListener); mZenModeController.removeCallback(mZenModeControllerCallback); mStatusBarStateController.removeCallback(mStateListener); + mLockscreenUserManager.removeOnNeedsRedactionInPublicChangedListener( + mOnNeedsRedactionInPublicChangedListener); } }; @@ -326,6 +331,7 @@ public class NotificationStackScrollLayoutController { mLockscreenUserManager.isAnyProfilePublicMode()); mView.onStatePostChange(mStatusBarStateController.fromShadeLocked()); mNotificationEntryManager.updateNotifications("CentralSurfaces state changed"); + updateClearButtonVisibility(); } }; @@ -338,6 +344,17 @@ public class NotificationStackScrollLayoutController { } }; + private final Runnable mOnNeedsRedactionInPublicChangedListener = new Runnable() { + @Override + public void run() { + // Whether or not the notification needs redaction when in public has changed, but if + // we're not actually in public, then we don't need to update anything. + if (mLockscreenUserManager.isAnyProfilePublicMode()) { + updateClearButtonVisibility(); + } + } + }; + /** * Set the overexpansion of the panel to be applied to the view. */ @@ -1274,12 +1291,44 @@ public class NotificationStackScrollLayoutController { return hasNotifications(selection, true /* clearable */); } + private boolean hasRedactedClearableSilentNotifs() { + if (!mLockscreenUserManager.isAnyProfilePublicMode()) { + return false; + } + for (int userId : mNotifStats.getClearableSilentSensitiveNotifUsers()) { + if (mLockscreenUserManager.sensitiveNotifsNeedRedactionInPublic(userId)) { + return true; + } + } + return false; + } + + private boolean hasClearableSilentNotifs() { + return mNotifStats.getHasClearableSilentNotifs() && !hasRedactedClearableSilentNotifs(); + } + + private boolean hasRedactedClearableAlertingNotifs() { + if (!mLockscreenUserManager.isAnyProfilePublicMode()) { + return false; + } + for (int userId : mNotifStats.getClearableAlertingSensitiveNotifUsers()) { + if (mLockscreenUserManager.sensitiveNotifsNeedRedactionInPublic(userId)) { + return true; + } + } + return false; + } + + private boolean hasClearableAlertingNotifs() { + return mNotifStats.getHasClearableAlertingNotifs() && !hasRedactedClearableAlertingNotifs(); + } + public boolean hasNotifications(@SelectedRows int selection, boolean isClearable) { boolean hasAlertingMatchingClearable = isClearable - ? mNotifStats.getHasClearableAlertingNotifs() + ? hasClearableAlertingNotifs() : mNotifStats.getHasNonClearableAlertingNotifs(); boolean hasSilentMatchingClearable = isClearable - ? mNotifStats.getHasClearableSilentNotifs() + ? hasClearableSilentNotifs() : mNotifStats.getHasNonClearableSilentNotifs(); switch (selection) { case ROWS_GENTLE: @@ -1579,6 +1628,15 @@ public class NotificationStackScrollLayoutController { mNotificationActivityStarter = activityStarter; } + private void updateClearButtonVisibility() { + updateClearSilentButton(); + updateFooter(); + } + + private void updateClearSilentButton() { + mSilentHeaderController.setClearSectionEnabled(hasClearableSilentNotifs()); + } + /** * Enum for UiEvent logged from this class */ @@ -1904,6 +1962,7 @@ public class NotificationStackScrollLayoutController { @Override public void setNotifStats(@NonNull NotifStats notifStats) { mNotifStats = notifStats; + updateClearSilentButton(); updateFooter(); updateShowEmptyShadeView(); } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/HeadsUpAppearanceController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/HeadsUpAppearanceController.java index 9863a0ed1ce0c..f386797e322aa 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/HeadsUpAppearanceController.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/HeadsUpAppearanceController.java @@ -29,6 +29,7 @@ import com.android.systemui.plugins.statusbar.StatusBarStateController; import com.android.systemui.statusbar.CommandQueue; import com.android.systemui.statusbar.CrossFadeHelper; import com.android.systemui.statusbar.HeadsUpStatusBarView; +import com.android.systemui.statusbar.NotificationLockscreenUserManager; import com.android.systemui.statusbar.StatusBarState; import com.android.systemui.statusbar.notification.NotificationWakeUpCoordinator; import com.android.systemui.statusbar.notification.collection.NotificationEntry; @@ -40,8 +41,8 @@ import com.android.systemui.statusbar.policy.KeyguardStateController; import com.android.systemui.statusbar.policy.OnHeadsUpChangedListener; import com.android.systemui.util.ViewController; -import java.util.Optional; import java.util.ArrayList; +import java.util.Optional; import java.util.function.BiConsumer; import java.util.function.Consumer; @@ -61,17 +62,17 @@ public class HeadsUpAppearanceController extends ViewController - mSetTrackingHeadsUp = this::setTrackingHeadsUp; + private final Consumer mSetTrackingHeadsUp = + this::setTrackingHeadsUp; private final BiConsumer mSetExpandedHeight = this::setAppearFraction; private final KeyguardBypassController mBypassController; private final StatusBarStateController mStatusBarStateController; private final CommandQueue mCommandQueue; private final NotificationWakeUpCoordinator mWakeUpCoordinator; - + private final NotificationLockscreenUserManager mNotifLockscreenUserManager; + private final Runnable mRedactionChanged = this::updateRedaction; private final View mClockView; private final Optional mOperatorNameViewOptional; @@ -90,6 +91,13 @@ public class HeadsUpAppearanceController extends ViewController false /* dismissAction */ , null /* cancelRunnable */, false /* afterKeyguardGone */); @@ -480,7 +480,7 @@ class StatusBarNotificationPresenter implements NotificationPresenter, .isLockscreenPublicMode(mLockscreenUserManager.getCurrentUserId()); boolean userPublic = devicePublic || mLockscreenUserManager.isLockscreenPublicMode(sbn.getUserId()); - boolean needsRedaction = mLockscreenUserManager.needsRedaction(entry); + boolean needsRedaction = mLockscreenUserManager.notifNeedsRedactionInPublic(entry); if (userPublic && needsRedaction) { // TODO(b/135046837): we can probably relax this with dynamic privacy return true; diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/LockscreenShadeTransitionControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/LockscreenShadeTransitionControllerTest.kt index 562c970178622..73e574eddd17d 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/LockscreenShadeTransitionControllerTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/LockscreenShadeTransitionControllerTest.kt @@ -130,8 +130,8 @@ class LockscreenShadeTransitionControllerTest : SysuiTestCase() { whenever(statusbarStateController.state).thenReturn(StatusBarState.KEYGUARD) whenever(nsslController.isInLockedDownShade).thenReturn(false) whenever(qS.isFullyCollapsed).thenReturn(true) - whenever(lockScreenUserManager.userAllowsPrivateNotificationsInPublic(anyInt())).thenReturn( - true) + whenever(lockScreenUserManager.sensitiveNotifsNeedRedactionInPublic(anyInt())) + .thenReturn(false) whenever(lockScreenUserManager.shouldShowLockscreenNotifications()).thenReturn(true) whenever(lockScreenUserManager.isLockscreenPublicMode(anyInt())).thenReturn(true) whenever(falsingCollector.shouldEnforceBouncer()).thenReturn(false) @@ -207,8 +207,8 @@ class LockscreenShadeTransitionControllerTest : SysuiTestCase() { @Test fun testTriggeringBouncerWhenPrivateNotificationsArentAllowed() { - whenever(lockScreenUserManager.userAllowsPrivateNotificationsInPublic(anyInt())).thenReturn( - false) + whenever(lockScreenUserManager.sensitiveNotifsNeedRedactionInPublic(anyInt())) + .thenReturn(true) transitionController.goToLockedShade(null) verify(statusbarStateController, never()).setState(anyInt()) verify(statusbarStateController).setLeaveOpenOnKeyguardHide(true) diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationLockscreenUserManagerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationLockscreenUserManagerTest.java index 7687d12045414..18937e784ed10 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationLockscreenUserManagerTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationLockscreenUserManagerTest.java @@ -53,11 +53,13 @@ import android.testing.TestableLooper; import androidx.test.filters.SmallTest; +import com.android.keyguard.KeyguardUpdateMonitor; import com.android.systemui.Dependency; import com.android.systemui.SysuiTestCase; import com.android.systemui.broadcast.BroadcastDispatcher; import com.android.systemui.dump.DumpManager; import com.android.systemui.plugins.statusbar.StatusBarStateController; +import com.android.systemui.recents.OverviewProxyService; import com.android.systemui.statusbar.NotificationLockscreenUserManager.KeyguardNotificationSuppressor; import com.android.systemui.statusbar.notification.NotificationEntryManager; import com.android.systemui.statusbar.notification.collection.NotificationEntry; @@ -106,6 +108,10 @@ public class NotificationLockscreenUserManagerTest extends SysuiTestCase { private BroadcastDispatcher mBroadcastDispatcher; @Mock private KeyguardStateController mKeyguardStateController; + @Mock + private KeyguardUpdateMonitor mKeyguardUpdateMonitor; + @Mock + private OverviewProxyService mOverviewProxyService; private UserInfo mCurrentUser; private UserInfo mSecondaryUser; @@ -119,7 +125,6 @@ public class NotificationLockscreenUserManagerTest extends SysuiTestCase { @Before public void setUp() { MockitoAnnotations.initMocks(this); - mDependency.injectTestDependency(NotificationEntryManager.class, mEntryManager); int currentUserId = ActivityManager.getCurrentUser(); mSettings = new FakeSettings(); @@ -212,7 +217,7 @@ public class NotificationLockscreenUserManagerTest extends SysuiTestCase { mLockscreenUserManager.getLockscreenSettingsObserverForTest().onChange(false); // THEN current user's notification is redacted - assertTrue(mLockscreenUserManager.needsRedaction(mCurrentUserNotif)); + assertTrue(mLockscreenUserManager.notifNeedsRedactionInPublic(mCurrentUserNotif)); } @Test @@ -223,7 +228,7 @@ public class NotificationLockscreenUserManagerTest extends SysuiTestCase { mLockscreenUserManager.getLockscreenSettingsObserverForTest().onChange(false); // THEN current user's notification isn't redacted - assertFalse(mLockscreenUserManager.needsRedaction(mCurrentUserNotif)); + assertFalse(mLockscreenUserManager.notifNeedsRedactionInPublic(mCurrentUserNotif)); } @Test @@ -234,7 +239,7 @@ public class NotificationLockscreenUserManagerTest extends SysuiTestCase { mLockscreenUserManager.getLockscreenSettingsObserverForTest().onChange(false); // THEN work profile notification is redacted - assertTrue(mLockscreenUserManager.needsRedaction(mWorkProfileNotif)); + assertTrue(mLockscreenUserManager.notifNeedsRedactionInPublic(mWorkProfileNotif)); } @Test @@ -245,7 +250,7 @@ public class NotificationLockscreenUserManagerTest extends SysuiTestCase { mLockscreenUserManager.getLockscreenSettingsObserverForTest().onChange(false); // THEN work profile notification isn't redacted - assertFalse(mLockscreenUserManager.needsRedaction(mWorkProfileNotif)); + assertFalse(mLockscreenUserManager.notifNeedsRedactionInPublic(mWorkProfileNotif)); } @Test @@ -260,11 +265,11 @@ public class NotificationLockscreenUserManagerTest extends SysuiTestCase { mLockscreenUserManager.getLockscreenSettingsObserverForTest().onChange(false); // THEN the work profile notification doesn't need to be redacted - assertFalse(mLockscreenUserManager.needsRedaction(mWorkProfileNotif)); + assertFalse(mLockscreenUserManager.notifNeedsRedactionInPublic(mWorkProfileNotif)); // THEN the current user and secondary user notifications do need to be redacted - assertTrue(mLockscreenUserManager.needsRedaction(mCurrentUserNotif)); - assertTrue(mLockscreenUserManager.needsRedaction(mSecondaryUserNotif)); + assertTrue(mLockscreenUserManager.notifNeedsRedactionInPublic(mCurrentUserNotif)); + assertTrue(mLockscreenUserManager.notifNeedsRedactionInPublic(mSecondaryUserNotif)); } @Test @@ -279,11 +284,11 @@ public class NotificationLockscreenUserManagerTest extends SysuiTestCase { mLockscreenUserManager.getLockscreenSettingsObserverForTest().onChange(false); // THEN the work profile notification needs to be redacted - assertTrue(mLockscreenUserManager.needsRedaction(mWorkProfileNotif)); + assertTrue(mLockscreenUserManager.notifNeedsRedactionInPublic(mWorkProfileNotif)); // THEN the current user and secondary user notifications don't need to be redacted - assertFalse(mLockscreenUserManager.needsRedaction(mCurrentUserNotif)); - assertFalse(mLockscreenUserManager.needsRedaction(mSecondaryUserNotif)); + assertFalse(mLockscreenUserManager.notifNeedsRedactionInPublic(mCurrentUserNotif)); + assertFalse(mLockscreenUserManager.notifNeedsRedactionInPublic(mSecondaryUserNotif)); } @Test @@ -298,7 +303,7 @@ public class NotificationLockscreenUserManagerTest extends SysuiTestCase { // THEN the secondary profile notification still needs to be redacted because the current // user's setting takes precedence - assertTrue(mLockscreenUserManager.needsRedaction(mSecondaryUserNotif)); + assertTrue(mLockscreenUserManager.notifNeedsRedactionInPublic(mSecondaryUserNotif)); } @Test @@ -418,9 +423,12 @@ public class NotificationLockscreenUserManagerTest extends SysuiTestCase { context, mBroadcastDispatcher, mDevicePolicyManager, + mKeyguardUpdateMonitor, + () -> mEntryManager, + () -> mOverviewProxyService, mUserManager, - (() -> mVisibilityProvider), - (() -> mNotifCollection), + () -> mVisibilityProvider, + () -> mNotifCollection, mClickNotifier, NotificationLockscreenUserManagerTest.this.mKeyguardManager, mStatusBarStateController, diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/DynamicPrivacyControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/DynamicPrivacyControllerTest.java index 7d06abf5cd671..e43ae0d2b22ac 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/DynamicPrivacyControllerTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/DynamicPrivacyControllerTest.java @@ -124,8 +124,8 @@ public class DynamicPrivacyControllerTest extends SysuiTestCase { } private void allowPrivateNotificationsInPublic(boolean allow) { - when(mLockScreenUserManager.userAllowsPrivateNotificationsInPublic(anyInt())).thenReturn( - allow); + when(mLockScreenUserManager.sensitiveNotifsNeedRedactionInPublic(anyInt())).thenReturn( + !allow); } @Test diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/RankingCoordinatorTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/RankingCoordinatorTest.java index f4d8405a796e1..5c2f5fa6bc1cf 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/RankingCoordinatorTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/RankingCoordinatorTest.java @@ -23,7 +23,6 @@ import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertTrue; import static org.mockito.ArgumentMatchers.any; -import static org.mockito.ArgumentMatchers.eq; import static org.mockito.Mockito.spy; import static org.mockito.Mockito.times; import static org.mockito.Mockito.verify; @@ -33,7 +32,6 @@ import android.app.Notification; import android.app.NotificationManager; import android.testing.AndroidTestingRunner; -import androidx.annotation.Nullable; import androidx.test.filters.SmallTest; import com.android.systemui.SysuiTestCase; @@ -41,7 +39,6 @@ import com.android.systemui.plugins.statusbar.StatusBarStateController; import com.android.systemui.statusbar.RankingBuilder; import com.android.systemui.statusbar.SbnBuilder; import com.android.systemui.statusbar.notification.SectionClassifier; -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.NotificationEntryBuilder; @@ -49,7 +46,6 @@ import com.android.systemui.statusbar.notification.collection.listbuilder.plugga 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.render.NodeController; -import com.android.systemui.statusbar.notification.collection.render.SectionHeaderController; import org.junit.Before; import org.junit.Test; @@ -72,7 +68,6 @@ public class RankingCoordinatorTest extends SysuiTestCase { @Mock private NotifPipeline mNotifPipeline; @Mock private NodeController mAlertingHeaderController; @Mock private NodeController mSilentNodeController; - @Mock private SectionHeaderController mSilentHeaderController; @Captor private ArgumentCaptor mNotifFilterCaptor; @@ -94,7 +89,6 @@ public class RankingCoordinatorTest extends SysuiTestCase { mHighPriorityProvider, mSectionClassifier, mAlertingHeaderController, - mSilentHeaderController, mSilentNodeController); mEntry = spy(new NotificationEntryBuilder().build()); mEntry.setRanking(getRankingForUnfilteredNotif().build()); @@ -111,25 +105,6 @@ public class RankingCoordinatorTest extends SysuiTestCase { mSections.addAll(Arrays.asList(mAlertingSectioner, mSilentSectioner, mMinimizedSectioner)); } - @Test - public void testSilentHeaderClearableChildrenUpdate() { - ListEntry listEntry = new ListEntry(mEntry.getKey(), 0L) { - @Nullable - @Override - public NotificationEntry getRepresentativeEntry() { - return mEntry; - } - }; - setRankingAmbient(false); - setSbnClearable(true); - mSilentSectioner.onEntriesUpdated(Arrays.asList(listEntry)); - verify(mSilentHeaderController).setClearSectionEnabled(eq(true)); - - setSbnClearable(false); - mSilentSectioner.onEntriesUpdated(Arrays.asList(listEntry)); - verify(mSilentHeaderController).setClearSectionEnabled(eq(false)); - } - @Test public void testUnfilteredState() { // GIVEN no suppressed visual effects + app not suspended @@ -225,46 +200,6 @@ public class RankingCoordinatorTest extends SysuiTestCase { assertInSection(mEntry, mSilentSectioner); } - @Test - public void testClearableSilentSection() { - when(mHighPriorityProvider.isHighPriority(mEntry)).thenReturn(false); - setSbnClearable(true); - setRankingAmbient(false); - mSilentSectioner.onEntriesUpdated(Arrays.asList(mEntry)); - verify(mSilentHeaderController).setClearSectionEnabled(eq(true)); - } - - @Test - public void testClearableMinimizedSection() { - when(mHighPriorityProvider.isHighPriority(mEntry)).thenReturn(false); - setSbnClearable(true); - setRankingAmbient(true); - mMinimizedSectioner.onEntriesUpdated(Arrays.asList(mEntry)); - verify(mSilentHeaderController).setClearSectionEnabled(eq(true)); - } - - @Test - public void testNotClearableSilentSection() { - setSbnClearable(false); - when(mHighPriorityProvider.isHighPriority(mEntry)).thenReturn(false); - setRankingAmbient(false); - mSilentSectioner.onEntriesUpdated(Arrays.asList(mEntry)); - mMinimizedSectioner.onEntriesUpdated(Arrays.asList(mEntry)); - mAlertingSectioner.onEntriesUpdated(Arrays.asList(mEntry)); - verify(mSilentHeaderController, times(2)).setClearSectionEnabled(eq(false)); - } - - @Test - public void testNotClearableMinimizedSection() { - setSbnClearable(false); - when(mHighPriorityProvider.isHighPriority(mEntry)).thenReturn(false); - setRankingAmbient(true); - mSilentSectioner.onEntriesUpdated(Arrays.asList(mEntry)); - mMinimizedSectioner.onEntriesUpdated(Arrays.asList(mEntry)); - mAlertingSectioner.onEntriesUpdated(Arrays.asList(mEntry)); - verify(mSilentHeaderController, times(2)).setClearSectionEnabled(eq(false)); - } - private void assertInSection(NotificationEntry entry, NotifSectioner section) { for (NotifSectioner current: mSections) { if (current == section) { 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 deleted file mode 100644 index a2d8e3ddba24d..0000000000000 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/SensitiveContentCoordinatorTest.kt +++ /dev/null @@ -1,268 +0,0 @@ -/* - * 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.keyguard.KeyguardUpdateMonitor -import com.android.systemui.SysuiTestCase -import com.android.systemui.plugins.statusbar.StatusBarStateController -import com.android.systemui.statusbar.NotificationLockscreenUserManager -import com.android.systemui.statusbar.StatusBarState -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.coordinator.dagger.CoordinatorScope -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.statusbar.policy.KeyguardStateController -import com.android.systemui.util.mockito.any -import com.android.systemui.util.mockito.mock -import com.android.systemui.util.mockito.withArgCaptor -import dagger.BindsInstance -import dagger.Component -import org.junit.Test -import org.mockito.Mockito.never -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 keyguardUpdateMonitor: KeyguardUpdateMonitor = mock() - val statusBarStateController: StatusBarStateController = mock() - val keyguardStateController: KeyguardStateController = mock() - - val coordinator: SensitiveContentCoordinator = - DaggerTestSensitiveContentCoordinatorComponent - .factory() - .create( - dynamicPrivacyController, - lockscreenUserManager, - keyguardUpdateMonitor, - statusBarStateController, - keyguardStateController) - .coordinator - - @Test - fun onDynamicPrivacyChanged_invokeInvalidationListener() { - coordinator.attach(pipeline) - val invalidator = withArgCaptor { - verify(pipeline).addPreRenderInvalidator(capture()) - } - val dynamicPrivacyListener = withArgCaptor { - verify(dynamicPrivacyController).addListener(capture()) - } - - val invalidationListener = mock>() - invalidator.setInvalidationListener(invalidationListener) - - dynamicPrivacyListener.onDynamicPrivacyChanged() - - verify(invalidationListener).onPluggableInvalidated(invalidator) - } - - @Test - fun onBeforeRenderList_deviceUnlocked_notifDoesNotNeedRedaction() { - coordinator.attach(pipeline) - val onBeforeRenderListListener = withArgCaptor { - 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 { - 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 { - 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 { - 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 { - 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 { - 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 { - 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) - } - - @Test - fun onBeforeRenderList_deviceDynamicallyUnlocked_deviceBiometricBypassingLockScreen() { - coordinator.attach(pipeline) - val onBeforeRenderListListener = withArgCaptor { - 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(statusBarStateController.getState()).thenReturn(StatusBarState.KEYGUARD) - whenever(keyguardUpdateMonitor.getUserUnlockedWithBiometricAndIsBypassing(any())) - .thenReturn(true) - - val entry = fakeNotification(2, true) - - onBeforeRenderListListener.onBeforeRenderList(listOf(entry)) - - verify(entry.representativeEntry!!, never()).setSensitive(any(), any()) - } - - private fun fakeNotification(notifUserId: Int, needsRedaction: Boolean): ListEntry { - val mockUserHandle = mock().apply { - whenever(identifier).thenReturn(notifUserId) - } - val mockSbn: StatusBarNotification = mock().apply { - whenever(user).thenReturn(mockUserHandle) - } - val mockEntry = mock().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 - } - } -} - -@CoordinatorScope -@Component(modules = [SensitiveContentCoordinatorModule::class]) -interface TestSensitiveContentCoordinatorComponent { - val coordinator: SensitiveContentCoordinator - - @Component.Factory - interface Factory { - fun create( - @BindsInstance dynamicPrivacyController: DynamicPrivacyController, - @BindsInstance lockscreenUserManager: NotificationLockscreenUserManager, - @BindsInstance keyguardUpdateMonitor: KeyguardUpdateMonitor, - @BindsInstance statusBarStateController: StatusBarStateController, - @BindsInstance keyguardStateController: KeyguardStateController - ): TestSensitiveContentCoordinatorComponent - } -} \ No newline at end of file diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/StackCoordinatorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/StackCoordinatorTest.kt index 70266e401f8af..cf2fc7c3a5fef 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/StackCoordinatorTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/StackCoordinatorTest.kt @@ -15,6 +15,7 @@ */ package com.android.systemui.statusbar.notification.collection.coordinator +import android.os.UserHandle import android.testing.AndroidTestingRunner import android.testing.TestableLooper.RunWithLooper import androidx.test.filters.SmallTest @@ -43,6 +44,11 @@ import org.mockito.Mockito.`when` as whenever @RunWith(AndroidTestingRunner::class) @RunWithLooper class StackCoordinatorTest : SysuiTestCase() { + + companion object { + const val NOTIF_USER_ID = 0 + } + private lateinit var coordinator: StackCoordinator private lateinit var afterRenderListListener: OnAfterRenderListListener @@ -61,7 +67,10 @@ class StackCoordinatorTest : SysuiTestCase() { afterRenderListListener = withArgCaptor { verify(pipeline).addOnAfterRenderListListener(capture()) } - entry = NotificationEntryBuilder().setSection(section).build() + entry = NotificationEntryBuilder() + .setSection(section) + .setUser(UserHandle.of(NOTIF_USER_ID)) + .build() } @Test @@ -74,13 +83,31 @@ class StackCoordinatorTest : SysuiTestCase() { fun testSetNotificationStats_clearableAlerting() { whenever(section.bucket).thenReturn(BUCKET_ALERTING) afterRenderListListener.onAfterRenderList(listOf(entry), stackController) - verify(stackController).setNotifStats(NotifStats(1, false, true, false, false)) + verify(stackController) + .setNotifStats( + NotifStats( + 1, + false, + true, + false, + false, + setOf(NOTIF_USER_ID), + emptySet())) } @Test fun testSetNotificationStats_clearableSilent() { whenever(section.bucket).thenReturn(BUCKET_SILENT) afterRenderListListener.onAfterRenderList(listOf(entry), stackController) - verify(stackController).setNotifStats(NotifStats(1, false, false, false, true)) + verify(stackController) + .setNotifStats( + NotifStats( + 1, + false, + false, + false, + true, + emptySet(), + setOf(NOTIF_USER_ID))) } } diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/icon/IconManagerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/icon/IconManagerTest.kt index b63e66f1ebe37..c7f7ec213b0c9 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/icon/IconManagerTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/icon/IconManagerTest.kt @@ -14,10 +14,10 @@ * limitations under the License. */ -package com.android.systemui.statusbar.notification.icon; +package com.android.systemui.statusbar.notification.icon -import android.app.ActivityManager; -import android.app.Notification; +import android.app.ActivityManager +import android.app.Notification import android.app.NotificationChannel import android.app.NotificationManager.IMPORTANCE_DEFAULT import android.app.Person @@ -27,11 +27,12 @@ import android.graphics.drawable.Drawable import android.graphics.drawable.Icon import android.os.SystemClock import android.os.UserHandle -import android.testing.AndroidTestingRunner; +import android.testing.AndroidTestingRunner import androidx.test.InstrumentationRegistry import androidx.test.filters.SmallTest import com.android.systemui.SysuiTestCase import com.android.systemui.controls.controller.AuxiliaryPersistenceWrapperTest.Companion.any +import com.android.systemui.statusbar.NotificationLockscreenUserManager import com.android.systemui.statusbar.notification.collection.NotificationEntry import com.android.systemui.statusbar.notification.collection.NotificationEntryBuilder import com.android.systemui.statusbar.notification.collection.notifcollection.CommonNotifCollection @@ -40,7 +41,7 @@ import org.junit.Assert.assertEquals import org.junit.Before import org.junit.Test -import org.junit.runner.RunWith; +import org.junit.runner.RunWith import org.mockito.Mock import org.mockito.Mockito.`when` import org.mockito.Mockito.anyInt @@ -48,15 +49,14 @@ import org.mockito.MockitoAnnotations @SmallTest @RunWith(AndroidTestingRunner::class) -class IconManagerTest: SysuiTestCase() { +class IconManagerTest : SysuiTestCase() { companion object { - private const val TEST_PACKAGE_NAME = "test"; - private const val TEST_UID = 0; + private const val TEST_PACKAGE_NAME = "test" + private const val TEST_UID = 0 } - private var id = 0 - private val context = InstrumentationRegistry.getTargetContext(); + private val context = InstrumentationRegistry.getTargetContext() @Mock private lateinit var shortcut: ShortcutInfo @Mock private lateinit var shortcutIc: Icon @Mock private lateinit var messageIc: Icon @@ -65,6 +65,7 @@ class IconManagerTest: SysuiTestCase() { @Mock private lateinit var drawable: Drawable @Mock private lateinit var row: ExpandableNotificationRow + @Mock private lateinit var notifLockscreenUserManager: NotificationLockscreenUserManager @Mock private lateinit var notifCollection: CommonNotifCollection @Mock private lateinit var launcherApps: LauncherApps @@ -83,13 +84,16 @@ class IconManagerTest: SysuiTestCase() { `when`(shortcut.icon).thenReturn(shortcutIc) `when`(launcherApps.getShortcutIcon(shortcut)).thenReturn(shortcutIc) + `when`(notifLockscreenUserManager.sensitiveNotifsNeedRedactionInPublic(TEST_UID)) + .thenReturn(true) - iconManager = IconManager(notifCollection, launcherApps, iconBuilder) + iconManager = + IconManager(notifCollection, launcherApps, iconBuilder, notifLockscreenUserManager) } @Test fun testCreateIcons_importantConversation_shortcutIcon() { - val entry = notificationEntry(true, true, true) + val entry = notificationEntry() entry?.channel?.isImportantConversation = true entry?.let { iconManager.createIcons(it) @@ -99,7 +103,7 @@ class IconManagerTest: SysuiTestCase() { @Test fun testCreateIcons_importantConversation_messageIcon() { - val entry = notificationEntry(false, true, true) + val entry = notificationEntry(hasShortcut = false) entry?.channel?.isImportantConversation = true entry?.let { iconManager.createIcons(it) @@ -109,7 +113,7 @@ class IconManagerTest: SysuiTestCase() { @Test fun testCreateIcons_importantConversation_largeIcon() { - val entry = notificationEntry(false, false, true) + val entry = notificationEntry(hasShortcut = false, hasMessage = false) entry?.channel?.isImportantConversation = true entry?.let { iconManager.createIcons(it) @@ -119,7 +123,7 @@ class IconManagerTest: SysuiTestCase() { @Test fun testCreateIcons_importantConversation_smallIcon() { - val entry = notificationEntry(false, false, false) + val entry = notificationEntry(hasShortcut = false, hasMessage = false, hasLargeIcon = false) entry?.channel?.isImportantConversation = true entry?.let { iconManager.createIcons(it) @@ -129,7 +133,7 @@ class IconManagerTest: SysuiTestCase() { @Test fun testCreateIcons_notImportantConversation() { - val entry = notificationEntry(true, true, true) + val entry = notificationEntry() entry?.let { iconManager.createIcons(it) } @@ -138,8 +142,10 @@ class IconManagerTest: SysuiTestCase() { @Test fun testCreateIcons_sensitiveImportantConversation() { - val entry = notificationEntry(true, false, false) - entry?.setSensitive(true, true); + val entry = notificationEntry( + hasMessage = false, + hasLargeIcon = false, + hasSensitiveContent = true) entry?.channel?.isImportantConversation = true entry?.let { iconManager.createIcons(it) @@ -151,14 +157,17 @@ class IconManagerTest: SysuiTestCase() { @Test fun testUpdateIcons_sensitivityChange() { - val entry = notificationEntry(true, false, false) + val entry = notificationEntry( + hasMessage = false, + hasLargeIcon = false, + hasSensitiveContent = true) entry?.channel?.isImportantConversation = true - entry?.setSensitive(true, true); entry?.let { iconManager.createIcons(it) } assertEquals(entry?.icons?.aodIcon?.sourceIcon, smallIc) - entry?.setSensitive(false, false); + `when`(notifLockscreenUserManager.sensitiveNotifsNeedRedactionInPublic(TEST_UID)) + .thenReturn(false) entry?.let { iconManager.updateIcons(it) } @@ -166,14 +175,19 @@ class IconManagerTest: SysuiTestCase() { } private fun notificationEntry( - hasShortcut: Boolean, - hasMessage: Boolean, - hasLargeIcon: Boolean + hasShortcut: Boolean = true, + hasMessage: Boolean = true, + hasLargeIcon: Boolean = true, + hasSensitiveContent: Boolean = false ): NotificationEntry? { val n = Notification.Builder(mContext, "id") .setSmallIcon(smallIc) .setContentTitle("Title") .setContentText("Text") + .setVisibility( + if (hasSensitiveContent) + Notification.VISIBILITY_PRIVATE + else Notification.VISIBILITY_PUBLIC) if (hasMessage) { n.style = Notification.MessagingStyle("") @@ -203,7 +217,6 @@ class IconManagerTest: SysuiTestCase() { val entry = builder.build() entry.row = row - entry.setSensitive(false, true); return entry } } diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRowTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRowTest.java index 4ea9321574573..9ff2d12b2ebe9 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRowTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRowTest.java @@ -91,7 +91,7 @@ public class ExpandableNotificationRowTest extends SysuiTestCase { @Test public void testGroupSummaryNotShowingIconWhenPublic() { - mGroupRow.setSensitive(true, true); + mGroupRow.setSensitive(true); mGroupRow.setHideSensitiveForIntrinsicHeight(true); assertTrue(mGroupRow.isSummaryWithChildren()); assertFalse(mGroupRow.isShowingIcon()); @@ -99,7 +99,7 @@ public class ExpandableNotificationRowTest extends SysuiTestCase { @Test public void testNotificationHeaderVisibleWhenAnimating() { - mGroupRow.setSensitive(true, true); + mGroupRow.setSensitive(true); mGroupRow.setHideSensitive(true, false, 0, 0); mGroupRow.setHideSensitive(false, true, 0, 0); assertEquals(View.VISIBLE, mGroupRow.getChildrenContainer().getVisibleWrapper() @@ -130,7 +130,7 @@ public class ExpandableNotificationRowTest extends SysuiTestCase { public void testIconColorShouldBeUpdatedWhenSensitive() throws Exception { ExpandableNotificationRow row = spy(mNotificationTestHelper.createRow( FLAG_CONTENT_VIEW_ALL)); - row.setSensitive(true, true); + row.setSensitive(true); row.setHideSensitive(true, false, 0, 0); verify(row).updateShelfIconColor(); } @@ -214,7 +214,7 @@ public class ExpandableNotificationRowTest extends SysuiTestCase { @Test public void testFeedback_noHeader() { // public notification is custom layout - no header - mGroupRow.setSensitive(true, true); + mGroupRow.setSensitive(true); mGroupRow.setOnFeedbackClickListener(null); mGroupRow.setFeedbackIcon(null); } diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationEntryManagerInflationTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationEntryManagerInflationTest.java index 251ac7d250fe1..a8557050dddb6 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationEntryManagerInflationTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationEntryManagerInflationTest.java @@ -78,7 +78,6 @@ import com.android.systemui.statusbar.notification.collection.legacy.Notificatio import com.android.systemui.statusbar.notification.collection.provider.HighPriorityProvider; import com.android.systemui.statusbar.notification.icon.IconBuilder; import com.android.systemui.statusbar.notification.icon.IconManager; -import com.android.systemui.statusbar.notification.interruption.NotificationInterruptStateProvider; import com.android.systemui.statusbar.notification.logging.NotificationLogger; import com.android.systemui.statusbar.notification.people.PeopleNotificationIdentifier; import com.android.systemui.statusbar.notification.row.dagger.ExpandableNotificationRowComponent; @@ -132,7 +131,6 @@ public class NotificationEntryManagerInflationTest extends SysuiTestCase { @Mock private NotificationEntryListener mEntryListener; @Mock private NotificationRowBinderImpl.BindRowCallback mBindCallback; @Mock private HeadsUpManager mHeadsUpManager; - @Mock private NotificationInterruptStateProvider mNotificationInterruptionStateProvider; @Mock private NotificationLockscreenUserManager mLockscreenUserManager; @Mock private NotificationGutsManager mGutsManager; @Mock private NotificationRemoteInputManager mRemoteInputManager; @@ -254,6 +252,7 @@ public class NotificationEntryManagerInflationTest extends SysuiTestCase { .thenAnswer((Answer) invocation -> new ExpandableNotificationRowController( viewCaptor.getValue(), + mLockscreenUserManager, mock(ActivatableNotificationViewController.class), mock(RemoteInputViewSubcomponent.Factory.class), mock(MetricsLogger.class), @@ -300,7 +299,8 @@ public class NotificationEntryManagerInflationTest extends SysuiTestCase { new IconManager( mEntryManager, mock(LauncherApps.class), - new IconBuilder(mContext)), + new IconBuilder(mContext), + mLockscreenUserManager), mock(LowPriorityInflationHelper.class), mNotifPipelineFlags); diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationTestHelper.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationTestHelper.java index 1ecb09bc8514f..d5ed37a570d25 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationTestHelper.java +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationTestHelper.java @@ -52,6 +52,7 @@ import com.android.systemui.dump.DumpManager; import com.android.systemui.media.MediaFeatureFlag; import com.android.systemui.media.dialog.MediaOutputDialogFactory; import com.android.systemui.plugins.statusbar.StatusBarStateController; +import com.android.systemui.statusbar.NotificationLockscreenUserManager; import com.android.systemui.statusbar.NotificationMediaManager; import com.android.systemui.statusbar.NotificationRemoteInputManager; import com.android.systemui.statusbar.NotificationShadeWindowController; @@ -152,7 +153,8 @@ public class NotificationTestHelper { mIconManager = new IconManager( mock(CommonNotifCollection.class), mock(LauncherApps.class), - new IconBuilder(mContext)); + new IconBuilder(mContext), + mock(NotificationLockscreenUserManager.class)); NotificationContentInflater contentBinder = new NotificationContentInflater( mock(NotifRemoteViewCache.class), diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/HeadsUpAppearanceControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/HeadsUpAppearanceControllerTest.java index ed22cd3c55fc9..169c04c692e0d 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/HeadsUpAppearanceControllerTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/HeadsUpAppearanceControllerTest.java @@ -36,6 +36,7 @@ import com.android.systemui.plugins.DarkIconDispatcher; import com.android.systemui.plugins.statusbar.StatusBarStateController; import com.android.systemui.statusbar.CommandQueue; import com.android.systemui.statusbar.HeadsUpStatusBarView; +import com.android.systemui.statusbar.NotificationLockscreenUserManager; import com.android.systemui.statusbar.notification.NotificationWakeUpCoordinator; import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow; import com.android.systemui.statusbar.notification.row.NotificationTestHelper; @@ -57,9 +58,12 @@ public class HeadsUpAppearanceControllerTest extends SysuiTestCase { private final NotificationStackScrollLayoutController mStackScrollerController = mock(NotificationStackScrollLayoutController.class); - private final NotificationPanelViewController mPanelView = + private final NotificationPanelViewController mPanelViewController = mock(NotificationPanelViewController.class); private final DarkIconDispatcher mDarkIconDispatcher = mock(DarkIconDispatcher.class); + private final NotificationLockscreenUserManager mLockscreenUserManager = + mock(NotificationLockscreenUserManager.class); + private HeadsUpAppearanceController mHeadsUpAppearanceController; private ExpandableNotificationRow mFirst; private HeadsUpStatusBarView mHeadsUpStatusBarView; @@ -93,12 +97,13 @@ public class HeadsUpAppearanceControllerTest extends SysuiTestCase { mHeadsUpManager, mStatusbarStateController, mBypassController, + mLockscreenUserManager, mWakeUpCoordinator, mDarkIconDispatcher, mKeyguardStateController, mCommandQueue, mStackScrollerController, - mPanelView, + mPanelViewController, mHeadsUpStatusBarView, new Clock(mContext, null), Optional.of(mOperatorNameView)); @@ -175,12 +180,13 @@ public class HeadsUpAppearanceControllerTest extends SysuiTestCase { mHeadsUpManager, mStatusbarStateController, mBypassController, + mLockscreenUserManager, mWakeUpCoordinator, mDarkIconDispatcher, mKeyguardStateController, mCommandQueue, mStackScrollerController, - mPanelView, + mPanelViewController, mHeadsUpStatusBarView, new Clock(mContext, null), Optional.empty()); @@ -193,15 +199,15 @@ public class HeadsUpAppearanceControllerTest extends SysuiTestCase { public void testDestroy() { reset(mHeadsUpManager); reset(mDarkIconDispatcher); - reset(mPanelView); + reset(mPanelViewController); reset(mStackScrollerController); mHeadsUpAppearanceController.onViewDetached(); verify(mHeadsUpManager).removeListener(any()); verify(mDarkIconDispatcher).removeDarkReceiver((DarkIconDispatcher.DarkReceiver) any()); - verify(mPanelView).removeTrackingHeadsUpListener(any()); - verify(mPanelView).setHeadsUpAppearanceController(isNull()); + verify(mPanelViewController).removeTrackingHeadsUpListener(any()); + verify(mPanelViewController).setHeadsUpAppearanceController(isNull()); verify(mStackScrollerController).removeOnExpandedHeightChangedListener(any()); } }