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
This commit is contained in:
Steve Elliott
2022-02-15 12:09:02 -05:00
parent 7bc9ba3bc5
commit 63122cbde2
35 changed files with 570 additions and 803 deletions

View File

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

View File

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

View File

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

View File

@@ -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<OverviewProxyService> mOverviewProxyService;
private final Object mLock = new Object();
// Lazy
private NotificationEntryManager mEntryManager;
private final Lazy<NotificationVisibilityProvider> mVisibilityProviderLazy;
private final Lazy<CommonNotifCollection> mCommonNotifCollectionLazy;
private final Lazy<NotificationEntryManager> 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<UserChangedListener> 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<KeyguardNotificationSuppressor> mKeyguardSuppressors = new ArrayList<>();
private final LockPatternUtils mLockPatternUtils;
private final List<KeyguardNotificationSuppressor> mKeyguardSuppressors = new ArrayList<>();
protected final Context mContext;
private final Handler mMainHandler;
protected final SparseArray<UserInfo> mCurrentProfiles = new SparseArray<>();
protected final SparseArray<UserInfo> mCurrentManagedProfiles = new SparseArray<>();
private final ListenerSet<Runnable> 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<UserInfo> mCurrentProfiles = new SparseArray<>();
protected final SparseArray<UserInfo> 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<NotificationEntryManager> notificationEntryManagerLazy,
Lazy<OverviewProxyService> overviewProxyServiceLazy,
UserManager userManager,
Lazy<NotificationVisibilityProvider> visibilityProviderLazy,
Lazy<CommonNotifCollection> 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<UserInfo> 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

View File

@@ -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<Integer> clearableAlertingSensitiveNotifUsers = new HashSet<>();
HashSet<Integer> 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();
}

View File

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

View File

@@ -169,9 +169,6 @@ public final class NotificationEntry extends ListEntry {
*/
private boolean hasSentReply;
private boolean mSensitive = true;
private List<OnSensitivityChangedListener> 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 */

View File

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

View File

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

View File

@@ -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<ListEntry>) {
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<ListEntry>
): Sequence<NotificationEntry> =
entries.asSequence().flatMap(::extractAllRepresentativeEntries)
private fun extractAllRepresentativeEntries(listEntry: ListEntry): Sequence<NotificationEntry> =
sequence {
listEntry.representativeEntry?.let { yield(it) }
if (listEntry is GroupEntry) {
yieldAll(extractAllRepresentativeEntries(listEntry.children))
}
}

View File

@@ -50,6 +50,8 @@ class StackCoordinator @Inject internal constructor(
var hasClearableAlertingNotifs = false
var hasNonClearableSilentNotifs = false
var hasClearableSilentNotifs = false
val clearableAlertingSensitiveNotifUsers = mutableSetOf<Int>()
val clearableSilentSensitiveNotifUsers = mutableSetOf<Int>()
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
)
}
}

View File

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

View File

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

View File

@@ -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<Int>,
val clearableSilentSensitiveNotifUsers: Set<Int>
) {
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(),
)
}
}

View File

@@ -182,4 +182,4 @@ annotation class HeaderClickAction
@Scope
@Retention(AnnotationRetention.BINARY)
annotation class SectionHeaderScope
annotation class SectionHeaderScope

View File

@@ -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<String> = 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<StatusBarIcon, StatusBarIcon> {
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<String>)
}
}

View File

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

View File

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

View File

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

View File

@@ -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() {

View File

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

View File

@@ -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<HeadsUpStatusBar
private final NotificationIconAreaController mNotificationIconAreaController;
private final HeadsUpManagerPhone mHeadsUpManager;
private final NotificationStackScrollLayoutController mStackScrollerController;
private final DarkIconDispatcher mDarkIconDispatcher;
private final NotificationPanelViewController mNotificationPanelViewController;
private final Consumer<ExpandableNotificationRow>
mSetTrackingHeadsUp = this::setTrackingHeadsUp;
private final Consumer<ExpandableNotificationRow> mSetTrackingHeadsUp =
this::setTrackingHeadsUp;
private final BiConsumer<Float, Float> 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<View> mOperatorNameViewOptional;
@@ -90,6 +91,13 @@ public class HeadsUpAppearanceController extends ViewController<HeadsUpStatusBar
};
private boolean mAnimationsEnabled = true;
private final KeyguardStateController mKeyguardStateController;
private final StatusBarStateController.StateListener mStatusBarStateListener =
new StatusBarStateController.StateListener() {
@Override
public void onStatePostChange() {
updateRedaction();
}
};
@VisibleForTesting
@Inject
@@ -98,6 +106,7 @@ public class HeadsUpAppearanceController extends ViewController<HeadsUpStatusBar
HeadsUpManagerPhone headsUpManager,
StatusBarStateController stateController,
KeyguardBypassController bypassController,
NotificationLockscreenUserManager notifLockscreenUserManager,
NotificationWakeUpCoordinator wakeUpCoordinator,
DarkIconDispatcher darkIconDispatcher,
KeyguardStateController keyguardStateController,
@@ -125,6 +134,7 @@ public class HeadsUpAppearanceController extends ViewController<HeadsUpStatusBar
mClockView = clockView;
mOperatorNameViewOptional = operatorNameViewOptional;
mDarkIconDispatcher = darkIconDispatcher;
mNotifLockscreenUserManager = notifLockscreenUserManager;
mView.addOnLayoutChangeListener(new View.OnLayoutChangeListener() {
@Override
@@ -156,6 +166,8 @@ public class HeadsUpAppearanceController extends ViewController<HeadsUpStatusBar
mNotificationPanelViewController.setHeadsUpAppearanceController(this);
mStackScrollerController.addOnExpandedHeightChangedListener(mSetExpandedHeight);
mDarkIconDispatcher.addDarkReceiver(this);
mNotifLockscreenUserManager.addOnNeedsRedactionInPublicChangedListener(mRedactionChanged);
mStatusBarStateController.addCallback(mStatusBarStateListener);
}
@Override
@@ -167,6 +179,9 @@ public class HeadsUpAppearanceController extends ViewController<HeadsUpStatusBar
mNotificationPanelViewController.setHeadsUpAppearanceController(null);
mStackScrollerController.removeOnExpandedHeightChangedListener(mSetExpandedHeight);
mDarkIconDispatcher.removeDarkReceiver(this);
mNotifLockscreenUserManager
.removeOnNeedsRedactionInPublicChangedListener(mRedactionChanged);
mStatusBarStateController.removeCallback(mStatusBarStateListener);
}
private void updateIsolatedIconLocation(boolean requireStateUpdate) {
@@ -180,6 +195,19 @@ public class HeadsUpAppearanceController extends ViewController<HeadsUpStatusBar
updateHeader(entry);
}
private void updateRedaction() {
NotificationEntry showingEntry = mView.getShowingEntry();
if (showingEntry == null) {
return;
}
int notifUserId = showingEntry.getSbn().getUserId();
boolean redactSensitiveContent =
mNotifLockscreenUserManager.isLockscreenPublicMode(notifUserId)
&& mNotifLockscreenUserManager
.sensitiveNotifsNeedRedactionInPublic(notifUserId);
mView.setRedactSensitiveContent(redactSensitiveContent);
}
private void updateTopEntry() {
NotificationEntry newEntry = null;
if (shouldBeVisible()) {

View File

@@ -31,7 +31,6 @@ import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_N
import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_QUICK_SETTINGS_EXPANDED;
import static com.android.systemui.statusbar.StatusBarState.KEYGUARD;
import static com.android.systemui.statusbar.StatusBarState.SHADE;
import static com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayout.ROWS_ALL;
import static com.android.systemui.statusbar.notification.stack.StackStateAnimator.ANIMATION_DURATION_FOLD_TO_AOD;
import static com.android.systemui.statusbar.phone.panelstate.PanelExpansionStateManagerKt.STATE_CLOSED;
import static com.android.systemui.statusbar.phone.panelstate.PanelExpansionStateManagerKt.STATE_OPEN;
@@ -3916,10 +3915,6 @@ public class NotificationPanelViewController extends PanelViewController {
}
}
public boolean hasActiveClearableNotifications() {
return mNotificationStackScrollLayoutController.hasActiveClearableNotifications(ROWS_ALL);
}
public RemoteInputController.Delegate createRemoteInputDelegate() {
return mNotificationStackScrollLayoutController.createDelegate();
}

View File

@@ -411,8 +411,8 @@ class StatusBarNotificationPresenter implements NotificationPresenter,
if (nowExpanded) {
if (mStatusBarStateController.getState() == StatusBarState.KEYGUARD) {
mShadeTransitionController.goToLockedShade(clickedEntry.getRow());
} else if (clickedEntry.isSensitive()
&& mDynamicPrivacyController.isInLockedDownShade()) {
} else if (mDynamicPrivacyController.isInLockedDownShade()
&& mLockscreenUserManager.notifNeedsRedactionInPublic(clickedEntry)) {
mStatusBarStateController.setLeaveOpenOnKeyguardHide(true);
mActivityStarter.dismissKeyguardThenExecute(() -> 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;

View File

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

View File

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

View File

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

View File

@@ -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<NotifFilter> 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) {

View File

@@ -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<Invalidator> {
verify(pipeline).addPreRenderInvalidator(capture())
}
val dynamicPrivacyListener = withArgCaptor<DynamicPrivacyController.Listener> {
verify(dynamicPrivacyController).addListener(capture())
}
val invalidationListener = mock<Pluggable.PluggableListener<Invalidator>>()
invalidator.setInvalidationListener(invalidationListener)
dynamicPrivacyListener.onDynamicPrivacyChanged()
verify(invalidationListener).onPluggableInvalidated(invalidator)
}
@Test
fun onBeforeRenderList_deviceUnlocked_notifDoesNotNeedRedaction() {
coordinator.attach(pipeline)
val onBeforeRenderListListener = withArgCaptor<OnBeforeRenderListListener> {
verify(pipeline).addOnBeforeRenderListListener(capture())
}
whenever(lockscreenUserManager.currentUserId).thenReturn(1)
whenever(lockscreenUserManager.isLockscreenPublicMode(1)).thenReturn(false)
whenever(lockscreenUserManager.userAllowsPrivateNotificationsInPublic(1)).thenReturn(true)
whenever(dynamicPrivacyController.isDynamicallyUnlocked).thenReturn(false)
val entry = fakeNotification(1, false)
onBeforeRenderListListener.onBeforeRenderList(listOf(entry))
verify(entry.representativeEntry!!).setSensitive(false, false)
}
@Test
fun onBeforeRenderList_deviceUnlocked_notifWouldNeedRedaction() {
coordinator.attach(pipeline)
val onBeforeRenderListListener = withArgCaptor<OnBeforeRenderListListener> {
verify(pipeline).addOnBeforeRenderListListener(capture())
}
whenever(lockscreenUserManager.currentUserId).thenReturn(1)
whenever(lockscreenUserManager.isLockscreenPublicMode(1)).thenReturn(false)
whenever(lockscreenUserManager.userAllowsPrivateNotificationsInPublic(1)).thenReturn(true)
whenever(dynamicPrivacyController.isDynamicallyUnlocked).thenReturn(false)
val entry = fakeNotification(1, true)
onBeforeRenderListListener.onBeforeRenderList(listOf(entry))
verify(entry.representativeEntry!!).setSensitive(false, false)
}
@Test
fun onBeforeRenderList_deviceLocked_userAllowsPublicNotifs() {
coordinator.attach(pipeline)
val onBeforeRenderListListener = withArgCaptor<OnBeforeRenderListListener> {
verify(pipeline).addOnBeforeRenderListListener(capture())
}
whenever(lockscreenUserManager.currentUserId).thenReturn(1)
whenever(lockscreenUserManager.isLockscreenPublicMode(1)).thenReturn(true)
whenever(lockscreenUserManager.userAllowsPrivateNotificationsInPublic(1)).thenReturn(true)
whenever(dynamicPrivacyController.isDynamicallyUnlocked).thenReturn(false)
val entry = fakeNotification(1, false)
onBeforeRenderListListener.onBeforeRenderList(listOf(entry))
verify(entry.representativeEntry!!).setSensitive(false, false)
}
@Test
fun onBeforeRenderList_deviceLocked_userDisallowsPublicNotifs_notifDoesNotNeedRedaction() {
coordinator.attach(pipeline)
val onBeforeRenderListListener = withArgCaptor<OnBeforeRenderListListener> {
verify(pipeline).addOnBeforeRenderListListener(capture())
}
whenever(lockscreenUserManager.currentUserId).thenReturn(1)
whenever(lockscreenUserManager.isLockscreenPublicMode(1)).thenReturn(true)
whenever(lockscreenUserManager.userAllowsPrivateNotificationsInPublic(1)).thenReturn(false)
whenever(dynamicPrivacyController.isDynamicallyUnlocked).thenReturn(false)
val entry = fakeNotification(1, false)
onBeforeRenderListListener.onBeforeRenderList(listOf(entry))
verify(entry.representativeEntry!!).setSensitive(false, true)
}
@Test
fun onBeforeRenderList_deviceLocked_notifNeedsRedaction() {
coordinator.attach(pipeline)
val onBeforeRenderListListener = withArgCaptor<OnBeforeRenderListListener> {
verify(pipeline).addOnBeforeRenderListListener(capture())
}
whenever(lockscreenUserManager.currentUserId).thenReturn(1)
whenever(lockscreenUserManager.isLockscreenPublicMode(1)).thenReturn(true)
whenever(lockscreenUserManager.userAllowsPrivateNotificationsInPublic(1)).thenReturn(false)
whenever(dynamicPrivacyController.isDynamicallyUnlocked).thenReturn(false)
val entry = fakeNotification(1, true)
onBeforeRenderListListener.onBeforeRenderList(listOf(entry))
verify(entry.representativeEntry!!).setSensitive(true, true)
}
@Test
fun onBeforeRenderList_deviceDynamicallyUnlocked_notifNeedsRedaction() {
coordinator.attach(pipeline)
val onBeforeRenderListListener = withArgCaptor<OnBeforeRenderListListener> {
verify(pipeline).addOnBeforeRenderListListener(capture())
}
whenever(lockscreenUserManager.currentUserId).thenReturn(1)
whenever(lockscreenUserManager.isLockscreenPublicMode(1)).thenReturn(true)
whenever(lockscreenUserManager.userAllowsPrivateNotificationsInPublic(1)).thenReturn(false)
whenever(dynamicPrivacyController.isDynamicallyUnlocked).thenReturn(true)
val entry = fakeNotification(1, true)
onBeforeRenderListListener.onBeforeRenderList(listOf(entry))
verify(entry.representativeEntry!!).setSensitive(false, true)
}
@Test
fun onBeforeRenderList_deviceDynamicallyUnlocked_notifUserNeedsWorkChallenge() {
coordinator.attach(pipeline)
val onBeforeRenderListListener = withArgCaptor<OnBeforeRenderListListener> {
verify(pipeline).addOnBeforeRenderListListener(capture())
}
whenever(lockscreenUserManager.currentUserId).thenReturn(1)
whenever(lockscreenUserManager.isLockscreenPublicMode(1)).thenReturn(true)
whenever(lockscreenUserManager.userAllowsPrivateNotificationsInPublic(1)).thenReturn(false)
whenever(dynamicPrivacyController.isDynamicallyUnlocked).thenReturn(true)
whenever(lockscreenUserManager.needsSeparateWorkChallenge(2)).thenReturn(true)
val entry = fakeNotification(2, true)
onBeforeRenderListListener.onBeforeRenderList(listOf(entry))
verify(entry.representativeEntry!!).setSensitive(true, true)
}
@Test
fun onBeforeRenderList_deviceDynamicallyUnlocked_deviceBiometricBypassingLockScreen() {
coordinator.attach(pipeline)
val onBeforeRenderListListener = withArgCaptor<OnBeforeRenderListListener> {
verify(pipeline).addOnBeforeRenderListListener(capture())
}
whenever(lockscreenUserManager.currentUserId).thenReturn(1)
whenever(lockscreenUserManager.isLockscreenPublicMode(1)).thenReturn(true)
whenever(lockscreenUserManager.userAllowsPrivateNotificationsInPublic(1)).thenReturn(false)
whenever(dynamicPrivacyController.isDynamicallyUnlocked).thenReturn(true)
whenever(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<UserHandle>().apply {
whenever(identifier).thenReturn(notifUserId)
}
val mockSbn: StatusBarNotification = mock<StatusBarNotification>().apply {
whenever(user).thenReturn(mockUserHandle)
}
val mockEntry = mock<NotificationEntry>().apply {
whenever(sbn).thenReturn(mockSbn)
}
whenever(lockscreenUserManager.needsRedaction(mockEntry)).thenReturn(needsRedaction)
whenever(mockEntry.rowExists()).thenReturn(true)
return object : ListEntry("key", 0) {
override fun getRepresentativeEntry(): NotificationEntry = mockEntry
}
}
}
@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
}
}

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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