Loading packages/SystemUI/src/com/android/systemui/statusbar/notification/interruption/FullScreenIntentDecisionProvider.kt 0 → 100644 +158 −0 Original line number Diff line number Diff line /* * Copyright (C) 2023 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.android.systemui.statusbar.notification.interruption import android.app.NotificationManager.IMPORTANCE_HIGH import android.os.PowerManager import com.android.systemui.plugins.statusbar.StatusBarStateController import com.android.systemui.statusbar.StatusBarState.KEYGUARD import com.android.systemui.statusbar.notification.collection.NotificationEntry import com.android.systemui.statusbar.notification.interruption.FullScreenIntentDecisionProvider.DecisionImpl.FSI_DEVICE_DREAMING import com.android.systemui.statusbar.notification.interruption.FullScreenIntentDecisionProvider.DecisionImpl.FSI_DEVICE_NOT_INTERACTIVE import com.android.systemui.statusbar.notification.interruption.FullScreenIntentDecisionProvider.DecisionImpl.FSI_DEVICE_NOT_PROVISIONED import com.android.systemui.statusbar.notification.interruption.FullScreenIntentDecisionProvider.DecisionImpl.FSI_KEYGUARD_OCCLUDED import com.android.systemui.statusbar.notification.interruption.FullScreenIntentDecisionProvider.DecisionImpl.FSI_KEYGUARD_SHOWING import com.android.systemui.statusbar.notification.interruption.FullScreenIntentDecisionProvider.DecisionImpl.FSI_LOCKED_SHADE import com.android.systemui.statusbar.notification.interruption.FullScreenIntentDecisionProvider.DecisionImpl.NO_FSI_EXPECTED_TO_HUN import com.android.systemui.statusbar.notification.interruption.FullScreenIntentDecisionProvider.DecisionImpl.NO_FSI_NOT_IMPORTANT_ENOUGH import com.android.systemui.statusbar.notification.interruption.FullScreenIntentDecisionProvider.DecisionImpl.NO_FSI_NO_FULL_SCREEN_INTENT import com.android.systemui.statusbar.notification.interruption.FullScreenIntentDecisionProvider.DecisionImpl.NO_FSI_NO_HUN_OR_KEYGUARD import com.android.systemui.statusbar.notification.interruption.FullScreenIntentDecisionProvider.DecisionImpl.NO_FSI_PACKAGE_SUSPENDED import com.android.systemui.statusbar.notification.interruption.FullScreenIntentDecisionProvider.DecisionImpl.NO_FSI_SHOW_STICKY_HUN import com.android.systemui.statusbar.notification.interruption.FullScreenIntentDecisionProvider.DecisionImpl.NO_FSI_SUPPRESSED_BY_DND import com.android.systemui.statusbar.notification.interruption.FullScreenIntentDecisionProvider.DecisionImpl.NO_FSI_SUPPRESSED_ONLY_BY_DND import com.android.systemui.statusbar.notification.interruption.FullScreenIntentDecisionProvider.DecisionImpl.NO_FSI_SUPPRESSIVE_BUBBLE_METADATA import com.android.systemui.statusbar.notification.interruption.FullScreenIntentDecisionProvider.DecisionImpl.NO_FSI_SUPPRESSIVE_GROUP_ALERT_BEHAVIOR import com.android.systemui.statusbar.policy.DeviceProvisionedController import com.android.systemui.statusbar.policy.KeyguardStateController class FullScreenIntentDecisionProvider( private val deviceProvisionedController: DeviceProvisionedController, private val keyguardStateController: KeyguardStateController, private val powerManager: PowerManager, private val statusBarStateController: StatusBarStateController ) { interface Decision { val shouldFsi: Boolean val wouldFsiWithoutDnd: Boolean val logReason: String } private enum class DecisionImpl( override val shouldFsi: Boolean, override val logReason: String, override val wouldFsiWithoutDnd: Boolean = shouldFsi, val supersedesDnd: Boolean = false ) : Decision { NO_FSI_NO_FULL_SCREEN_INTENT(false, "no full-screen intent", supersedesDnd = true), NO_FSI_SHOW_STICKY_HUN(false, "full-screen intents are disabled", supersedesDnd = true), NO_FSI_NOT_IMPORTANT_ENOUGH(false, "not important enough"), NO_FSI_SUPPRESSIVE_GROUP_ALERT_BEHAVIOR(false, "suppressive group alert behavior"), NO_FSI_SUPPRESSIVE_BUBBLE_METADATA(false, "suppressive bubble metadata"), NO_FSI_PACKAGE_SUSPENDED(false, "package suspended"), FSI_DEVICE_NOT_INTERACTIVE(true, "device is not interactive"), FSI_DEVICE_DREAMING(true, "device is dreaming"), FSI_KEYGUARD_SHOWING(true, "keyguard is showing"), NO_FSI_EXPECTED_TO_HUN(false, "expected to heads-up instead"), FSI_KEYGUARD_OCCLUDED(true, "keyguard is occluded"), FSI_LOCKED_SHADE(true, "locked shade"), FSI_DEVICE_NOT_PROVISIONED(true, "device not provisioned"), NO_FSI_NO_HUN_OR_KEYGUARD(false, "no HUN or keyguard"), NO_FSI_SUPPRESSED_BY_DND(false, "suppressed by DND", wouldFsiWithoutDnd = false), NO_FSI_SUPPRESSED_ONLY_BY_DND(false, "suppressed only by DND", wouldFsiWithoutDnd = true) } fun makeFullScreenIntentDecision(entry: NotificationEntry, couldHeadsUp: Boolean): Decision { val reasonWithoutDnd = makeDecisionWithoutDnd(entry, couldHeadsUp) val suppressedWithoutDnd = !reasonWithoutDnd.shouldFsi val suppressedByDnd = entry.shouldSuppressFullScreenIntent() val reasonWithDnd = when { reasonWithoutDnd.supersedesDnd -> reasonWithoutDnd suppressedByDnd && !suppressedWithoutDnd -> NO_FSI_SUPPRESSED_ONLY_BY_DND suppressedByDnd -> NO_FSI_SUPPRESSED_BY_DND else -> reasonWithoutDnd } return reasonWithDnd } private fun makeDecisionWithoutDnd( entry: NotificationEntry, couldHeadsUp: Boolean ): DecisionImpl { val sbn = entry.sbn val notification = sbn.notification!! if (notification.fullScreenIntent == null) { return if (entry.isStickyAndNotDemoted) { NO_FSI_SHOW_STICKY_HUN } else { NO_FSI_NO_FULL_SCREEN_INTENT } } if (entry.importance < IMPORTANCE_HIGH) { return NO_FSI_NOT_IMPORTANT_ENOUGH } if (sbn.isGroup && notification.suppressAlertingDueToGrouping()) { return NO_FSI_SUPPRESSIVE_GROUP_ALERT_BEHAVIOR } val bubbleMetadata = notification.bubbleMetadata if (bubbleMetadata != null && bubbleMetadata.isNotificationSuppressed) { return NO_FSI_SUPPRESSIVE_BUBBLE_METADATA } if (entry.ranking.isSuspended) { return NO_FSI_PACKAGE_SUSPENDED } if (!powerManager.isInteractive) { return FSI_DEVICE_NOT_INTERACTIVE } if (statusBarStateController.isDreaming) { return FSI_DEVICE_DREAMING } if (statusBarStateController.state == KEYGUARD) { return FSI_KEYGUARD_SHOWING } if (couldHeadsUp) { return NO_FSI_EXPECTED_TO_HUN } if (keyguardStateController.isShowing) { return if (keyguardStateController.isOccluded) { FSI_KEYGUARD_OCCLUDED } else { FSI_LOCKED_SHADE } } if (!deviceProvisionedController.isDeviceProvisioned) { return FSI_DEVICE_NOT_PROVISIONED } return NO_FSI_NO_HUN_OR_KEYGUARD } } packages/SystemUI/src/com/android/systemui/statusbar/notification/interruption/VisualInterruptionDecisionProviderImpl.kt +46 −49 Original line number Diff line number Diff line Loading @@ -18,7 +18,6 @@ package com.android.systemui.statusbar.notification.interruption import android.hardware.display.AmbientDisplayConfiguration import android.os.Handler import android.os.PowerManager import android.util.Log import com.android.internal.annotations.VisibleForTesting import com.android.systemui.dagger.qualifiers.Main import com.android.systemui.plugins.statusbar.StatusBarStateController Loading @@ -30,7 +29,9 @@ import com.android.systemui.statusbar.notification.interruption.VisualInterrupti import com.android.systemui.statusbar.notification.interruption.VisualInterruptionType.PEEK import com.android.systemui.statusbar.notification.interruption.VisualInterruptionType.PULSE import com.android.systemui.statusbar.policy.BatteryController import com.android.systemui.statusbar.policy.DeviceProvisionedController import com.android.systemui.statusbar.policy.HeadsUpManager import com.android.systemui.statusbar.policy.KeyguardStateController import com.android.systemui.util.settings.GlobalSettings import com.android.systemui.util.time.SystemClock import javax.inject.Inject Loading @@ -40,9 +41,11 @@ class VisualInterruptionDecisionProviderImpl constructor( private val ambientDisplayConfiguration: AmbientDisplayConfiguration, private val batteryController: BatteryController, deviceProvisionedController: DeviceProvisionedController, private val globalSettings: GlobalSettings, private val headsUpManager: HeadsUpManager, private val keyguardNotificationVisibilityProvider: KeyguardNotificationVisibilityProvider, keyguardStateController: KeyguardStateController, private val logger: NotificationInterruptLogger, @Main private val mainHandler: Handler, private val powerManager: PowerManager, Loading @@ -50,6 +53,36 @@ constructor( private val systemClock: SystemClock, private val userTracker: UserTracker, ) : VisualInterruptionDecisionProvider { private class DecisionImpl( override val shouldInterrupt: Boolean, override val logReason: String ) : Decision private class FullScreenIntentDecisionImpl( private val fsiDecision: FullScreenIntentDecisionProvider.Decision ) : FullScreenIntentDecision { override val shouldInterrupt get() = fsiDecision.shouldFsi override val wouldInterruptWithoutDnd get() = fsiDecision.wouldFsiWithoutDnd override val logReason get() = fsiDecision.logReason } private val fullScreenIntentDecisionProvider = FullScreenIntentDecisionProvider( deviceProvisionedController, keyguardStateController, powerManager, statusBarStateController ) private val legacySuppressors = mutableSetOf<NotificationInterruptSuppressor>() private val conditions = mutableListOf<VisualInterruptionCondition>() private val filters = mutableListOf<VisualInterruptionFilter>() private var started = false override fun start() { Loading @@ -76,24 +109,6 @@ constructor( started = true } private class DecisionImpl( override val shouldInterrupt: Boolean, override val logReason: String ) : Decision private class FullScreenIntentDecisionImpl( override val shouldInterrupt: Boolean, override val wouldInterruptWithoutDnd: Boolean, override val logReason: String, val originalEntry: NotificationEntry, ) : FullScreenIntentDecision { var hasBeenLogged = false } private val legacySuppressors = mutableSetOf<NotificationInterruptSuppressor>() private val conditions = mutableListOf<VisualInterruptionCondition>() private val filters = mutableListOf<VisualInterruptionFilter>() override fun addLegacySuppressor(suppressor: NotificationInterruptSuppressor) { legacySuppressors.add(suppressor) } Loading Loading @@ -132,32 +147,21 @@ constructor( return makeHeadsUpDecision(entry).also { logHeadsUpDecision(entry, it) } } override fun makeAndLogBubbleDecision(entry: NotificationEntry): Decision { check(started) return makeBubbleDecision(entry).also { logBubbleDecision(entry, it) } } override fun makeUnloggedFullScreenIntentDecision( entry: NotificationEntry ): FullScreenIntentDecision { check(started) return makeFullScreenDecision(entry) return makeFullScreenIntentDecision(entry) } override fun logFullScreenIntentDecision(decision: FullScreenIntentDecision) { check(started) val decisionImpl = decision as? FullScreenIntentDecisionImpl ?: run { Log.wtf(TAG, "Wrong subclass of FullScreenIntentDecision: $decision") return } if (decision.hasBeenLogged) { Log.wtf(TAG, "Already logged decision: $decision") return } logFullScreenIntentDecision(decisionImpl) decision.hasBeenLogged = true } override fun makeAndLogBubbleDecision(entry: NotificationEntry): Decision { check(started) return makeBubbleDecision(entry).also { logBubbleDecision(entry, it) } // Not yet implemented. } private fun makeHeadsUpDecision(entry: NotificationEntry): DecisionImpl { Loading Loading @@ -234,16 +238,6 @@ constructor( return DecisionImpl(shouldInterrupt = true, logReason = "not suppressed") } private fun makeFullScreenDecision(entry: NotificationEntry): FullScreenIntentDecisionImpl { // Not yet implemented. return FullScreenIntentDecisionImpl( shouldInterrupt = true, wouldInterruptWithoutDnd = true, logReason = "FSI logic not yet implemented in VisualInterruptionDecisionProviderImpl", originalEntry = entry ) } private fun logHeadsUpDecision(entry: NotificationEntry, decision: DecisionImpl) { // Not yet implemented. } Loading @@ -252,8 +246,11 @@ constructor( // Not yet implemented. } private fun logFullScreenIntentDecision(decision: FullScreenIntentDecisionImpl) { // Not yet implemented. private fun makeFullScreenIntentDecision(entry: NotificationEntry): FullScreenIntentDecision { val wouldHeadsUp = makeUnloggedHeadsUpDecision(entry).shouldInterrupt val fsiDecision = fullScreenIntentDecisionProvider.makeFullScreenIntentDecision(entry, wouldHeadsUp) return FullScreenIntentDecisionImpl(fsiDecision) } private fun checkSuppressors(entry: NotificationEntry) = Loading packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/interruption/VisualInterruptionDecisionProviderImplTest.kt +14 −0 Original line number Diff line number Diff line Loading @@ -32,9 +32,11 @@ class VisualInterruptionDecisionProviderImplTest : VisualInterruptionDecisionPro VisualInterruptionDecisionProviderImpl( ambientDisplayConfiguration, batteryController, deviceProvisionedController, globalSettings, headsUpManager, keyguardNotificationVisibilityProvider, keyguardStateController, logger, mainHandler, powerManager, Loading @@ -50,6 +52,7 @@ class VisualInterruptionDecisionProviderImplTest : VisualInterruptionDecisionPro assertPeekNotSuppressed() assertPulseNotSuppressed() assertBubbleNotSuppressed() assertFsiNotSuppressed() } } Loading @@ -59,6 +62,7 @@ class VisualInterruptionDecisionProviderImplTest : VisualInterruptionDecisionPro assertPeekNotSuppressed() assertPulseNotSuppressed() assertBubbleNotSuppressed() assertFsiNotSuppressed() } } Loading @@ -68,6 +72,7 @@ class VisualInterruptionDecisionProviderImplTest : VisualInterruptionDecisionPro assertPeekSuppressed() assertPulseNotSuppressed() assertBubbleNotSuppressed() assertFsiNotSuppressed() } } Loading @@ -77,6 +82,7 @@ class VisualInterruptionDecisionProviderImplTest : VisualInterruptionDecisionPro assertPeekSuppressed() assertPulseNotSuppressed() assertBubbleNotSuppressed() assertFsiNotSuppressed() } } Loading @@ -86,6 +92,7 @@ class VisualInterruptionDecisionProviderImplTest : VisualInterruptionDecisionPro assertPeekNotSuppressed() assertPulseSuppressed() assertBubbleNotSuppressed() assertFsiNotSuppressed() } } Loading @@ -95,6 +102,7 @@ class VisualInterruptionDecisionProviderImplTest : VisualInterruptionDecisionPro assertPeekNotSuppressed() assertPulseSuppressed() assertBubbleNotSuppressed() assertFsiNotSuppressed() } } Loading @@ -104,6 +112,7 @@ class VisualInterruptionDecisionProviderImplTest : VisualInterruptionDecisionPro assertPeekNotSuppressed() assertPulseNotSuppressed() assertBubbleSuppressed() assertFsiNotSuppressed() } } Loading @@ -113,6 +122,7 @@ class VisualInterruptionDecisionProviderImplTest : VisualInterruptionDecisionPro assertPeekNotSuppressed() assertPulseNotSuppressed() assertBubbleSuppressed() assertFsiNotSuppressed() } } Loading Loading @@ -193,6 +203,10 @@ class VisualInterruptionDecisionProviderImplTest : VisualInterruptionDecisionPro assertShouldBubble(buildBubbleEntry()) } private fun assertFsiNotSuppressed() { forEachFsiState { assertShouldFsi(buildFsiEntry()) } } private fun withCondition(condition: VisualInterruptionCondition, block: () -> Unit) { provider.addCondition(condition) block() Loading packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/interruption/VisualInterruptionDecisionProviderTestBase.kt +288 −13 File changed.Preview size limit exceeded, changes collapsed. Show changes packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/policy/FakeDeviceProvisionedController.kt 0 → 100644 +31 −0 Original line number Diff line number Diff line package com.android.systemui.statusbar.policy class FakeDeviceProvisionedController : DeviceProvisionedController { @JvmField var deviceProvisioned = true override fun addCallback(listener: DeviceProvisionedController.DeviceProvisionedListener) { TODO("Not yet implemented") } override fun removeCallback(listener: DeviceProvisionedController.DeviceProvisionedListener) { TODO("Not yet implemented") } override fun isDeviceProvisioned() = deviceProvisioned override fun getCurrentUser(): Int { TODO("Not yet implemented") } override fun isUserSetup(user: Int): Boolean { TODO("Not yet implemented") } override fun isCurrentUserSetup(): Boolean { TODO("Not yet implemented") } override fun isFrpActive(): Boolean { TODO("Not yet implemented") } } Loading
packages/SystemUI/src/com/android/systemui/statusbar/notification/interruption/FullScreenIntentDecisionProvider.kt 0 → 100644 +158 −0 Original line number Diff line number Diff line /* * Copyright (C) 2023 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.android.systemui.statusbar.notification.interruption import android.app.NotificationManager.IMPORTANCE_HIGH import android.os.PowerManager import com.android.systemui.plugins.statusbar.StatusBarStateController import com.android.systemui.statusbar.StatusBarState.KEYGUARD import com.android.systemui.statusbar.notification.collection.NotificationEntry import com.android.systemui.statusbar.notification.interruption.FullScreenIntentDecisionProvider.DecisionImpl.FSI_DEVICE_DREAMING import com.android.systemui.statusbar.notification.interruption.FullScreenIntentDecisionProvider.DecisionImpl.FSI_DEVICE_NOT_INTERACTIVE import com.android.systemui.statusbar.notification.interruption.FullScreenIntentDecisionProvider.DecisionImpl.FSI_DEVICE_NOT_PROVISIONED import com.android.systemui.statusbar.notification.interruption.FullScreenIntentDecisionProvider.DecisionImpl.FSI_KEYGUARD_OCCLUDED import com.android.systemui.statusbar.notification.interruption.FullScreenIntentDecisionProvider.DecisionImpl.FSI_KEYGUARD_SHOWING import com.android.systemui.statusbar.notification.interruption.FullScreenIntentDecisionProvider.DecisionImpl.FSI_LOCKED_SHADE import com.android.systemui.statusbar.notification.interruption.FullScreenIntentDecisionProvider.DecisionImpl.NO_FSI_EXPECTED_TO_HUN import com.android.systemui.statusbar.notification.interruption.FullScreenIntentDecisionProvider.DecisionImpl.NO_FSI_NOT_IMPORTANT_ENOUGH import com.android.systemui.statusbar.notification.interruption.FullScreenIntentDecisionProvider.DecisionImpl.NO_FSI_NO_FULL_SCREEN_INTENT import com.android.systemui.statusbar.notification.interruption.FullScreenIntentDecisionProvider.DecisionImpl.NO_FSI_NO_HUN_OR_KEYGUARD import com.android.systemui.statusbar.notification.interruption.FullScreenIntentDecisionProvider.DecisionImpl.NO_FSI_PACKAGE_SUSPENDED import com.android.systemui.statusbar.notification.interruption.FullScreenIntentDecisionProvider.DecisionImpl.NO_FSI_SHOW_STICKY_HUN import com.android.systemui.statusbar.notification.interruption.FullScreenIntentDecisionProvider.DecisionImpl.NO_FSI_SUPPRESSED_BY_DND import com.android.systemui.statusbar.notification.interruption.FullScreenIntentDecisionProvider.DecisionImpl.NO_FSI_SUPPRESSED_ONLY_BY_DND import com.android.systemui.statusbar.notification.interruption.FullScreenIntentDecisionProvider.DecisionImpl.NO_FSI_SUPPRESSIVE_BUBBLE_METADATA import com.android.systemui.statusbar.notification.interruption.FullScreenIntentDecisionProvider.DecisionImpl.NO_FSI_SUPPRESSIVE_GROUP_ALERT_BEHAVIOR import com.android.systemui.statusbar.policy.DeviceProvisionedController import com.android.systemui.statusbar.policy.KeyguardStateController class FullScreenIntentDecisionProvider( private val deviceProvisionedController: DeviceProvisionedController, private val keyguardStateController: KeyguardStateController, private val powerManager: PowerManager, private val statusBarStateController: StatusBarStateController ) { interface Decision { val shouldFsi: Boolean val wouldFsiWithoutDnd: Boolean val logReason: String } private enum class DecisionImpl( override val shouldFsi: Boolean, override val logReason: String, override val wouldFsiWithoutDnd: Boolean = shouldFsi, val supersedesDnd: Boolean = false ) : Decision { NO_FSI_NO_FULL_SCREEN_INTENT(false, "no full-screen intent", supersedesDnd = true), NO_FSI_SHOW_STICKY_HUN(false, "full-screen intents are disabled", supersedesDnd = true), NO_FSI_NOT_IMPORTANT_ENOUGH(false, "not important enough"), NO_FSI_SUPPRESSIVE_GROUP_ALERT_BEHAVIOR(false, "suppressive group alert behavior"), NO_FSI_SUPPRESSIVE_BUBBLE_METADATA(false, "suppressive bubble metadata"), NO_FSI_PACKAGE_SUSPENDED(false, "package suspended"), FSI_DEVICE_NOT_INTERACTIVE(true, "device is not interactive"), FSI_DEVICE_DREAMING(true, "device is dreaming"), FSI_KEYGUARD_SHOWING(true, "keyguard is showing"), NO_FSI_EXPECTED_TO_HUN(false, "expected to heads-up instead"), FSI_KEYGUARD_OCCLUDED(true, "keyguard is occluded"), FSI_LOCKED_SHADE(true, "locked shade"), FSI_DEVICE_NOT_PROVISIONED(true, "device not provisioned"), NO_FSI_NO_HUN_OR_KEYGUARD(false, "no HUN or keyguard"), NO_FSI_SUPPRESSED_BY_DND(false, "suppressed by DND", wouldFsiWithoutDnd = false), NO_FSI_SUPPRESSED_ONLY_BY_DND(false, "suppressed only by DND", wouldFsiWithoutDnd = true) } fun makeFullScreenIntentDecision(entry: NotificationEntry, couldHeadsUp: Boolean): Decision { val reasonWithoutDnd = makeDecisionWithoutDnd(entry, couldHeadsUp) val suppressedWithoutDnd = !reasonWithoutDnd.shouldFsi val suppressedByDnd = entry.shouldSuppressFullScreenIntent() val reasonWithDnd = when { reasonWithoutDnd.supersedesDnd -> reasonWithoutDnd suppressedByDnd && !suppressedWithoutDnd -> NO_FSI_SUPPRESSED_ONLY_BY_DND suppressedByDnd -> NO_FSI_SUPPRESSED_BY_DND else -> reasonWithoutDnd } return reasonWithDnd } private fun makeDecisionWithoutDnd( entry: NotificationEntry, couldHeadsUp: Boolean ): DecisionImpl { val sbn = entry.sbn val notification = sbn.notification!! if (notification.fullScreenIntent == null) { return if (entry.isStickyAndNotDemoted) { NO_FSI_SHOW_STICKY_HUN } else { NO_FSI_NO_FULL_SCREEN_INTENT } } if (entry.importance < IMPORTANCE_HIGH) { return NO_FSI_NOT_IMPORTANT_ENOUGH } if (sbn.isGroup && notification.suppressAlertingDueToGrouping()) { return NO_FSI_SUPPRESSIVE_GROUP_ALERT_BEHAVIOR } val bubbleMetadata = notification.bubbleMetadata if (bubbleMetadata != null && bubbleMetadata.isNotificationSuppressed) { return NO_FSI_SUPPRESSIVE_BUBBLE_METADATA } if (entry.ranking.isSuspended) { return NO_FSI_PACKAGE_SUSPENDED } if (!powerManager.isInteractive) { return FSI_DEVICE_NOT_INTERACTIVE } if (statusBarStateController.isDreaming) { return FSI_DEVICE_DREAMING } if (statusBarStateController.state == KEYGUARD) { return FSI_KEYGUARD_SHOWING } if (couldHeadsUp) { return NO_FSI_EXPECTED_TO_HUN } if (keyguardStateController.isShowing) { return if (keyguardStateController.isOccluded) { FSI_KEYGUARD_OCCLUDED } else { FSI_LOCKED_SHADE } } if (!deviceProvisionedController.isDeviceProvisioned) { return FSI_DEVICE_NOT_PROVISIONED } return NO_FSI_NO_HUN_OR_KEYGUARD } }
packages/SystemUI/src/com/android/systemui/statusbar/notification/interruption/VisualInterruptionDecisionProviderImpl.kt +46 −49 Original line number Diff line number Diff line Loading @@ -18,7 +18,6 @@ package com.android.systemui.statusbar.notification.interruption import android.hardware.display.AmbientDisplayConfiguration import android.os.Handler import android.os.PowerManager import android.util.Log import com.android.internal.annotations.VisibleForTesting import com.android.systemui.dagger.qualifiers.Main import com.android.systemui.plugins.statusbar.StatusBarStateController Loading @@ -30,7 +29,9 @@ import com.android.systemui.statusbar.notification.interruption.VisualInterrupti import com.android.systemui.statusbar.notification.interruption.VisualInterruptionType.PEEK import com.android.systemui.statusbar.notification.interruption.VisualInterruptionType.PULSE import com.android.systemui.statusbar.policy.BatteryController import com.android.systemui.statusbar.policy.DeviceProvisionedController import com.android.systemui.statusbar.policy.HeadsUpManager import com.android.systemui.statusbar.policy.KeyguardStateController import com.android.systemui.util.settings.GlobalSettings import com.android.systemui.util.time.SystemClock import javax.inject.Inject Loading @@ -40,9 +41,11 @@ class VisualInterruptionDecisionProviderImpl constructor( private val ambientDisplayConfiguration: AmbientDisplayConfiguration, private val batteryController: BatteryController, deviceProvisionedController: DeviceProvisionedController, private val globalSettings: GlobalSettings, private val headsUpManager: HeadsUpManager, private val keyguardNotificationVisibilityProvider: KeyguardNotificationVisibilityProvider, keyguardStateController: KeyguardStateController, private val logger: NotificationInterruptLogger, @Main private val mainHandler: Handler, private val powerManager: PowerManager, Loading @@ -50,6 +53,36 @@ constructor( private val systemClock: SystemClock, private val userTracker: UserTracker, ) : VisualInterruptionDecisionProvider { private class DecisionImpl( override val shouldInterrupt: Boolean, override val logReason: String ) : Decision private class FullScreenIntentDecisionImpl( private val fsiDecision: FullScreenIntentDecisionProvider.Decision ) : FullScreenIntentDecision { override val shouldInterrupt get() = fsiDecision.shouldFsi override val wouldInterruptWithoutDnd get() = fsiDecision.wouldFsiWithoutDnd override val logReason get() = fsiDecision.logReason } private val fullScreenIntentDecisionProvider = FullScreenIntentDecisionProvider( deviceProvisionedController, keyguardStateController, powerManager, statusBarStateController ) private val legacySuppressors = mutableSetOf<NotificationInterruptSuppressor>() private val conditions = mutableListOf<VisualInterruptionCondition>() private val filters = mutableListOf<VisualInterruptionFilter>() private var started = false override fun start() { Loading @@ -76,24 +109,6 @@ constructor( started = true } private class DecisionImpl( override val shouldInterrupt: Boolean, override val logReason: String ) : Decision private class FullScreenIntentDecisionImpl( override val shouldInterrupt: Boolean, override val wouldInterruptWithoutDnd: Boolean, override val logReason: String, val originalEntry: NotificationEntry, ) : FullScreenIntentDecision { var hasBeenLogged = false } private val legacySuppressors = mutableSetOf<NotificationInterruptSuppressor>() private val conditions = mutableListOf<VisualInterruptionCondition>() private val filters = mutableListOf<VisualInterruptionFilter>() override fun addLegacySuppressor(suppressor: NotificationInterruptSuppressor) { legacySuppressors.add(suppressor) } Loading Loading @@ -132,32 +147,21 @@ constructor( return makeHeadsUpDecision(entry).also { logHeadsUpDecision(entry, it) } } override fun makeAndLogBubbleDecision(entry: NotificationEntry): Decision { check(started) return makeBubbleDecision(entry).also { logBubbleDecision(entry, it) } } override fun makeUnloggedFullScreenIntentDecision( entry: NotificationEntry ): FullScreenIntentDecision { check(started) return makeFullScreenDecision(entry) return makeFullScreenIntentDecision(entry) } override fun logFullScreenIntentDecision(decision: FullScreenIntentDecision) { check(started) val decisionImpl = decision as? FullScreenIntentDecisionImpl ?: run { Log.wtf(TAG, "Wrong subclass of FullScreenIntentDecision: $decision") return } if (decision.hasBeenLogged) { Log.wtf(TAG, "Already logged decision: $decision") return } logFullScreenIntentDecision(decisionImpl) decision.hasBeenLogged = true } override fun makeAndLogBubbleDecision(entry: NotificationEntry): Decision { check(started) return makeBubbleDecision(entry).also { logBubbleDecision(entry, it) } // Not yet implemented. } private fun makeHeadsUpDecision(entry: NotificationEntry): DecisionImpl { Loading Loading @@ -234,16 +238,6 @@ constructor( return DecisionImpl(shouldInterrupt = true, logReason = "not suppressed") } private fun makeFullScreenDecision(entry: NotificationEntry): FullScreenIntentDecisionImpl { // Not yet implemented. return FullScreenIntentDecisionImpl( shouldInterrupt = true, wouldInterruptWithoutDnd = true, logReason = "FSI logic not yet implemented in VisualInterruptionDecisionProviderImpl", originalEntry = entry ) } private fun logHeadsUpDecision(entry: NotificationEntry, decision: DecisionImpl) { // Not yet implemented. } Loading @@ -252,8 +246,11 @@ constructor( // Not yet implemented. } private fun logFullScreenIntentDecision(decision: FullScreenIntentDecisionImpl) { // Not yet implemented. private fun makeFullScreenIntentDecision(entry: NotificationEntry): FullScreenIntentDecision { val wouldHeadsUp = makeUnloggedHeadsUpDecision(entry).shouldInterrupt val fsiDecision = fullScreenIntentDecisionProvider.makeFullScreenIntentDecision(entry, wouldHeadsUp) return FullScreenIntentDecisionImpl(fsiDecision) } private fun checkSuppressors(entry: NotificationEntry) = Loading
packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/interruption/VisualInterruptionDecisionProviderImplTest.kt +14 −0 Original line number Diff line number Diff line Loading @@ -32,9 +32,11 @@ class VisualInterruptionDecisionProviderImplTest : VisualInterruptionDecisionPro VisualInterruptionDecisionProviderImpl( ambientDisplayConfiguration, batteryController, deviceProvisionedController, globalSettings, headsUpManager, keyguardNotificationVisibilityProvider, keyguardStateController, logger, mainHandler, powerManager, Loading @@ -50,6 +52,7 @@ class VisualInterruptionDecisionProviderImplTest : VisualInterruptionDecisionPro assertPeekNotSuppressed() assertPulseNotSuppressed() assertBubbleNotSuppressed() assertFsiNotSuppressed() } } Loading @@ -59,6 +62,7 @@ class VisualInterruptionDecisionProviderImplTest : VisualInterruptionDecisionPro assertPeekNotSuppressed() assertPulseNotSuppressed() assertBubbleNotSuppressed() assertFsiNotSuppressed() } } Loading @@ -68,6 +72,7 @@ class VisualInterruptionDecisionProviderImplTest : VisualInterruptionDecisionPro assertPeekSuppressed() assertPulseNotSuppressed() assertBubbleNotSuppressed() assertFsiNotSuppressed() } } Loading @@ -77,6 +82,7 @@ class VisualInterruptionDecisionProviderImplTest : VisualInterruptionDecisionPro assertPeekSuppressed() assertPulseNotSuppressed() assertBubbleNotSuppressed() assertFsiNotSuppressed() } } Loading @@ -86,6 +92,7 @@ class VisualInterruptionDecisionProviderImplTest : VisualInterruptionDecisionPro assertPeekNotSuppressed() assertPulseSuppressed() assertBubbleNotSuppressed() assertFsiNotSuppressed() } } Loading @@ -95,6 +102,7 @@ class VisualInterruptionDecisionProviderImplTest : VisualInterruptionDecisionPro assertPeekNotSuppressed() assertPulseSuppressed() assertBubbleNotSuppressed() assertFsiNotSuppressed() } } Loading @@ -104,6 +112,7 @@ class VisualInterruptionDecisionProviderImplTest : VisualInterruptionDecisionPro assertPeekNotSuppressed() assertPulseNotSuppressed() assertBubbleSuppressed() assertFsiNotSuppressed() } } Loading @@ -113,6 +122,7 @@ class VisualInterruptionDecisionProviderImplTest : VisualInterruptionDecisionPro assertPeekNotSuppressed() assertPulseNotSuppressed() assertBubbleSuppressed() assertFsiNotSuppressed() } } Loading Loading @@ -193,6 +203,10 @@ class VisualInterruptionDecisionProviderImplTest : VisualInterruptionDecisionPro assertShouldBubble(buildBubbleEntry()) } private fun assertFsiNotSuppressed() { forEachFsiState { assertShouldFsi(buildFsiEntry()) } } private fun withCondition(condition: VisualInterruptionCondition, block: () -> Unit) { provider.addCondition(condition) block() Loading
packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/interruption/VisualInterruptionDecisionProviderTestBase.kt +288 −13 File changed.Preview size limit exceeded, changes collapsed. Show changes
packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/policy/FakeDeviceProvisionedController.kt 0 → 100644 +31 −0 Original line number Diff line number Diff line package com.android.systemui.statusbar.policy class FakeDeviceProvisionedController : DeviceProvisionedController { @JvmField var deviceProvisioned = true override fun addCallback(listener: DeviceProvisionedController.DeviceProvisionedListener) { TODO("Not yet implemented") } override fun removeCallback(listener: DeviceProvisionedController.DeviceProvisionedListener) { TODO("Not yet implemented") } override fun isDeviceProvisioned() = deviceProvisioned override fun getCurrentUser(): Int { TODO("Not yet implemented") } override fun isUserSetup(user: Int): Boolean { TODO("Not yet implemented") } override fun isCurrentUserSetup(): Boolean { TODO("Not yet implemented") } override fun isFrpActive(): Boolean { TODO("Not yet implemented") } }