Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 2d0f66a6 authored by Steve Elliott's avatar Steve Elliott
Browse files

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
Merged-In: I70661583f0d98c6dac7924667dad89a3664334e2
parent b9bad684
Loading
Loading
Loading
Loading
+1 −1
Original line number Diff line number Diff line
@@ -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);
        }
    };

+3 −1
Original line number Diff line number Diff line
@@ -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 {
+63 −36
Original line number Diff line number Diff line
@@ -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. */
interface KeyguardNotificationVisibilityProvider {
    /**
 * Determines if notifications should be visible based on the state of the keyguard
     * 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.
     */
class KeyguardNotificationVisibilityProvider @Inject constructor(
    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,
    @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<Consumer<String>>()
    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<String>) {
    override fun addOnStateChangedListener(listener: Consumer<String>) {
        onStateChangedListeners.addIfAbsent(listener)
    }

    fun removeOnStateChangedListener(listener: Consumer<String>) {
    override fun removeOnStateChangedListener(listener: Consumer<String>) {
        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
+1 −1
Original line number Diff line number Diff line
@@ -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;
        }
+71 −0
Original line number Diff line number Diff line
@@ -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.
     * <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
     * as a {@code long}.  Note that internally setting values are always
Loading