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:
@@ -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) {
|
||||
|
||||
@@ -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])
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
|
||||
@@ -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?
|
||||
|
||||
@@ -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 */
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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))
|
||||
}
|
||||
}
|
||||
@@ -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
|
||||
)
|
||||
}
|
||||
}
|
||||
@@ -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
|
||||
|
||||
@@ -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 -> {
|
||||
|
||||
@@ -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(),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -182,4 +182,4 @@ annotation class HeaderClickAction
|
||||
|
||||
@Scope
|
||||
@Retention(AnnotationRetention.BINARY)
|
||||
annotation class SectionHeaderScope
|
||||
annotation class SectionHeaderScope
|
||||
|
||||
@@ -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>)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -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 */
|
||||
|
||||
@@ -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() {
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
|
||||
@@ -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()) {
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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
|
||||
}
|
||||
}
|
||||
@@ -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)))
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
|
||||
|
||||
@@ -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),
|
||||
|
||||
@@ -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());
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user