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 index 1237c70fcbe30..aac5b8d6b2ebd 100644 --- 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 @@ -70,7 +70,7 @@ public class KeyguardCoordinator implements Coordinator { private final NotifFilter mNotifFilter = new NotifFilter(TAG) { @Override public boolean shouldFilterOut(NotificationEntry entry, long now) { - return mKeyguardNotificationVisibilityProvider.hideNotification(entry); + return mKeyguardNotificationVisibilityProvider.shouldHideNotification(entry); } }; diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/dagger/NotificationsModule.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/dagger/NotificationsModule.java index efe88e6d4f0a0..34c8044ef0d3f 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/dagger/NotificationsModule.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/dagger/NotificationsModule.java @@ -73,6 +73,7 @@ import com.android.systemui.statusbar.notification.collection.render.Notificatio import com.android.systemui.statusbar.notification.init.NotificationsController; import com.android.systemui.statusbar.notification.init.NotificationsControllerImpl; import com.android.systemui.statusbar.notification.init.NotificationsControllerStub; +import com.android.systemui.statusbar.notification.interruption.KeyguardNotificationVisibilityProviderModule; import com.android.systemui.statusbar.notification.interruption.NotificationInterruptStateProvider; import com.android.systemui.statusbar.notification.interruption.NotificationInterruptStateProviderImpl; import com.android.systemui.statusbar.notification.logging.NotificationLogger; @@ -107,9 +108,10 @@ import dagger.Provides; */ @Module(includes = { CoordinatorsModule.class, + KeyguardNotificationVisibilityProviderModule.class, NotifActivityLaunchEventsModule.class, - NotifPipelineChoreographerModule.class, NotifPanelEventsModule.class, + NotifPipelineChoreographerModule.class, NotificationSectionHeadersModule.class, }) public interface NotificationsModule { diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/interruption/KeyguardNotificationVisibilityProvider.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/interruption/KeyguardNotificationVisibilityProvider.kt index 70c9a16b9d7a1..ff0e47f3cfecc 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/interruption/KeyguardNotificationVisibilityProvider.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/interruption/KeyguardNotificationVisibilityProvider.kt @@ -14,6 +14,7 @@ import com.android.keyguard.KeyguardUpdateMonitor import com.android.keyguard.KeyguardUpdateMonitorCallback import com.android.systemui.CoreStartable import com.android.systemui.broadcast.BroadcastDispatcher +import com.android.systemui.dagger.SysUISingleton import com.android.systemui.dagger.qualifiers.Main import com.android.systemui.plugins.statusbar.StatusBarStateController import com.android.systemui.statusbar.NotificationLockscreenUserManager @@ -22,13 +23,49 @@ import com.android.systemui.statusbar.notification.collection.NotificationEntry import com.android.systemui.statusbar.notification.collection.provider.HighPriorityProvider import com.android.systemui.statusbar.policy.KeyguardStateController import com.android.systemui.util.ListenerSet +import com.android.systemui.util.settings.GlobalSettings +import com.android.systemui.util.settings.SecureSettings +import dagger.Binds +import dagger.Module +import dagger.multibindings.ClassKey +import dagger.multibindings.IntoMap import java.util.function.Consumer import javax.inject.Inject -/** - * Determines if notifications should be visible based on the state of the keyguard - */ -class KeyguardNotificationVisibilityProvider @Inject constructor( +/** Determines if notifications should be visible based on the state of the keyguard. */ +interface KeyguardNotificationVisibilityProvider { + /** + * Determines if the given notification should be hidden based on the current keyguard state. + * If a [Consumer] registered via [addOnStateChangedListener] is invoked, the results of this + * method may no longer be valid and should be re-queried. + */ + fun shouldHideNotification(entry: NotificationEntry): Boolean + + /** Registers a listener to be notified when the internal keyguard state has been updated. */ + fun addOnStateChangedListener(listener: Consumer) + + /** Unregisters a listener previously registered with [addOnStateChangedListener]. */ + fun removeOnStateChangedListener(listener: Consumer) +} + +/** Provides a [KeyguardNotificationVisibilityProvider] in [SysUISingleton] scope. */ +@Module(includes = [KeyguardNotificationVisibilityProviderImplModule::class]) +object KeyguardNotificationVisibilityProviderModule + +@Module +private interface KeyguardNotificationVisibilityProviderImplModule { + @Binds + fun bindImpl(impl: KeyguardNotificationVisibilityProviderImpl): + KeyguardNotificationVisibilityProvider + + @Binds + @IntoMap + @ClassKey(KeyguardNotificationVisibilityProvider::class) + fun bindStartable(impl: KeyguardNotificationVisibilityProviderImpl): CoreStartable +} + +@SysUISingleton +private class KeyguardNotificationVisibilityProviderImpl @Inject constructor( context: Context, @Main private val handler: Handler, private val keyguardStateController: KeyguardStateController, @@ -36,8 +73,10 @@ class KeyguardNotificationVisibilityProvider @Inject constructor( private val keyguardUpdateMonitor: KeyguardUpdateMonitor, private val highPriorityProvider: HighPriorityProvider, private val statusBarStateController: StatusBarStateController, - private val broadcastDispatcher: BroadcastDispatcher -) : CoreStartable(context) { + private val broadcastDispatcher: BroadcastDispatcher, + private val secureSettings: SecureSettings, + private val globalSettings: GlobalSettings +) : CoreStartable(context), KeyguardNotificationVisibilityProvider { private val onStateChangedListeners = ListenerSet>() private var hideSilentNotificationsOnLockscreen: Boolean = false @@ -60,33 +99,28 @@ class KeyguardNotificationVisibilityProvider @Inject constructor( // register lockscreen settings changed callbacks: val settingsObserver: ContentObserver = object : ContentObserver(handler) { - override fun onChange(selfChange: Boolean, uri: Uri) { + override fun onChange(selfChange: Boolean, uri: Uri?) { if (keyguardStateController.isShowing) { notifyStateChanged("Settings $uri changed") } } } - mContext.contentResolver.registerContentObserver( - Settings.Secure.getUriFor(Settings.Secure.LOCK_SCREEN_SHOW_NOTIFICATIONS), - false, + secureSettings.registerContentObserverForUser( + Settings.Secure.LOCK_SCREEN_SHOW_NOTIFICATIONS, settingsObserver, UserHandle.USER_ALL) - mContext.contentResolver.registerContentObserver( - Settings.Secure.getUriFor(Settings.Secure.LOCK_SCREEN_ALLOW_PRIVATE_NOTIFICATIONS), + secureSettings.registerContentObserverForUser( + Settings.Secure.LOCK_SCREEN_ALLOW_PRIVATE_NOTIFICATIONS, true, settingsObserver, UserHandle.USER_ALL) - mContext.contentResolver.registerContentObserver( - Settings.Global.getUriFor(Settings.Global.ZEN_MODE), - false, - settingsObserver) + globalSettings.registerContentObserver(Settings.Global.ZEN_MODE, settingsObserver) - mContext.contentResolver.registerContentObserver( - Settings.Secure.getUriFor(Settings.Secure.LOCK_SCREEN_SHOW_SILENT_NOTIFICATIONS), - false, + secureSettings.registerContentObserverForUser( + Settings.Secure.LOCK_SCREEN_SHOW_SILENT_NOTIFICATIONS, settingsObserver, UserHandle.USER_ALL) @@ -98,41 +132,36 @@ class KeyguardNotificationVisibilityProvider @Inject constructor( }) broadcastDispatcher.registerReceiver(object : BroadcastReceiver() { override fun onReceive(context: Context, intent: Intent) { - if (keyguardStateController.isShowing()) { + if (keyguardStateController.isShowing) { // maybe public mode changed - notifyStateChanged(intent.action) + notifyStateChanged(intent.action!!) } } }, IntentFilter(Intent.ACTION_USER_SWITCHED)) } - fun addOnStateChangedListener(listener: Consumer) { + override fun addOnStateChangedListener(listener: Consumer) { onStateChangedListeners.addIfAbsent(listener) } - fun removeOnStateChangedListener(listener: Consumer) { + override fun removeOnStateChangedListener(listener: Consumer) { onStateChangedListeners.remove(listener) } private fun notifyStateChanged(reason: String) { - onStateChangedListeners.forEach({ it.accept(reason) }) + onStateChangedListeners.forEach { it.accept(reason) } } - /** - * Determines if the given notification should be hidden based on the current keyguard state. - * If Listener#onKeyguardStateChanged is invoked, the results of this method may no longer - * be valid, and so should be re-queried - */ - fun hideNotification(entry: NotificationEntry): Boolean { + override fun shouldHideNotification(entry: NotificationEntry): Boolean { val sbn = entry.sbn // FILTER OUT the notification when the keyguard is showing and... - if (keyguardStateController.isShowing()) { + if (keyguardStateController.isShowing) { // ... user settings or the device policy manager doesn't allow lockscreen // notifications; if (!lockscreenUserManager.shouldShowLockscreenNotifications()) { return true } - val currUserId: Int = lockscreenUserManager.getCurrentUserId() + val currUserId: Int = lockscreenUserManager.currentUserId val notifUserId = if (sbn.user.identifier == UserHandle.USER_ALL) currUserId else sbn.user.identifier @@ -178,9 +207,7 @@ class KeyguardNotificationVisibilityProvider @Inject constructor( } private fun readShowSilentNotificationSetting() { - hideSilentNotificationsOnLockscreen = Settings.Secure.getInt( - mContext.getContentResolver(), - Settings.Secure.LOCK_SCREEN_SHOW_SILENT_NOTIFICATIONS, - 1) == 0 + hideSilentNotificationsOnLockscreen = + secureSettings.getBool(Settings.Secure.LOCK_SCREEN_SHOW_SILENT_NOTIFICATIONS, true) } } \ No newline at end of file diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/interruption/NotificationInterruptStateProviderImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/interruption/NotificationInterruptStateProviderImpl.java index c1771ccce9d03..e210f193b0a1d 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/interruption/NotificationInterruptStateProviderImpl.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/interruption/NotificationInterruptStateProviderImpl.java @@ -312,7 +312,7 @@ public class NotificationInterruptStateProviderImpl implements NotificationInter return false; } - if (mKeyguardNotificationVisibilityProvider.hideNotification(entry)) { + if (mKeyguardNotificationVisibilityProvider.shouldHideNotification(entry)) { mLogger.keyguardHideNotification(entry.getKey()); return false; } diff --git a/packages/SystemUI/src/com/android/systemui/util/settings/SettingsProxy.java b/packages/SystemUI/src/com/android/systemui/util/settings/SettingsProxy.java index 5aaf7f680d5cd..1bf5f076ac2f8 100644 --- a/packages/SystemUI/src/com/android/systemui/util/settings/SettingsProxy.java +++ b/packages/SystemUI/src/com/android/systemui/util/settings/SettingsProxy.java @@ -303,11 +303,82 @@ public interface SettingsProxy { default boolean putInt(String name, int value) { return putIntForUser(name, value, getUserId()); } + /** See {@link #putInt(String, int)}. */ default boolean putIntForUser(String name, int value, int userHandle) { return putStringForUser(name, Integer.toString(value), userHandle); } + /** + * Convenience function for retrieving a single secure settings value + * as a boolean. Note that internally setting values are always + * stored as strings; this function converts the string to a boolean + * for you. The default value will be returned if the setting is + * not defined or not a boolean. + * + * @param name The name of the setting to retrieve. + * @param def Value to return if the setting is not defined. + * + * @return The setting's current value, or 'def' if it is not defined + * or not a valid boolean. + */ + default boolean getBool(String name, boolean def) { + return getBoolForUser(name, def, getUserId()); + } + + /** See {@link #getBool(String, boolean)}. */ + default boolean getBoolForUser(String name, boolean def, int userHandle) { + return getIntForUser(name, def ? 1 : 0, userHandle) != 0; + } + + /** + * Convenience function for retrieving a single secure settings value + * as a boolean. Note that internally setting values are always + * stored as strings; this function converts the string to a boolean + * for you. + *

+ * This version does not take a default value. If the setting has not + * been set, or the string value is not a number, + * it throws {@link Settings.SettingNotFoundException}. + * + * @param name The name of the setting to retrieve. + * + * @throws Settings.SettingNotFoundException Thrown if a setting by the given + * name can't be found or the setting value is not a boolean. + * + * @return The setting's current value. + */ + default boolean getBool(String name) throws Settings.SettingNotFoundException { + return getBoolForUser(name, getUserId()); + } + + /** See {@link #getBool(String)}. */ + default boolean getBoolForUser(String name, int userHandle) + throws Settings.SettingNotFoundException { + return getIntForUser(name, userHandle) != 0; + } + + /** + * Convenience function for updating a single settings value as a + * boolean. This will either create a new entry in the table if the + * given name does not exist, or modify the value of the existing row + * with that name. Note that internally setting values are always + * stored as strings, so this function converts the given value to a + * string before storing it. + * + * @param name The name of the setting to modify. + * @param value The new value for the setting. + * @return true if the value was set, false on database errors + */ + default boolean putBool(String name, boolean value) { + return putBoolForUser(name, value, getUserId()); + } + + /** See {@link #putBool(String, boolean)}. */ + default boolean putBoolForUser(String name, boolean value, int userHandle) { + return putIntForUser(name, value ? 1 : 0, userHandle); + } + /** * Convenience function for retrieving a single secure settings value * as a {@code long}. Note that internally setting values are always diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/interruption/KeyguardNotificationVisibilityProviderTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/interruption/KeyguardNotificationVisibilityProviderTest.java index de9ea27e7c139..32d625c707cf6 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/interruption/KeyguardNotificationVisibilityProviderTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/interruption/KeyguardNotificationVisibilityProviderTest.java @@ -22,84 +22,238 @@ import static android.app.NotificationManager.IMPORTANCE_HIGH; import static android.app.NotificationManager.IMPORTANCE_MIN; import static com.android.systemui.statusbar.notification.collection.EntryUtilKt.modifyEntry; +import static com.android.systemui.util.mockito.KotlinMockitoHelpersKt.argThat; import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertTrue; -import static org.mockito.Mockito.spy; +import static org.mockito.ArgumentMatchers.anyString; +import static org.mockito.ArgumentMatchers.eq; +import static org.mockito.ArgumentMatchers.isNull; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; +import android.content.BroadcastReceiver; +import android.content.Context; +import android.content.Intent; import android.os.Handler; import android.os.UserHandle; +import android.provider.Settings; import android.testing.AndroidTestingRunner; +import android.testing.TestableLooper; import androidx.test.filters.SmallTest; import com.android.keyguard.KeyguardUpdateMonitor; +import com.android.keyguard.KeyguardUpdateMonitorCallback; +import com.android.systemui.CoreStartable; import com.android.systemui.SysuiTestCase; import com.android.systemui.broadcast.BroadcastDispatcher; +import com.android.systemui.dagger.SysUISingleton; +import com.android.systemui.dagger.qualifiers.Main; import com.android.systemui.plugins.statusbar.StatusBarStateController; import com.android.systemui.statusbar.NotificationLockscreenUserManager; import com.android.systemui.statusbar.RankingBuilder; -import com.android.systemui.statusbar.notification.SectionHeaderVisibilityProvider; import com.android.systemui.statusbar.notification.collection.GroupEntry; import com.android.systemui.statusbar.notification.collection.GroupEntryBuilder; import com.android.systemui.statusbar.notification.collection.NotificationEntry; import com.android.systemui.statusbar.notification.collection.NotificationEntryBuilder; import com.android.systemui.statusbar.notification.collection.provider.HighPriorityProvider; import com.android.systemui.statusbar.policy.KeyguardStateController; +import com.android.systemui.util.settings.FakeSettings; +import com.android.systemui.util.settings.GlobalSettings; +import com.android.systemui.util.settings.SecureSettings; +import com.android.systemui.utils.os.FakeHandler; import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; +import org.mockito.ArgumentCaptor; import org.mockito.Mock; import org.mockito.MockitoAnnotations; +import java.util.Map; +import java.util.function.Consumer; + +import dagger.BindsInstance; +import dagger.Component; + @SmallTest @RunWith(AndroidTestingRunner.class) +@TestableLooper.RunWithLooper public class KeyguardNotificationVisibilityProviderTest 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 KeyguardUpdateMonitor mKeyguardUpdateMonitor; @Mock private HighPriorityProvider mHighPriorityProvider; - @Mock private SectionHeaderVisibilityProvider mSectionHeaderVisibilityProvider; - @Mock private KeyguardNotificationVisibilityProvider mKeyguardNotificationVisibilityProvider; @Mock private StatusBarStateController mStatusBarStateController; @Mock private BroadcastDispatcher mBroadcastDispatcher; + private final FakeSettings mFakeSettings = new FakeSettings(); + private KeyguardNotificationVisibilityProvider mKeyguardNotificationVisibilityProvider; private NotificationEntry mEntry; @Before public void setup() { MockitoAnnotations.initMocks(this); - // TODO refactor the test of KeyguardNotificationVisibilityProvider out - mKeyguardNotificationVisibilityProvider = spy(new KeyguardNotificationVisibilityProvider( - mContext, - mMainHandler, - mKeyguardStateController, - mLockscreenUserManager, - mKeyguardUpdateMonitor, - mHighPriorityProvider, - mStatusBarStateController, - mBroadcastDispatcher - )); - + TestComponent component = + DaggerKeyguardNotificationVisibilityProviderTest_TestComponent + .factory() + .create( + mContext, + new FakeHandler(TestableLooper.get(this).getLooper()), + mKeyguardStateController, + mLockscreenUserManager, + mKeyguardUpdateMonitor, + mHighPriorityProvider, + mStatusBarStateController, + mBroadcastDispatcher, + mFakeSettings, + mFakeSettings); + mKeyguardNotificationVisibilityProvider = component.getProvider(); + for (CoreStartable startable : component.getCoreStartables().values()) { + startable.start(); + } mEntry = new NotificationEntryBuilder() .setUser(new UserHandle(NOTIF_USER_ID)) .build(); } + @Test + public void notifyListeners_onUnlockedChanged() { + ArgumentCaptor callbackCaptor = + ArgumentCaptor.forClass(KeyguardStateController.Callback.class); + verify(mKeyguardStateController).addCallback(callbackCaptor.capture()); + KeyguardStateController.Callback callback = callbackCaptor.getValue(); + + Consumer listener = mock(Consumer.class); + mKeyguardNotificationVisibilityProvider.addOnStateChangedListener(listener); + + callback.onUnlockedChanged(); + + verify(listener).accept(anyString()); + } + + @Test + public void notifyListeners_onKeyguardShowingChanged() { + ArgumentCaptor callbackCaptor = + ArgumentCaptor.forClass(KeyguardStateController.Callback.class); + verify(mKeyguardStateController).addCallback(callbackCaptor.capture()); + KeyguardStateController.Callback callback = callbackCaptor.getValue(); + + Consumer listener = mock(Consumer.class); + mKeyguardNotificationVisibilityProvider.addOnStateChangedListener(listener); + + callback.onKeyguardShowingChanged(); + + verify(listener).accept(anyString()); + } + + @Test + public void notifyListeners_onStrongAuthStateChanged() { + ArgumentCaptor callbackCaptor = + ArgumentCaptor.forClass(KeyguardUpdateMonitorCallback.class); + verify(mKeyguardUpdateMonitor).registerCallback(callbackCaptor.capture()); + KeyguardUpdateMonitorCallback callback = callbackCaptor.getValue(); + + Consumer listener = mock(Consumer.class); + mKeyguardNotificationVisibilityProvider.addOnStateChangedListener(listener); + + callback.onStrongAuthStateChanged(0); + + verify(listener).accept(anyString()); + } + + @Test + public void notifyListeners_onStatusBarStateChanged() { + ArgumentCaptor callbackCaptor = + ArgumentCaptor.forClass(StatusBarStateController.StateListener.class); + verify(mStatusBarStateController).addCallback(callbackCaptor.capture()); + StatusBarStateController.StateListener callback = callbackCaptor.getValue(); + + Consumer listener = mock(Consumer.class); + mKeyguardNotificationVisibilityProvider.addOnStateChangedListener(listener); + + callback.onStateChanged(0); + + verify(listener).accept(anyString()); + } + + @Test + public void notifyListeners_onReceiveUserSwitchBroadcast() { + ArgumentCaptor callbackCaptor = + ArgumentCaptor.forClass(BroadcastReceiver.class); + verify(mBroadcastDispatcher).registerReceiver( + callbackCaptor.capture(), + argThat(intentFilter -> intentFilter.hasAction(Intent.ACTION_USER_SWITCHED)), + isNull(), + isNull(), + eq(Context.RECEIVER_EXPORTED)); + BroadcastReceiver callback = callbackCaptor.getValue(); + + Consumer listener = mock(Consumer.class); + mKeyguardNotificationVisibilityProvider.addOnStateChangedListener(listener); + + when(mKeyguardStateController.isShowing()).thenReturn(true); + callback.onReceive(mContext, new Intent(Intent.ACTION_USER_SWITCHED)); + + verify(listener).accept(anyString()); + } + + @Test + public void notifyListeners_onSettingChange_lockScreenShowNotifs() { + when(mKeyguardStateController.isShowing()).thenReturn(true); + Consumer listener = mock(Consumer.class); + mKeyguardNotificationVisibilityProvider.addOnStateChangedListener(listener); + + mFakeSettings.putInt(Settings.Secure.LOCK_SCREEN_SHOW_NOTIFICATIONS, 1); + + verify(listener).accept(anyString()); + } + + @Test + public void notifyListeners_onSettingChange_lockScreenAllowPrivateNotifs() { + when(mKeyguardStateController.isShowing()).thenReturn(true); + Consumer listener = mock(Consumer.class); + mKeyguardNotificationVisibilityProvider.addOnStateChangedListener(listener); + + mFakeSettings.putInt(Settings.Secure.LOCK_SCREEN_ALLOW_PRIVATE_NOTIFICATIONS, 1); + + verify(listener).accept(anyString()); + } + + @Test + public void notifyListeners_onSettingChange_zenMode() { + when(mKeyguardStateController.isShowing()).thenReturn(true); + Consumer listener = mock(Consumer.class); + mKeyguardNotificationVisibilityProvider.addOnStateChangedListener(listener); + + mFakeSettings.putInt(Settings.Global.ZEN_MODE, 1); + + verify(listener).accept(anyString()); + } + + @Test + public void notifyListeners_onSettingChange_lockScreenShowSilentNotifs() { + when(mKeyguardStateController.isShowing()).thenReturn(true); + Consumer listener = mock(Consumer.class); + mKeyguardNotificationVisibilityProvider.addOnStateChangedListener(listener); + + mFakeSettings.putInt(Settings.Secure.LOCK_SCREEN_SHOW_SILENT_NOTIFICATIONS, 1); + + verify(listener).accept(anyString()); + } + @Test public void unfilteredState() { // GIVEN an 'unfiltered-keyguard-showing' state setupUnfilteredState(mEntry); // THEN don't filter out the entry - assertFalse(mKeyguardNotificationVisibilityProvider.hideNotification(mEntry)); + assertFalse(mKeyguardNotificationVisibilityProvider.shouldHideNotification(mEntry)); } @Test @@ -109,7 +263,7 @@ public class KeyguardNotificationVisibilityProviderTest extends SysuiTestCase { when(mKeyguardStateController.isShowing()).thenReturn(false); // THEN don't filter out the entry - assertFalse(mKeyguardNotificationVisibilityProvider.hideNotification(mEntry)); + assertFalse(mKeyguardNotificationVisibilityProvider.shouldHideNotification(mEntry)); } @Test @@ -121,7 +275,7 @@ public class KeyguardNotificationVisibilityProviderTest extends SysuiTestCase { when(mLockscreenUserManager.shouldShowLockscreenNotifications()).thenReturn(false); // THEN filter out the entry - assertTrue(mKeyguardNotificationVisibilityProvider.hideNotification(mEntry)); + assertTrue(mKeyguardNotificationVisibilityProvider.shouldHideNotification(mEntry)); } @Test @@ -133,7 +287,7 @@ public class KeyguardNotificationVisibilityProviderTest extends SysuiTestCase { when(mKeyguardUpdateMonitor.isUserInLockdown(NOTIF_USER_ID)).thenReturn(true); // THEN filter out the entry - assertTrue(mKeyguardNotificationVisibilityProvider.hideNotification(mEntry)); + assertTrue(mKeyguardNotificationVisibilityProvider.shouldHideNotification(mEntry)); } @Test @@ -148,7 +302,7 @@ public class KeyguardNotificationVisibilityProviderTest extends SysuiTestCase { .thenReturn(false); // THEN filter out the entry - assertTrue(mKeyguardNotificationVisibilityProvider.hideNotification(mEntry)); + assertTrue(mKeyguardNotificationVisibilityProvider.shouldHideNotification(mEntry)); } @Test @@ -164,7 +318,7 @@ public class KeyguardNotificationVisibilityProviderTest extends SysuiTestCase { .setVisibilityOverride(VISIBILITY_SECRET).build()); // THEN filter out the entry - assertTrue(mKeyguardNotificationVisibilityProvider.hideNotification(mEntry)); + assertTrue(mKeyguardNotificationVisibilityProvider.shouldHideNotification(mEntry)); } @Test @@ -180,7 +334,7 @@ public class KeyguardNotificationVisibilityProviderTest extends SysuiTestCase { when(mHighPriorityProvider.isHighPriority(mEntry)).thenReturn(false); // THEN filter out the entry - assertTrue(mKeyguardNotificationVisibilityProvider.hideNotification(mEntry)); + assertTrue(mKeyguardNotificationVisibilityProvider.shouldHideNotification(mEntry)); } @Test @@ -209,7 +363,8 @@ public class KeyguardNotificationVisibilityProviderTest extends SysuiTestCase { when(mHighPriorityProvider.isHighPriority(parent)).thenReturn(true); // THEN don't filter out the entry - assertFalse(mKeyguardNotificationVisibilityProvider.hideNotification(entryWithParent)); + assertFalse( + mKeyguardNotificationVisibilityProvider.shouldHideNotification(entryWithParent)); // WHEN its parent doesn't exceed threshold to show on lockscreen when(mHighPriorityProvider.isHighPriority(parent)).thenReturn(false); @@ -218,7 +373,7 @@ public class KeyguardNotificationVisibilityProviderTest extends SysuiTestCase { .done()); // THEN filter out the entry - assertTrue(mKeyguardNotificationVisibilityProvider.hideNotification(entryWithParent)); + assertTrue(mKeyguardNotificationVisibilityProvider.shouldHideNotification(entryWithParent)); } /** @@ -259,4 +414,27 @@ public class KeyguardNotificationVisibilityProviderTest extends SysuiTestCase { // notification is high priority, so it shouldn't be filtered when(mHighPriorityProvider.isHighPriority(mEntry)).thenReturn(true); } -} + + @SysUISingleton + @Component(modules = { KeyguardNotificationVisibilityProviderModule.class }) + interface TestComponent { + KeyguardNotificationVisibilityProvider getProvider(); + Map, CoreStartable> getCoreStartables(); + + @Component.Factory + interface Factory { + TestComponent create( + @BindsInstance Context context, + @BindsInstance @Main Handler handler, + @BindsInstance KeyguardStateController keyguardStateController, + @BindsInstance NotificationLockscreenUserManager lockscreenUserManager, + @BindsInstance KeyguardUpdateMonitor keyguardUpdateMonitor, + @BindsInstance HighPriorityProvider highPriorityProvider, + @BindsInstance StatusBarStateController statusBarStateController, + @BindsInstance BroadcastDispatcher broadcastDispatcher, + @BindsInstance SecureSettings secureSettings, + @BindsInstance GlobalSettings globalSettings + ); + } + } +} \ No newline at end of file diff --git a/packages/SystemUI/tests/src/com/android/systemui/util/mockito/KotlinMockitoHelpers.kt b/packages/SystemUI/tests/src/com/android/systemui/util/mockito/KotlinMockitoHelpers.kt index 0f1b65cb7f04a..309acdf13a5de 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/util/mockito/KotlinMockitoHelpers.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/util/mockito/KotlinMockitoHelpers.kt @@ -24,6 +24,7 @@ package com.android.systemui.util.mockito */ import org.mockito.ArgumentCaptor +import org.mockito.ArgumentMatcher import org.mockito.Mockito /** @@ -43,6 +44,14 @@ fun eq(obj: T): T = Mockito.eq(obj) fun any(type: Class): T = Mockito.any(type) inline fun any(): T = any(T::class.java) +/** + * Returns Mockito.argThat() as nullable type to avoid java.lang.IllegalStateException when + * null is returned. + * + * Generic T is nullable because implicitly bounded by Any?. + */ +fun argThat(matcher: ArgumentMatcher): T = Mockito.argThat(matcher) + /** * Kotlin type-inferred version of Mockito.nullable() */