diff options
| author | 2019-11-25 10:38:56 -0500 | |
|---|---|---|
| committer | 2019-12-04 10:48:54 -0500 | |
| commit | af16ee0c016ea37b60067f8ec184ba3e4d40856c (patch) | |
| tree | 5c529c454630b9c89c6bddc2be4e2413947c08b1 | |
| parent | 0f2b7fd29a37f35864e7bd3138d3d0b98704faec (diff) | |
Add Coordinators to the NewNotifPipeline
Coordinators added to NotifCoordinators will be passed the
initialized NotifListBuilder and NotifCollection when they're
ready for Pluggables, NotiLfetimeExtenders and NotifCollectionListeners
to be registered.
This CL adds the KeyguardCoordinator which filters notifications based
on whether the keyguard is showing and user lockscreen notifcation
settings
Test: atest KeyguardCoordinatorTest
Bug: 145134683
Change-Id: Ie5a2c0f696a6eedbed203b1b5809164742d23132
10 files changed, 621 insertions, 6 deletions
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationLockscreenUserManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationLockscreenUserManager.java index 4cc5b2144adc..ff4ce9492082 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationLockscreenUserManager.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationLockscreenUserManager.java @@ -65,8 +65,18 @@ public interface NotificationLockscreenUserManager { boolean needsRedaction(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? + */ boolean userAllowsPrivateNotificationsInPublic(int currentUserId); + /** + * Has the given user chosen to allow notifications to be shown even when the lockscreen is in + * "public" (secure & locked) mode? + */ + boolean userAllowsNotificationsInPublic(int userId); + /** Notified when the current user changes. */ interface UserChangedListener { void onUserChanged(int userId); diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationLockscreenUserManagerImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationLockscreenUserManagerImpl.java index f5710a84ddc8..0f3f6b7d9222 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationLockscreenUserManagerImpl.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationLockscreenUserManagerImpl.java @@ -435,7 +435,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? */ - private boolean userAllowsNotificationsInPublic(int userHandle) { + public boolean userAllowsNotificationsInPublic(int userHandle) { if (isCurrentProfile(userHandle) && userHandle != mCurrentUserId) { return true; } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationFilter.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationFilter.java index e5f44bd3b9f7..b61c1ef50cae 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationFilter.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationFilter.java @@ -36,7 +36,10 @@ import com.android.systemui.statusbar.phone.StatusBar; import javax.inject.Inject; import javax.inject.Singleton; -/** Component which manages the various reasons a notification might be filtered out. */ +/** Component which manages the various reasons a notification might be filtered out.*/ +// TODO: delete NotificationFilter.java after migrating to new NotifPipeline b/145659174. +// Notification filtering is taken care of across the different Coordinators (mostly +// KeyguardCoordinator.java) @Singleton public class NotificationFilter { @@ -109,7 +112,7 @@ public class NotificationFilter { return true; } - if (entry.isSuspended()) { + if (entry.getRanking().isSuspended()) { return true; } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/GroupEntry.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/GroupEntry.java index f9f3266f1afa..9ae3882e8e82 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/GroupEntry.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/GroupEntry.java @@ -18,6 +18,8 @@ package com.android.systemui.statusbar.notification.collection; import android.annotation.Nullable; +import com.android.internal.annotations.VisibleForTesting; + import java.util.ArrayList; import java.util.Collections; import java.util.Comparator; @@ -34,7 +36,8 @@ public class GroupEntry extends ListEntry { private final List<NotificationEntry> mUnmodifiableChildren = Collections.unmodifiableList(mChildren); - GroupEntry(String key) { + @VisibleForTesting + public GroupEntry(String key) { super(key); } @@ -52,7 +55,8 @@ public class GroupEntry extends ListEntry { return mUnmodifiableChildren; } - void setSummary(@Nullable NotificationEntry summary) { + @VisibleForTesting + public void setSummary(@Nullable NotificationEntry summary) { mSummary = summary; } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/ListEntry.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/ListEntry.java index dc68c4bdba78..6ce7fd96e6a0 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/ListEntry.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/ListEntry.java @@ -18,6 +18,8 @@ package com.android.systemui.statusbar.notification.collection; import android.annotation.Nullable; +import com.android.internal.annotations.VisibleForTesting; + /** * Abstract superclass for top-level entries, i.e. things that can appear in the final notification * list shown to users. In practice, this means either GroupEntries or NotificationEntries. @@ -49,7 +51,8 @@ public abstract class ListEntry { return mParent; } - void setParent(@Nullable GroupEntry parent) { + @VisibleForTesting + public void setParent(@Nullable GroupEntry parent) { mParent = parent; } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/Coordinator.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/Coordinator.java new file mode 100644 index 000000000000..898918eb076d --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/Coordinator.java @@ -0,0 +1,42 @@ +/* + * Copyright (C) 2019 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.systemui.statusbar.notification.collection.coordinator; + +import com.android.systemui.statusbar.notification.collection.NotifCollection; +import com.android.systemui.statusbar.notification.collection.NotifCollectionListener; +import com.android.systemui.statusbar.notification.collection.NotifLifetimeExtender; +import com.android.systemui.statusbar.notification.collection.init.NewNotifPipeline; +import com.android.systemui.statusbar.notification.collection.listbuilder.NotifListBuilder; +import com.android.systemui.statusbar.notification.collection.listbuilder.pluggable.Pluggable; + +/** + * Interface for registering callbacks to the {@link NewNotifPipeline}. + * + * This includes registering: + * {@link Pluggable}s to the {@link NotifListBuilder} + * {@link NotifCollectionListener}s and {@link NotifLifetimeExtender}s to {@link NotifCollection} + */ +public interface Coordinator { + + /** + * Called after the NewNotifPipeline is initialized. + * Coordinators should register their {@link Pluggable}s to the notifListBuilder + * and their {@link NotifCollectionListener}s and {@link NotifLifetimeExtender}s + * to the notifCollection in this method. + */ + void attach(NotifCollection notifCollection, NotifListBuilder notifListBuilder); +} diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/KeyguardCoordinator.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/KeyguardCoordinator.java new file mode 100644 index 000000000000..f5ed089b63bd --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/KeyguardCoordinator.java @@ -0,0 +1,233 @@ +/* + * Copyright (C) 2019 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.systemui.statusbar.notification.collection.coordinator; + +import static android.app.Notification.VISIBILITY_SECRET; + +import android.content.BroadcastReceiver; +import android.content.Context; +import android.content.Intent; +import android.content.IntentFilter; +import android.database.ContentObserver; +import android.net.Uri; +import android.os.Handler; +import android.os.UserHandle; +import android.provider.Settings; +import android.service.notification.StatusBarNotification; + +import androidx.annotation.MainThread; + +import com.android.keyguard.KeyguardUpdateMonitor; +import com.android.systemui.broadcast.BroadcastDispatcher; +import com.android.systemui.plugins.statusbar.StatusBarStateController; +import com.android.systemui.statusbar.NotificationLockscreenUserManager; +import com.android.systemui.statusbar.notification.NotificationUtils; +import com.android.systemui.statusbar.notification.collection.NotifCollection; +import com.android.systemui.statusbar.notification.collection.NotificationEntry; +import com.android.systemui.statusbar.notification.collection.listbuilder.NotifListBuilder; +import com.android.systemui.statusbar.notification.collection.listbuilder.pluggable.NotifFilter; +import com.android.systemui.statusbar.policy.KeyguardStateController; + +import javax.inject.Inject; +import javax.inject.Singleton; + +/** + * Filters low priority and privacy-sensitive notifications from the lockscreen. + */ +@Singleton +public class KeyguardCoordinator implements Coordinator { + private static final String TAG = "KeyguardNotificationCoordinator"; + + private final Context mContext; + private final Handler mMainHandler; + private final KeyguardStateController mKeyguardStateController; + private final NotificationLockscreenUserManager mLockscreenUserManager; + private final BroadcastDispatcher mBroadcastDispatcher; + private final StatusBarStateController mStatusBarStateController; + private final KeyguardUpdateMonitor mKeyguardUpdateMonitor; + + @Inject + public KeyguardCoordinator( + Context context, + @MainThread Handler mainThreadHandler, + KeyguardStateController keyguardStateController, + NotificationLockscreenUserManager lockscreenUserManager, + BroadcastDispatcher broadcastDispatcher, + StatusBarStateController statusBarStateController, + KeyguardUpdateMonitor keyguardUpdateMonitor) { + mContext = context; + mMainHandler = mainThreadHandler; + mKeyguardStateController = keyguardStateController; + mLockscreenUserManager = lockscreenUserManager; + + mBroadcastDispatcher = broadcastDispatcher; + mStatusBarStateController = statusBarStateController; + mKeyguardUpdateMonitor = keyguardUpdateMonitor; + } + + @Override + public void attach(NotifCollection notifCollection, NotifListBuilder notifListBuilder) { + setupInvalidateNotifListCallbacks(); + notifListBuilder.addFilter(mNotifFilter); + } + + protected final NotifFilter mNotifFilter = new NotifFilter(TAG) { + @Override + public boolean shouldFilterOut(NotificationEntry entry, long now) { + final StatusBarNotification sbn = entry.getSbn(); + + // FILTER OUT the notification when the notification isn't for the current profile + if (!mLockscreenUserManager.isCurrentProfile(sbn.getUserId())) { + return true; + } + + // FILTER OUT the notification when the keyguard is showing and... + if (mKeyguardStateController.isShowing()) { + // ... user settings or the device policy manager doesn't allow lockscreen + // notifications; + if (!mLockscreenUserManager.shouldShowLockscreenNotifications()) { + return true; + } + + final int currUserId = mLockscreenUserManager.getCurrentUserId(); + final int notifUserId = (sbn.getUser().getIdentifier() == UserHandle.USER_ALL) + ? currUserId : sbn.getUser().getIdentifier(); + + // ... user is in lockdown + if (mKeyguardUpdateMonitor.isUserInLockdown(currUserId) + || mKeyguardUpdateMonitor.isUserInLockdown(notifUserId)) { + return true; + } + + // ... device is in public mode and the user's settings doesn't allow + // notifications to show in public mode + if (mLockscreenUserManager.isLockscreenPublicMode(currUserId) + || mLockscreenUserManager.isLockscreenPublicMode(notifUserId)) { + if (entry.getRanking().getVisibilityOverride() == VISIBILITY_SECRET) { + return true; + } + + if (!mLockscreenUserManager.userAllowsNotificationsInPublic(currUserId) + || !mLockscreenUserManager.userAllowsNotificationsInPublic( + notifUserId)) { + return true; + } + } + + // ... neither this notification nor its summary have high enough priority + // to be shown on the lockscreen + // TODO: grouping hasn't happened yet (b/145134683) + if (entry.getParent() != null) { + final NotificationEntry summary = entry.getParent().getRepresentativeEntry(); + if (priorityExceedsLockscreenShowingThreshold(summary)) { + return false; + } + } + return !priorityExceedsLockscreenShowingThreshold(entry); + } + return false; + } + }; + + private boolean priorityExceedsLockscreenShowingThreshold(NotificationEntry entry) { + if (entry == null) { + return false; + } + if (NotificationUtils.useNewInterruptionModel(mContext) + && hideSilentNotificationsOnLockscreen()) { + // TODO: make sure in the NewNotifPipeline that entry.isHighPriority() has been + // correctly updated before reaching this point (b/145134683) + return entry.isHighPriority(); + } else { + return !entry.getRanking().isAmbient(); + } + } + + private boolean hideSilentNotificationsOnLockscreen() { + return Settings.Secure.getInt(mContext.getContentResolver(), + Settings.Secure.LOCK_SCREEN_SHOW_SILENT_NOTIFICATIONS, 1) == 0; + } + + private void setupInvalidateNotifListCallbacks() { + // register onKeyguardShowing callback + mKeyguardStateController.addCallback(mKeyguardCallback); + + // register lockscreen settings changed callbacks: + final ContentObserver settingsObserver = new ContentObserver(mMainHandler) { + @Override + public void onChange(boolean selfChange, Uri uri) { + if (mKeyguardStateController.isShowing()) { + invalidateListFromFilter("Settings " + uri + " changed"); + } + } + }; + + mContext.getContentResolver().registerContentObserver( + Settings.Secure.getUriFor(Settings.Secure.LOCK_SCREEN_SHOW_NOTIFICATIONS), + false, + settingsObserver, + UserHandle.USER_ALL); + + mContext.getContentResolver().registerContentObserver( + Settings.Secure.getUriFor(Settings.Secure.LOCK_SCREEN_ALLOW_PRIVATE_NOTIFICATIONS), + true, + settingsObserver, + UserHandle.USER_ALL); + + mContext.getContentResolver().registerContentObserver( + Settings.Global.getUriFor(Settings.Global.ZEN_MODE), + false, + settingsObserver); + + // register (maybe) public mode changed callbacks: + mStatusBarStateController.addCallback(mStatusBarStateListener); + mBroadcastDispatcher.registerReceiver(new BroadcastReceiver() { + @Override + public void onReceive(Context context, Intent intent) { + if (mKeyguardStateController.isShowing()) { + // maybe public mode changed + invalidateListFromFilter(intent.getAction()); + } + }}, new IntentFilter(Intent.ACTION_USER_SWITCHED)); + } + + private void invalidateListFromFilter(String reason) { + mNotifFilter.invalidateList(); + } + + private final KeyguardStateController.Callback mKeyguardCallback = + new KeyguardStateController.Callback() { + @Override + public void onUnlockedChanged() { + invalidateListFromFilter("onUnlockedChanged"); + } + + @Override + public void onKeyguardShowingChanged() { + invalidateListFromFilter("onKeyguardShowingChanged"); + } + }; + + private final StatusBarStateController.StateListener mStatusBarStateListener = + new StatusBarStateController.StateListener() { + @Override + public void onStateChanged(int newState) { + // maybe public mode changed + invalidateListFromFilter("onStatusBarStateChanged"); + } + }; +} diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/NotifCoordinators.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/NotifCoordinators.java new file mode 100644 index 000000000000..0d53a760dc96 --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/NotifCoordinators.java @@ -0,0 +1,71 @@ +/* + * Copyright (C) 2019 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.systemui.statusbar.notification.collection.coordinator; + +import com.android.systemui.Dumpable; +import com.android.systemui.statusbar.notification.collection.NotifCollection; +import com.android.systemui.statusbar.notification.collection.NotifCollectionListener; +import com.android.systemui.statusbar.notification.collection.NotifLifetimeExtender; +import com.android.systemui.statusbar.notification.collection.listbuilder.NotifListBuilder; +import com.android.systemui.statusbar.notification.collection.listbuilder.pluggable.Pluggable; + +import java.io.FileDescriptor; +import java.io.PrintWriter; +import java.util.ArrayList; +import java.util.List; + +import javax.inject.Inject; +import javax.inject.Singleton; + +/** + * Handles the attachment of the {@link NotifListBuilder} and {@link NotifCollection} to the + * {@link Coordinator}s, so that the Coordinators can register their respective callbacks. + */ +@Singleton +public class NotifCoordinators implements Dumpable { + private static final String TAG = "NotifCoordinators"; + private final List<Coordinator> mCoordinators = new ArrayList<>(); + + /** + * Creates all the coordinators. + */ + @Inject + public NotifCoordinators(KeyguardCoordinator keyguardNotificationCoordinator) { + mCoordinators.add(keyguardNotificationCoordinator); + // TODO: add new Coordinators here! (b/145134683, b/112656837) + } + + /** + * Sends the initialized notifListBuilder and notifCollection to each + * coordinator to indicate the notifListBuilder is ready to accept {@link Pluggable}s + * and the notifCollection is ready to accept {@link NotifCollectionListener}s and + * {@link NotifLifetimeExtender}s. + */ + public void attach(NotifCollection notifCollection, NotifListBuilder notifListBuilder) { + for (Coordinator c : mCoordinators) { + c.attach(notifCollection, notifListBuilder); + } + } + + @Override + public void dump(FileDescriptor fd, PrintWriter pw, String[] args) { + pw.println(TAG + ":"); + for (Coordinator c : mCoordinators) { + pw.println("\t" + c.getClass()); + } + } +} diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/init/NewNotifPipeline.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/init/NewNotifPipeline.java index 3b3e7e2a2933..5fc55daa6b6b 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/init/NewNotifPipeline.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/init/NewNotifPipeline.java @@ -23,6 +23,7 @@ import com.android.systemui.Dumpable; import com.android.systemui.statusbar.NotificationListener; import com.android.systemui.statusbar.notification.collection.NotifCollection; import com.android.systemui.statusbar.notification.collection.NotifListBuilderImpl; +import com.android.systemui.statusbar.notification.collection.coordinator.NotifCoordinators; import java.io.FileDescriptor; import java.io.PrintWriter; @@ -37,6 +38,7 @@ import javax.inject.Singleton; public class NewNotifPipeline implements Dumpable { private final NotifCollection mNotifCollection; private final NotifListBuilderImpl mNotifPipeline; + private final NotifCoordinators mNotifPluggableCoordinators; private final DumpController mDumpController; private final FakePipelineConsumer mFakePipelineConsumer = new FakePipelineConsumer(); @@ -45,9 +47,11 @@ public class NewNotifPipeline implements Dumpable { public NewNotifPipeline( NotifCollection notifCollection, NotifListBuilderImpl notifPipeline, + NotifCoordinators notifCoordinators, DumpController dumpController) { mNotifCollection = notifCollection; mNotifPipeline = notifPipeline; + mNotifPluggableCoordinators = notifCoordinators; mDumpController = dumpController; } @@ -57,6 +61,7 @@ public class NewNotifPipeline implements Dumpable { mFakePipelineConsumer.attach(mNotifPipeline); mNotifPipeline.attach(mNotifCollection); mNotifCollection.attach(notificationService); + mNotifPluggableCoordinators.attach(mNotifCollection, mNotifPipeline); Log.d(TAG, "Notif pipeline initialized"); @@ -66,6 +71,7 @@ public class NewNotifPipeline implements Dumpable { @Override public void dump(FileDescriptor fd, PrintWriter pw, String[] args) { mFakePipelineConsumer.dump(fd, pw, args); + mNotifPluggableCoordinators.dump(fd, pw, args); } private static final String TAG = "NewNotifPipeline"; diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/KeyguardCoordinatorTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/KeyguardCoordinatorTest.java new file mode 100644 index 000000000000..87b3783d1984 --- /dev/null +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/KeyguardCoordinatorTest.java @@ -0,0 +1,243 @@ +/* + * Copyright (C) 2019 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.systemui.statusbar.notification.collection.coordinator; + +import static android.app.Notification.VISIBILITY_PUBLIC; +import static android.app.Notification.VISIBILITY_SECRET; +import static android.app.NotificationManager.IMPORTANCE_HIGH; +import static android.app.NotificationManager.IMPORTANCE_MIN; + +import static junit.framework.Assert.assertFalse; +import static junit.framework.Assert.assertTrue; + +import static org.mockito.Mockito.when; + +import android.os.Handler; +import android.os.UserHandle; +import android.testing.AndroidTestingRunner; + +import androidx.test.filters.SmallTest; + +import com.android.keyguard.KeyguardUpdateMonitor; +import com.android.systemui.SysuiTestCase; +import com.android.systemui.broadcast.BroadcastDispatcher; +import com.android.systemui.plugins.statusbar.StatusBarStateController; +import com.android.systemui.statusbar.NotificationEntryBuilder; +import com.android.systemui.statusbar.NotificationLockscreenUserManager; +import com.android.systemui.statusbar.RankingBuilder; +import com.android.systemui.statusbar.notification.collection.GroupEntry; +import com.android.systemui.statusbar.notification.collection.NotificationEntry; +import com.android.systemui.statusbar.policy.KeyguardStateController; + +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.Mock; +import org.mockito.MockitoAnnotations; + +@SmallTest +@RunWith(AndroidTestingRunner.class) +public class KeyguardCoordinatorTest extends SysuiTestCase { + private static final int NOTIF_USER_ID = 0; + private static final int CURR_USER_ID = 1; + + @Mock private Handler mMainHandler; + @Mock private KeyguardStateController mKeyguardStateController; + @Mock private NotificationLockscreenUserManager mLockscreenUserManager; + @Mock private BroadcastDispatcher mBroadcastDispatcher; + @Mock private StatusBarStateController mStatusBarStateController; + @Mock private KeyguardUpdateMonitor mKeyguardUpdateMonitor; + + private NotificationEntry mEntry; + private KeyguardCoordinator mKeyguardNotificationCoordinator; + + @Before + public void setup() { + MockitoAnnotations.initMocks(this); + mKeyguardNotificationCoordinator = new KeyguardCoordinator( + mContext, mMainHandler, mKeyguardStateController, mLockscreenUserManager, + mBroadcastDispatcher, mStatusBarStateController, + mKeyguardUpdateMonitor); + + mEntry = new NotificationEntryBuilder() + .setUser(new UserHandle(NOTIF_USER_ID)) + .build(); + } + + @Test + public void unfilteredState() { + // GIVEN an 'unfiltered-keyguard-showing' state + setupUnfilteredState(); + + // THEN don't filter out the entry + assertFalse(mKeyguardNotificationCoordinator.mNotifFilter.shouldFilterOut(mEntry, 0)); + } + + @Test + public void notificationNotForCurrentProfile() { + // GIVEN the notification isn't for the given user + setupUnfilteredState(); + when(mLockscreenUserManager.isCurrentProfile(NOTIF_USER_ID)).thenReturn(false); + + // THEN filter out the entry + assertTrue(mKeyguardNotificationCoordinator.mNotifFilter.shouldFilterOut(mEntry, 0)); + } + + @Test + public void keyguardNotShowing() { + // GIVEN the lockscreen isn't showing + setupUnfilteredState(); + when(mKeyguardStateController.isShowing()).thenReturn(false); + + // THEN don't filter out the entry + assertFalse(mKeyguardNotificationCoordinator.mNotifFilter.shouldFilterOut(mEntry, 0)); + } + + @Test + public void doNotShowLockscreenNotifications() { + // GIVEN an 'unfiltered-keyguard-showing' state + setupUnfilteredState(); + + // WHEN we shouldn't show any lockscreen notifications + when(mLockscreenUserManager.shouldShowLockscreenNotifications()).thenReturn(false); + + // THEN filter out the entry + assertTrue(mKeyguardNotificationCoordinator.mNotifFilter.shouldFilterOut(mEntry, 0)); + } + + @Test + public void lockdown() { + // GIVEN an 'unfiltered-keyguard-showing' state + setupUnfilteredState(); + + // WHEN the notification's user is in lockdown: + when(mKeyguardUpdateMonitor.isUserInLockdown(NOTIF_USER_ID)).thenReturn(true); + + // THEN filter out the entry + assertTrue(mKeyguardNotificationCoordinator.mNotifFilter.shouldFilterOut(mEntry, 0)); + } + + @Test + public void publicMode_settingsDisallow() { + // GIVEN an 'unfiltered-keyguard-showing' state + setupUnfilteredState(); + + // WHEN the notification's user is in public mode and settings are configured to disallow + // notifications in public mode + when(mLockscreenUserManager.isLockscreenPublicMode(NOTIF_USER_ID)).thenReturn(true); + when(mLockscreenUserManager.userAllowsNotificationsInPublic(NOTIF_USER_ID)) + .thenReturn(false); + + // THEN filter out the entry + assertTrue(mKeyguardNotificationCoordinator.mNotifFilter.shouldFilterOut(mEntry, 0)); + } + + @Test + public void publicMode_notifDisallowed() { + // GIVEN an 'unfiltered-keyguard-showing' state + setupUnfilteredState(); + + // WHEN the notification's user is in public mode and settings are configured to disallow + // notifications in public mode + when(mLockscreenUserManager.isLockscreenPublicMode(CURR_USER_ID)).thenReturn(true); + mEntry.setRanking(new RankingBuilder() + .setKey(mEntry.getKey()) + .setVisibilityOverride(VISIBILITY_SECRET).build()); + + // THEN filter out the entry + assertTrue(mKeyguardNotificationCoordinator.mNotifFilter.shouldFilterOut(mEntry, 0)); + } + + @Test + public void doesNotExceedThresholdToShow() { + // GIVEN an 'unfiltered-keyguard-showing' state + setupUnfilteredState(); + + // WHEN the notification doesn't exceed the threshold to show on the lockscreen + mEntry.setRanking(new RankingBuilder() + .setKey(mEntry.getKey()) + .setImportance(IMPORTANCE_MIN) + .build()); + + // THEN filter out the entry + assertTrue(mKeyguardNotificationCoordinator.mNotifFilter.shouldFilterOut(mEntry, 0)); + } + + @Test + public void summaryExceedsThresholdToShow() { + // GIVEN an 'unfiltered-keyguard-showing' state + setupUnfilteredState(); + + // WHEN the notification doesn't exceed the threshold to show on the lockscreen + // but its summary does + mEntry.setRanking(new RankingBuilder() + .setKey(mEntry.getKey()) + .setImportance(IMPORTANCE_MIN) + .build()); + + final NotificationEntry summary = new NotificationEntryBuilder().build(); + summary.setRanking(new RankingBuilder() + .setKey(summary.getKey()) + .setImportance(IMPORTANCE_HIGH) + .build()); + final GroupEntry group = new GroupEntry(mEntry.getSbn().getGroupKey()); + group.setSummary(summary); + mEntry.setParent(group); + + // THEN don't filter out the entry + assertFalse(mKeyguardNotificationCoordinator.mNotifFilter.shouldFilterOut(mEntry, 0)); + } + + /** + * setup a state where the notification will not be filtered by the + * KeyguardNotificationCoordinator when the keyguard is showing. + */ + private void setupUnfilteredState() { + // notification is for current profile + when(mLockscreenUserManager.isCurrentProfile(NOTIF_USER_ID)).thenReturn(true); + + // keyguard is showing + when(mKeyguardStateController.isShowing()).thenReturn(true); + + // show notifications on the lockscreen + when(mLockscreenUserManager.shouldShowLockscreenNotifications()).thenReturn(true); + + // neither the current user nor the notification's user is in lockdown + when(mLockscreenUserManager.getCurrentUserId()).thenReturn(CURR_USER_ID); + when(mKeyguardUpdateMonitor.isUserInLockdown(NOTIF_USER_ID)).thenReturn(false); + when(mKeyguardUpdateMonitor.isUserInLockdown(CURR_USER_ID)).thenReturn(false); + + // not in public mode + when(mLockscreenUserManager.isLockscreenPublicMode(CURR_USER_ID)).thenReturn(false); + when(mLockscreenUserManager.isLockscreenPublicMode(NOTIF_USER_ID)).thenReturn(false); + + // entry's ranking - should show on all lockscreens + // + priority of the notification exceeds the threshold to be shown on the lockscreen + mEntry.setRanking(new RankingBuilder() + .setKey(mEntry.getKey()) + .setVisibilityOverride(VISIBILITY_PUBLIC) + .setImportance(IMPORTANCE_HIGH) + .build()); + + // settings allows notifications in public mode + when(mLockscreenUserManager.userAllowsNotificationsInPublic(CURR_USER_ID)).thenReturn(true); + when(mLockscreenUserManager.userAllowsNotificationsInPublic(NOTIF_USER_ID)) + .thenReturn(true); + + // notification doesn't have a summary + } +} |