blob: 70c9a16b9d7a13b1bedaaba148bb07c8c03de7ae [file] [log] [blame]
package com.android.systemui.statusbar.notification.interruption
import android.app.Notification
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 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.qualifiers.Main
import com.android.systemui.plugins.statusbar.StatusBarStateController
import com.android.systemui.statusbar.NotificationLockscreenUserManager
import com.android.systemui.statusbar.notification.collection.ListEntry
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 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(
context: Context,
@Main private val handler: Handler,
private val keyguardStateController: KeyguardStateController,
private val lockscreenUserManager: NotificationLockscreenUserManager,
private val keyguardUpdateMonitor: KeyguardUpdateMonitor,
private val highPriorityProvider: HighPriorityProvider,
private val statusBarStateController: StatusBarStateController,
private val broadcastDispatcher: BroadcastDispatcher
) : CoreStartable(context) {
private val onStateChangedListeners = ListenerSet<Consumer<String>>()
private var hideSilentNotificationsOnLockscreen: Boolean = false
override fun start() {
readShowSilentNotificationSetting()
keyguardStateController.addCallback(object : KeyguardStateController.Callback {
override fun onUnlockedChanged() {
notifyStateChanged("onUnlockedChanged")
}
override fun onKeyguardShowingChanged() {
notifyStateChanged("onKeyguardShowingChanged")
}
})
keyguardUpdateMonitor.registerCallback(object : KeyguardUpdateMonitorCallback() {
override fun onStrongAuthStateChanged(userId: Int) {
notifyStateChanged("onStrongAuthStateChanged")
}
})
// register lockscreen settings changed callbacks:
val settingsObserver: ContentObserver = object : ContentObserver(handler) {
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,
settingsObserver,
UserHandle.USER_ALL)
mContext.contentResolver.registerContentObserver(
Settings.Secure.getUriFor(Settings.Secure.LOCK_SCREEN_ALLOW_PRIVATE_NOTIFICATIONS),
true,
settingsObserver,
UserHandle.USER_ALL)
mContext.contentResolver.registerContentObserver(
Settings.Global.getUriFor(Settings.Global.ZEN_MODE),
false,
settingsObserver)
mContext.contentResolver.registerContentObserver(
Settings.Secure.getUriFor(Settings.Secure.LOCK_SCREEN_SHOW_SILENT_NOTIFICATIONS),
false,
settingsObserver,
UserHandle.USER_ALL)
// register (maybe) public mode changed callbacks:
statusBarStateController.addCallback(object : StatusBarStateController.StateListener {
override fun onStateChanged(state: Int) {
notifyStateChanged("onStatusBarStateChanged")
}
})
broadcastDispatcher.registerReceiver(object : BroadcastReceiver() {
override fun onReceive(context: Context, intent: Intent) {
if (keyguardStateController.isShowing()) {
// maybe public mode changed
notifyStateChanged(intent.action)
}
}
}, IntentFilter(Intent.ACTION_USER_SWITCHED))
}
fun addOnStateChangedListener(listener: Consumer<String>) {
onStateChangedListeners.addIfAbsent(listener)
}
fun removeOnStateChangedListener(listener: Consumer<String>) {
onStateChangedListeners.remove(listener)
}
private fun notifyStateChanged(reason: String) {
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 {
val sbn = entry.sbn
// FILTER OUT the notification when the keyguard is showing and...
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 notifUserId =
if (sbn.user.identifier == UserHandle.USER_ALL) currUserId
else sbn.user.identifier
// ... user is in lockdown
if (keyguardUpdateMonitor.isUserInLockdown(currUserId) ||
keyguardUpdateMonitor.isUserInLockdown(notifUserId)) {
return true
}
// ... device is in public mode and the user's settings doesn't allow
// notifications to show in public mode
if (lockscreenUserManager.isLockscreenPublicMode(currUserId) ||
lockscreenUserManager.isLockscreenPublicMode(notifUserId)) {
if (entry.ranking.lockscreenVisibilityOverride == Notification.VISIBILITY_SECRET) {
return true
}
if (!lockscreenUserManager.userAllowsNotificationsInPublic(currUserId) ||
!lockscreenUserManager.userAllowsNotificationsInPublic(
notifUserId)) {
return true
}
}
// ... neither this notification nor its group have high enough priority
// to be shown on the lockscreen
if (entry.parent != null) {
val parent = entry.parent
if (priorityExceedsLockscreenShowingThreshold(parent)) {
return false
}
}
return !priorityExceedsLockscreenShowingThreshold(entry)
}
return false
}
private fun priorityExceedsLockscreenShowingThreshold(entry: ListEntry?): Boolean =
when {
entry == null -> false
hideSilentNotificationsOnLockscreen -> highPriorityProvider.isHighPriority(entry)
else -> entry.representativeEntry?.ranking?.isAmbient == false
}
private fun readShowSilentNotificationSetting() {
hideSilentNotificationsOnLockscreen = Settings.Secure.getInt(
mContext.getContentResolver(),
Settings.Secure.LOCK_SCREEN_SHOW_SILENT_NOTIFICATIONS,
1) == 0
}
}