diff options
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 + } +} |