Register KeyguardNotifVisProvider CoreStartable
KeyguardNotificationVisibilityProvider's `start` method was never being
invoked, because it was never being registered as a CoreStartable. This
was in turn causing all invalidaton events to be missed, resulting in
stale notification shade states being visible.
This CL properly registers the CoreStartable, and adds additional tests
to verify that listeners are called as expected once registered.
Fixes: 224763592
Test: atest KeyguardNotificationVisibilityProviderTest
Test: 0. Have silent notifications
1. lock device
2. expand shade, keeping device locked
3. collapse shade
4. verify silent header no longer visible
Change-Id: I70661583f0d98c6dac7924667dad89a3664334e2
This commit is contained in:
@@ -70,7 +70,7 @@ public class KeyguardCoordinator implements Coordinator {
|
|||||||
private final NotifFilter mNotifFilter = new NotifFilter(TAG) {
|
private final NotifFilter mNotifFilter = new NotifFilter(TAG) {
|
||||||
@Override
|
@Override
|
||||||
public boolean shouldFilterOut(NotificationEntry entry, long now) {
|
public boolean shouldFilterOut(NotificationEntry entry, long now) {
|
||||||
return mKeyguardNotificationVisibilityProvider.hideNotification(entry);
|
return mKeyguardNotificationVisibilityProvider.shouldHideNotification(entry);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -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.NotificationsController;
|
||||||
import com.android.systemui.statusbar.notification.init.NotificationsControllerImpl;
|
import com.android.systemui.statusbar.notification.init.NotificationsControllerImpl;
|
||||||
import com.android.systemui.statusbar.notification.init.NotificationsControllerStub;
|
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.NotificationInterruptStateProvider;
|
||||||
import com.android.systemui.statusbar.notification.interruption.NotificationInterruptStateProviderImpl;
|
import com.android.systemui.statusbar.notification.interruption.NotificationInterruptStateProviderImpl;
|
||||||
import com.android.systemui.statusbar.notification.logging.NotificationLogger;
|
import com.android.systemui.statusbar.notification.logging.NotificationLogger;
|
||||||
@@ -107,9 +108,10 @@ import dagger.Provides;
|
|||||||
*/
|
*/
|
||||||
@Module(includes = {
|
@Module(includes = {
|
||||||
CoordinatorsModule.class,
|
CoordinatorsModule.class,
|
||||||
|
KeyguardNotificationVisibilityProviderModule.class,
|
||||||
NotifActivityLaunchEventsModule.class,
|
NotifActivityLaunchEventsModule.class,
|
||||||
NotifPipelineChoreographerModule.class,
|
|
||||||
NotifPanelEventsModule.class,
|
NotifPanelEventsModule.class,
|
||||||
|
NotifPipelineChoreographerModule.class,
|
||||||
NotificationSectionHeadersModule.class,
|
NotificationSectionHeadersModule.class,
|
||||||
})
|
})
|
||||||
public interface NotificationsModule {
|
public interface NotificationsModule {
|
||||||
|
|||||||
@@ -14,6 +14,7 @@ import com.android.keyguard.KeyguardUpdateMonitor
|
|||||||
import com.android.keyguard.KeyguardUpdateMonitorCallback
|
import com.android.keyguard.KeyguardUpdateMonitorCallback
|
||||||
import com.android.systemui.CoreStartable
|
import com.android.systemui.CoreStartable
|
||||||
import com.android.systemui.broadcast.BroadcastDispatcher
|
import com.android.systemui.broadcast.BroadcastDispatcher
|
||||||
|
import com.android.systemui.dagger.SysUISingleton
|
||||||
import com.android.systemui.dagger.qualifiers.Main
|
import com.android.systemui.dagger.qualifiers.Main
|
||||||
import com.android.systemui.plugins.statusbar.StatusBarStateController
|
import com.android.systemui.plugins.statusbar.StatusBarStateController
|
||||||
import com.android.systemui.statusbar.NotificationLockscreenUserManager
|
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.notification.collection.provider.HighPriorityProvider
|
||||||
import com.android.systemui.statusbar.policy.KeyguardStateController
|
import com.android.systemui.statusbar.policy.KeyguardStateController
|
||||||
import com.android.systemui.util.ListenerSet
|
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 java.util.function.Consumer
|
||||||
import javax.inject.Inject
|
import javax.inject.Inject
|
||||||
|
|
||||||
/**
|
/** Determines if notifications should be visible based on the state of the keyguard. */
|
||||||
* Determines if notifications should be visible based on the state of the keyguard
|
interface KeyguardNotificationVisibilityProvider {
|
||||||
*/
|
/**
|
||||||
class KeyguardNotificationVisibilityProvider @Inject constructor(
|
* 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<String>)
|
||||||
|
|
||||||
|
/** Unregisters a listener previously registered with [addOnStateChangedListener]. */
|
||||||
|
fun removeOnStateChangedListener(listener: Consumer<String>)
|
||||||
|
}
|
||||||
|
|
||||||
|
/** 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,
|
context: Context,
|
||||||
@Main private val handler: Handler,
|
@Main private val handler: Handler,
|
||||||
private val keyguardStateController: KeyguardStateController,
|
private val keyguardStateController: KeyguardStateController,
|
||||||
@@ -36,8 +73,10 @@ class KeyguardNotificationVisibilityProvider @Inject constructor(
|
|||||||
private val keyguardUpdateMonitor: KeyguardUpdateMonitor,
|
private val keyguardUpdateMonitor: KeyguardUpdateMonitor,
|
||||||
private val highPriorityProvider: HighPriorityProvider,
|
private val highPriorityProvider: HighPriorityProvider,
|
||||||
private val statusBarStateController: StatusBarStateController,
|
private val statusBarStateController: StatusBarStateController,
|
||||||
private val broadcastDispatcher: BroadcastDispatcher
|
private val broadcastDispatcher: BroadcastDispatcher,
|
||||||
) : CoreStartable(context) {
|
private val secureSettings: SecureSettings,
|
||||||
|
private val globalSettings: GlobalSettings
|
||||||
|
) : CoreStartable(context), KeyguardNotificationVisibilityProvider {
|
||||||
private val onStateChangedListeners = ListenerSet<Consumer<String>>()
|
private val onStateChangedListeners = ListenerSet<Consumer<String>>()
|
||||||
private var hideSilentNotificationsOnLockscreen: Boolean = false
|
private var hideSilentNotificationsOnLockscreen: Boolean = false
|
||||||
|
|
||||||
@@ -60,33 +99,28 @@ class KeyguardNotificationVisibilityProvider @Inject constructor(
|
|||||||
|
|
||||||
// register lockscreen settings changed callbacks:
|
// register lockscreen settings changed callbacks:
|
||||||
val settingsObserver: ContentObserver = object : ContentObserver(handler) {
|
val settingsObserver: ContentObserver = object : ContentObserver(handler) {
|
||||||
override fun onChange(selfChange: Boolean, uri: Uri) {
|
override fun onChange(selfChange: Boolean, uri: Uri?) {
|
||||||
if (keyguardStateController.isShowing) {
|
if (keyguardStateController.isShowing) {
|
||||||
notifyStateChanged("Settings $uri changed")
|
notifyStateChanged("Settings $uri changed")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
mContext.contentResolver.registerContentObserver(
|
secureSettings.registerContentObserverForUser(
|
||||||
Settings.Secure.getUriFor(Settings.Secure.LOCK_SCREEN_SHOW_NOTIFICATIONS),
|
Settings.Secure.LOCK_SCREEN_SHOW_NOTIFICATIONS,
|
||||||
false,
|
|
||||||
settingsObserver,
|
settingsObserver,
|
||||||
UserHandle.USER_ALL)
|
UserHandle.USER_ALL)
|
||||||
|
|
||||||
mContext.contentResolver.registerContentObserver(
|
secureSettings.registerContentObserverForUser(
|
||||||
Settings.Secure.getUriFor(Settings.Secure.LOCK_SCREEN_ALLOW_PRIVATE_NOTIFICATIONS),
|
Settings.Secure.LOCK_SCREEN_ALLOW_PRIVATE_NOTIFICATIONS,
|
||||||
true,
|
true,
|
||||||
settingsObserver,
|
settingsObserver,
|
||||||
UserHandle.USER_ALL)
|
UserHandle.USER_ALL)
|
||||||
|
|
||||||
mContext.contentResolver.registerContentObserver(
|
globalSettings.registerContentObserver(Settings.Global.ZEN_MODE, settingsObserver)
|
||||||
Settings.Global.getUriFor(Settings.Global.ZEN_MODE),
|
|
||||||
false,
|
|
||||||
settingsObserver)
|
|
||||||
|
|
||||||
mContext.contentResolver.registerContentObserver(
|
secureSettings.registerContentObserverForUser(
|
||||||
Settings.Secure.getUriFor(Settings.Secure.LOCK_SCREEN_SHOW_SILENT_NOTIFICATIONS),
|
Settings.Secure.LOCK_SCREEN_SHOW_SILENT_NOTIFICATIONS,
|
||||||
false,
|
|
||||||
settingsObserver,
|
settingsObserver,
|
||||||
UserHandle.USER_ALL)
|
UserHandle.USER_ALL)
|
||||||
|
|
||||||
@@ -98,41 +132,36 @@ class KeyguardNotificationVisibilityProvider @Inject constructor(
|
|||||||
})
|
})
|
||||||
broadcastDispatcher.registerReceiver(object : BroadcastReceiver() {
|
broadcastDispatcher.registerReceiver(object : BroadcastReceiver() {
|
||||||
override fun onReceive(context: Context, intent: Intent) {
|
override fun onReceive(context: Context, intent: Intent) {
|
||||||
if (keyguardStateController.isShowing()) {
|
if (keyguardStateController.isShowing) {
|
||||||
// maybe public mode changed
|
// maybe public mode changed
|
||||||
notifyStateChanged(intent.action)
|
notifyStateChanged(intent.action!!)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}, IntentFilter(Intent.ACTION_USER_SWITCHED))
|
}, IntentFilter(Intent.ACTION_USER_SWITCHED))
|
||||||
}
|
}
|
||||||
|
|
||||||
fun addOnStateChangedListener(listener: Consumer<String>) {
|
override fun addOnStateChangedListener(listener: Consumer<String>) {
|
||||||
onStateChangedListeners.addIfAbsent(listener)
|
onStateChangedListeners.addIfAbsent(listener)
|
||||||
}
|
}
|
||||||
|
|
||||||
fun removeOnStateChangedListener(listener: Consumer<String>) {
|
override fun removeOnStateChangedListener(listener: Consumer<String>) {
|
||||||
onStateChangedListeners.remove(listener)
|
onStateChangedListeners.remove(listener)
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun notifyStateChanged(reason: String) {
|
private fun notifyStateChanged(reason: String) {
|
||||||
onStateChangedListeners.forEach({ it.accept(reason) })
|
onStateChangedListeners.forEach { it.accept(reason) }
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
override fun shouldHideNotification(entry: NotificationEntry): Boolean {
|
||||||
* 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 {
|
|
||||||
val sbn = entry.sbn
|
val sbn = entry.sbn
|
||||||
// FILTER OUT the notification when the keyguard is showing and...
|
// 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
|
// ... user settings or the device policy manager doesn't allow lockscreen
|
||||||
// notifications;
|
// notifications;
|
||||||
if (!lockscreenUserManager.shouldShowLockscreenNotifications()) {
|
if (!lockscreenUserManager.shouldShowLockscreenNotifications()) {
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
val currUserId: Int = lockscreenUserManager.getCurrentUserId()
|
val currUserId: Int = lockscreenUserManager.currentUserId
|
||||||
val notifUserId =
|
val notifUserId =
|
||||||
if (sbn.user.identifier == UserHandle.USER_ALL) currUserId
|
if (sbn.user.identifier == UserHandle.USER_ALL) currUserId
|
||||||
else sbn.user.identifier
|
else sbn.user.identifier
|
||||||
@@ -178,9 +207,7 @@ class KeyguardNotificationVisibilityProvider @Inject constructor(
|
|||||||
}
|
}
|
||||||
|
|
||||||
private fun readShowSilentNotificationSetting() {
|
private fun readShowSilentNotificationSetting() {
|
||||||
hideSilentNotificationsOnLockscreen = Settings.Secure.getInt(
|
hideSilentNotificationsOnLockscreen =
|
||||||
mContext.getContentResolver(),
|
secureSettings.getBool(Settings.Secure.LOCK_SCREEN_SHOW_SILENT_NOTIFICATIONS, true)
|
||||||
Settings.Secure.LOCK_SCREEN_SHOW_SILENT_NOTIFICATIONS,
|
|
||||||
1) == 0
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -312,7 +312,7 @@ public class NotificationInterruptStateProviderImpl implements NotificationInter
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (mKeyguardNotificationVisibilityProvider.hideNotification(entry)) {
|
if (mKeyguardNotificationVisibilityProvider.shouldHideNotification(entry)) {
|
||||||
mLogger.keyguardHideNotification(entry.getKey());
|
mLogger.keyguardHideNotification(entry.getKey());
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -303,11 +303,82 @@ public interface SettingsProxy {
|
|||||||
default boolean putInt(String name, int value) {
|
default boolean putInt(String name, int value) {
|
||||||
return putIntForUser(name, value, getUserId());
|
return putIntForUser(name, value, getUserId());
|
||||||
}
|
}
|
||||||
|
|
||||||
/** See {@link #putInt(String, int)}. */
|
/** See {@link #putInt(String, int)}. */
|
||||||
default boolean putIntForUser(String name, int value, int userHandle) {
|
default boolean putIntForUser(String name, int value, int userHandle) {
|
||||||
return putStringForUser(name, Integer.toString(value), 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.
|
||||||
|
* <p>
|
||||||
|
* 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
|
* Convenience function for retrieving a single secure settings value
|
||||||
* as a {@code long}. Note that internally setting values are always
|
* as a {@code long}. Note that internally setting values are always
|
||||||
|
|||||||
@@ -22,84 +22,238 @@ import static android.app.NotificationManager.IMPORTANCE_HIGH;
|
|||||||
import static android.app.NotificationManager.IMPORTANCE_MIN;
|
import static android.app.NotificationManager.IMPORTANCE_MIN;
|
||||||
|
|
||||||
import static com.android.systemui.statusbar.notification.collection.EntryUtilKt.modifyEntry;
|
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.assertFalse;
|
||||||
import static org.junit.Assert.assertTrue;
|
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 static org.mockito.Mockito.when;
|
||||||
|
|
||||||
|
import android.content.BroadcastReceiver;
|
||||||
|
import android.content.Context;
|
||||||
|
import android.content.Intent;
|
||||||
import android.os.Handler;
|
import android.os.Handler;
|
||||||
import android.os.UserHandle;
|
import android.os.UserHandle;
|
||||||
|
import android.provider.Settings;
|
||||||
import android.testing.AndroidTestingRunner;
|
import android.testing.AndroidTestingRunner;
|
||||||
|
import android.testing.TestableLooper;
|
||||||
|
|
||||||
import androidx.test.filters.SmallTest;
|
import androidx.test.filters.SmallTest;
|
||||||
|
|
||||||
import com.android.keyguard.KeyguardUpdateMonitor;
|
import com.android.keyguard.KeyguardUpdateMonitor;
|
||||||
|
import com.android.keyguard.KeyguardUpdateMonitorCallback;
|
||||||
|
import com.android.systemui.CoreStartable;
|
||||||
import com.android.systemui.SysuiTestCase;
|
import com.android.systemui.SysuiTestCase;
|
||||||
import com.android.systemui.broadcast.BroadcastDispatcher;
|
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.plugins.statusbar.StatusBarStateController;
|
||||||
import com.android.systemui.statusbar.NotificationLockscreenUserManager;
|
import com.android.systemui.statusbar.NotificationLockscreenUserManager;
|
||||||
import com.android.systemui.statusbar.RankingBuilder;
|
import com.android.systemui.statusbar.RankingBuilder;
|
||||||
import com.android.systemui.statusbar.notification.collection.provider.SectionHeaderVisibilityProvider;
|
|
||||||
import com.android.systemui.statusbar.notification.collection.GroupEntry;
|
import com.android.systemui.statusbar.notification.collection.GroupEntry;
|
||||||
import com.android.systemui.statusbar.notification.collection.GroupEntryBuilder;
|
import com.android.systemui.statusbar.notification.collection.GroupEntryBuilder;
|
||||||
import com.android.systemui.statusbar.notification.collection.NotificationEntry;
|
import com.android.systemui.statusbar.notification.collection.NotificationEntry;
|
||||||
import com.android.systemui.statusbar.notification.collection.NotificationEntryBuilder;
|
import com.android.systemui.statusbar.notification.collection.NotificationEntryBuilder;
|
||||||
import com.android.systemui.statusbar.notification.collection.provider.HighPriorityProvider;
|
import com.android.systemui.statusbar.notification.collection.provider.HighPriorityProvider;
|
||||||
import com.android.systemui.statusbar.policy.KeyguardStateController;
|
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.Before;
|
||||||
import org.junit.Test;
|
import org.junit.Test;
|
||||||
import org.junit.runner.RunWith;
|
import org.junit.runner.RunWith;
|
||||||
|
import org.mockito.ArgumentCaptor;
|
||||||
import org.mockito.Mock;
|
import org.mockito.Mock;
|
||||||
import org.mockito.MockitoAnnotations;
|
import org.mockito.MockitoAnnotations;
|
||||||
|
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.function.Consumer;
|
||||||
|
|
||||||
|
import dagger.BindsInstance;
|
||||||
|
import dagger.Component;
|
||||||
|
|
||||||
@SmallTest
|
@SmallTest
|
||||||
@RunWith(AndroidTestingRunner.class)
|
@RunWith(AndroidTestingRunner.class)
|
||||||
|
@TestableLooper.RunWithLooper
|
||||||
public class KeyguardNotificationVisibilityProviderTest extends SysuiTestCase {
|
public class KeyguardNotificationVisibilityProviderTest extends SysuiTestCase {
|
||||||
private static final int NOTIF_USER_ID = 0;
|
private static final int NOTIF_USER_ID = 0;
|
||||||
private static final int CURR_USER_ID = 1;
|
private static final int CURR_USER_ID = 1;
|
||||||
|
|
||||||
@Mock
|
|
||||||
private Handler mMainHandler;
|
|
||||||
@Mock private KeyguardStateController mKeyguardStateController;
|
@Mock private KeyguardStateController mKeyguardStateController;
|
||||||
@Mock private NotificationLockscreenUserManager mLockscreenUserManager;
|
@Mock private NotificationLockscreenUserManager mLockscreenUserManager;
|
||||||
@Mock private KeyguardUpdateMonitor mKeyguardUpdateMonitor;
|
@Mock private KeyguardUpdateMonitor mKeyguardUpdateMonitor;
|
||||||
@Mock private HighPriorityProvider mHighPriorityProvider;
|
@Mock private HighPriorityProvider mHighPriorityProvider;
|
||||||
@Mock private SectionHeaderVisibilityProvider mSectionHeaderVisibilityProvider;
|
|
||||||
@Mock private KeyguardNotificationVisibilityProvider mKeyguardNotificationVisibilityProvider;
|
|
||||||
@Mock private StatusBarStateController mStatusBarStateController;
|
@Mock private StatusBarStateController mStatusBarStateController;
|
||||||
@Mock private BroadcastDispatcher mBroadcastDispatcher;
|
@Mock private BroadcastDispatcher mBroadcastDispatcher;
|
||||||
|
private final FakeSettings mFakeSettings = new FakeSettings();
|
||||||
|
|
||||||
|
private KeyguardNotificationVisibilityProvider mKeyguardNotificationVisibilityProvider;
|
||||||
private NotificationEntry mEntry;
|
private NotificationEntry mEntry;
|
||||||
|
|
||||||
@Before
|
@Before
|
||||||
public void setup() {
|
public void setup() {
|
||||||
MockitoAnnotations.initMocks(this);
|
MockitoAnnotations.initMocks(this);
|
||||||
// TODO refactor the test of KeyguardNotificationVisibilityProvider out
|
TestComponent component =
|
||||||
mKeyguardNotificationVisibilityProvider = spy(new KeyguardNotificationVisibilityProvider(
|
DaggerKeyguardNotificationVisibilityProviderTest_TestComponent
|
||||||
mContext,
|
.factory()
|
||||||
mMainHandler,
|
.create(
|
||||||
mKeyguardStateController,
|
mContext,
|
||||||
mLockscreenUserManager,
|
new FakeHandler(TestableLooper.get(this).getLooper()),
|
||||||
mKeyguardUpdateMonitor,
|
mKeyguardStateController,
|
||||||
mHighPriorityProvider,
|
mLockscreenUserManager,
|
||||||
mStatusBarStateController,
|
mKeyguardUpdateMonitor,
|
||||||
mBroadcastDispatcher
|
mHighPriorityProvider,
|
||||||
));
|
mStatusBarStateController,
|
||||||
|
mBroadcastDispatcher,
|
||||||
|
mFakeSettings,
|
||||||
|
mFakeSettings);
|
||||||
|
mKeyguardNotificationVisibilityProvider = component.getProvider();
|
||||||
|
for (CoreStartable startable : component.getCoreStartables().values()) {
|
||||||
|
startable.start();
|
||||||
|
}
|
||||||
mEntry = new NotificationEntryBuilder()
|
mEntry = new NotificationEntryBuilder()
|
||||||
.setUser(new UserHandle(NOTIF_USER_ID))
|
.setUser(new UserHandle(NOTIF_USER_ID))
|
||||||
.build();
|
.build();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void notifyListeners_onUnlockedChanged() {
|
||||||
|
ArgumentCaptor<KeyguardStateController.Callback> callbackCaptor =
|
||||||
|
ArgumentCaptor.forClass(KeyguardStateController.Callback.class);
|
||||||
|
verify(mKeyguardStateController).addCallback(callbackCaptor.capture());
|
||||||
|
KeyguardStateController.Callback callback = callbackCaptor.getValue();
|
||||||
|
|
||||||
|
Consumer<String> listener = mock(Consumer.class);
|
||||||
|
mKeyguardNotificationVisibilityProvider.addOnStateChangedListener(listener);
|
||||||
|
|
||||||
|
callback.onUnlockedChanged();
|
||||||
|
|
||||||
|
verify(listener).accept(anyString());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void notifyListeners_onKeyguardShowingChanged() {
|
||||||
|
ArgumentCaptor<KeyguardStateController.Callback> callbackCaptor =
|
||||||
|
ArgumentCaptor.forClass(KeyguardStateController.Callback.class);
|
||||||
|
verify(mKeyguardStateController).addCallback(callbackCaptor.capture());
|
||||||
|
KeyguardStateController.Callback callback = callbackCaptor.getValue();
|
||||||
|
|
||||||
|
Consumer<String> listener = mock(Consumer.class);
|
||||||
|
mKeyguardNotificationVisibilityProvider.addOnStateChangedListener(listener);
|
||||||
|
|
||||||
|
callback.onKeyguardShowingChanged();
|
||||||
|
|
||||||
|
verify(listener).accept(anyString());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void notifyListeners_onStrongAuthStateChanged() {
|
||||||
|
ArgumentCaptor<KeyguardUpdateMonitorCallback> callbackCaptor =
|
||||||
|
ArgumentCaptor.forClass(KeyguardUpdateMonitorCallback.class);
|
||||||
|
verify(mKeyguardUpdateMonitor).registerCallback(callbackCaptor.capture());
|
||||||
|
KeyguardUpdateMonitorCallback callback = callbackCaptor.getValue();
|
||||||
|
|
||||||
|
Consumer<String> listener = mock(Consumer.class);
|
||||||
|
mKeyguardNotificationVisibilityProvider.addOnStateChangedListener(listener);
|
||||||
|
|
||||||
|
callback.onStrongAuthStateChanged(0);
|
||||||
|
|
||||||
|
verify(listener).accept(anyString());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void notifyListeners_onStatusBarStateChanged() {
|
||||||
|
ArgumentCaptor<StatusBarStateController.StateListener> callbackCaptor =
|
||||||
|
ArgumentCaptor.forClass(StatusBarStateController.StateListener.class);
|
||||||
|
verify(mStatusBarStateController).addCallback(callbackCaptor.capture());
|
||||||
|
StatusBarStateController.StateListener callback = callbackCaptor.getValue();
|
||||||
|
|
||||||
|
Consumer<String> listener = mock(Consumer.class);
|
||||||
|
mKeyguardNotificationVisibilityProvider.addOnStateChangedListener(listener);
|
||||||
|
|
||||||
|
callback.onStateChanged(0);
|
||||||
|
|
||||||
|
verify(listener).accept(anyString());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void notifyListeners_onReceiveUserSwitchBroadcast() {
|
||||||
|
ArgumentCaptor<BroadcastReceiver> 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<String> 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<String> 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<String> 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<String> 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<String> listener = mock(Consumer.class);
|
||||||
|
mKeyguardNotificationVisibilityProvider.addOnStateChangedListener(listener);
|
||||||
|
|
||||||
|
mFakeSettings.putInt(Settings.Secure.LOCK_SCREEN_SHOW_SILENT_NOTIFICATIONS, 1);
|
||||||
|
|
||||||
|
verify(listener).accept(anyString());
|
||||||
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void unfilteredState() {
|
public void unfilteredState() {
|
||||||
// GIVEN an 'unfiltered-keyguard-showing' state
|
// GIVEN an 'unfiltered-keyguard-showing' state
|
||||||
setupUnfilteredState(mEntry);
|
setupUnfilteredState(mEntry);
|
||||||
|
|
||||||
// THEN don't filter out the entry
|
// THEN don't filter out the entry
|
||||||
assertFalse(mKeyguardNotificationVisibilityProvider.hideNotification(mEntry));
|
assertFalse(mKeyguardNotificationVisibilityProvider.shouldHideNotification(mEntry));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@@ -109,7 +263,7 @@ public class KeyguardNotificationVisibilityProviderTest extends SysuiTestCase {
|
|||||||
when(mKeyguardStateController.isShowing()).thenReturn(false);
|
when(mKeyguardStateController.isShowing()).thenReturn(false);
|
||||||
|
|
||||||
// THEN don't filter out the entry
|
// THEN don't filter out the entry
|
||||||
assertFalse(mKeyguardNotificationVisibilityProvider.hideNotification(mEntry));
|
assertFalse(mKeyguardNotificationVisibilityProvider.shouldHideNotification(mEntry));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@@ -121,7 +275,7 @@ public class KeyguardNotificationVisibilityProviderTest extends SysuiTestCase {
|
|||||||
when(mLockscreenUserManager.shouldShowLockscreenNotifications()).thenReturn(false);
|
when(mLockscreenUserManager.shouldShowLockscreenNotifications()).thenReturn(false);
|
||||||
|
|
||||||
// THEN filter out the entry
|
// THEN filter out the entry
|
||||||
assertTrue(mKeyguardNotificationVisibilityProvider.hideNotification(mEntry));
|
assertTrue(mKeyguardNotificationVisibilityProvider.shouldHideNotification(mEntry));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@@ -133,7 +287,7 @@ public class KeyguardNotificationVisibilityProviderTest extends SysuiTestCase {
|
|||||||
when(mKeyguardUpdateMonitor.isUserInLockdown(NOTIF_USER_ID)).thenReturn(true);
|
when(mKeyguardUpdateMonitor.isUserInLockdown(NOTIF_USER_ID)).thenReturn(true);
|
||||||
|
|
||||||
// THEN filter out the entry
|
// THEN filter out the entry
|
||||||
assertTrue(mKeyguardNotificationVisibilityProvider.hideNotification(mEntry));
|
assertTrue(mKeyguardNotificationVisibilityProvider.shouldHideNotification(mEntry));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@@ -148,7 +302,7 @@ public class KeyguardNotificationVisibilityProviderTest extends SysuiTestCase {
|
|||||||
.thenReturn(false);
|
.thenReturn(false);
|
||||||
|
|
||||||
// THEN filter out the entry
|
// THEN filter out the entry
|
||||||
assertTrue(mKeyguardNotificationVisibilityProvider.hideNotification(mEntry));
|
assertTrue(mKeyguardNotificationVisibilityProvider.shouldHideNotification(mEntry));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@@ -164,7 +318,7 @@ public class KeyguardNotificationVisibilityProviderTest extends SysuiTestCase {
|
|||||||
.setVisibilityOverride(VISIBILITY_SECRET).build());
|
.setVisibilityOverride(VISIBILITY_SECRET).build());
|
||||||
|
|
||||||
// THEN filter out the entry
|
// THEN filter out the entry
|
||||||
assertTrue(mKeyguardNotificationVisibilityProvider.hideNotification(mEntry));
|
assertTrue(mKeyguardNotificationVisibilityProvider.shouldHideNotification(mEntry));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@@ -180,7 +334,7 @@ public class KeyguardNotificationVisibilityProviderTest extends SysuiTestCase {
|
|||||||
when(mHighPriorityProvider.isHighPriority(mEntry)).thenReturn(false);
|
when(mHighPriorityProvider.isHighPriority(mEntry)).thenReturn(false);
|
||||||
|
|
||||||
// THEN filter out the entry
|
// THEN filter out the entry
|
||||||
assertTrue(mKeyguardNotificationVisibilityProvider.hideNotification(mEntry));
|
assertTrue(mKeyguardNotificationVisibilityProvider.shouldHideNotification(mEntry));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@@ -209,7 +363,8 @@ public class KeyguardNotificationVisibilityProviderTest extends SysuiTestCase {
|
|||||||
when(mHighPriorityProvider.isHighPriority(parent)).thenReturn(true);
|
when(mHighPriorityProvider.isHighPriority(parent)).thenReturn(true);
|
||||||
|
|
||||||
// THEN don't filter out the entry
|
// 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 its parent doesn't exceed threshold to show on lockscreen
|
||||||
when(mHighPriorityProvider.isHighPriority(parent)).thenReturn(false);
|
when(mHighPriorityProvider.isHighPriority(parent)).thenReturn(false);
|
||||||
@@ -218,7 +373,7 @@ public class KeyguardNotificationVisibilityProviderTest extends SysuiTestCase {
|
|||||||
.done());
|
.done());
|
||||||
|
|
||||||
// THEN filter out the entry
|
// 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
|
// notification is high priority, so it shouldn't be filtered
|
||||||
when(mHighPriorityProvider.isHighPriority(mEntry)).thenReturn(true);
|
when(mHighPriorityProvider.isHighPriority(mEntry)).thenReturn(true);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@SysUISingleton
|
||||||
|
@Component(modules = { KeyguardNotificationVisibilityProviderModule.class })
|
||||||
|
interface TestComponent {
|
||||||
|
KeyguardNotificationVisibilityProvider getProvider();
|
||||||
|
Map<Class<?>, 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
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
@@ -24,6 +24,7 @@ package com.android.systemui.util.mockito
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
import org.mockito.ArgumentCaptor
|
import org.mockito.ArgumentCaptor
|
||||||
|
import org.mockito.ArgumentMatcher
|
||||||
import org.mockito.Mockito
|
import org.mockito.Mockito
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -43,6 +44,14 @@ fun <T> eq(obj: T): T = Mockito.eq<T>(obj)
|
|||||||
fun <T> any(type: Class<T>): T = Mockito.any<T>(type)
|
fun <T> any(type: Class<T>): T = Mockito.any<T>(type)
|
||||||
inline fun <reified T> any(): T = any(T::class.java)
|
inline fun <reified T> 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 <T> argThat(matcher: ArgumentMatcher<T>): T = Mockito.argThat(matcher)
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Kotlin type-inferred version of Mockito.nullable()
|
* Kotlin type-inferred version of Mockito.nullable()
|
||||||
*/
|
*/
|
||||||
|
|||||||
Reference in New Issue
Block a user