Loading packages/SystemUI/src/com/android/systemui/statusbar/notification/interruption/CommonVisualInterruptionSuppressors.kt 0 → 100644 +90 −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.database.ContentObserver import android.hardware.display.AmbientDisplayConfiguration import android.os.Handler import android.provider.Settings.Global.HEADS_UP_NOTIFICATIONS_ENABLED import android.provider.Settings.Global.HEADS_UP_OFF import com.android.systemui.dagger.qualifiers.Main import com.android.systemui.settings.UserTracker 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.HeadsUpManager import com.android.systemui.util.settings.GlobalSettings class PeekDisabledSuppressor( private val globalSettings: GlobalSettings, private val headsUpManager: HeadsUpManager, private val logger: NotificationInterruptLogger, @Main private val mainHandler: Handler, ) : VisualInterruptionCondition(types = setOf(PEEK), reason = "peek setting disabled") { private var isEnabled = false override fun shouldSuppress(): Boolean = !isEnabled override fun start() { val observer = object : ContentObserver(mainHandler) { override fun onChange(selfChange: Boolean) { val wasEnabled = isEnabled isEnabled = globalSettings.getInt(HEADS_UP_NOTIFICATIONS_ENABLED, HEADS_UP_OFF) != HEADS_UP_OFF // QQQ: Do we want to log this even if it hasn't changed? logger.logHeadsUpFeatureChanged(isEnabled) // QQQ: Is there a better place for this side effect? What if HeadsUpManager // registered for it directly? if (wasEnabled && !isEnabled) { logger.logWillDismissAll() headsUpManager.releaseAllImmediately() } } } globalSettings.registerContentObserver( globalSettings.getUriFor(HEADS_UP_NOTIFICATIONS_ENABLED), /* notifyForDescendants = */ true, observer ) // QQQ: Do we need to register for SETTING_HEADS_UP_TICKER? It seems unused. observer.onChange(/* selfChange = */ true) } } class PulseDisabledSuppressor( private val ambientDisplayConfiguration: AmbientDisplayConfiguration, private val userTracker: UserTracker, ) : VisualInterruptionCondition(types = setOf(PULSE), reason = "pulse setting disabled") { override fun shouldSuppress(): Boolean = !ambientDisplayConfiguration.pulseOnNotificationEnabled(userTracker.userId) } class PulseBatterySaverSuppressor(private val batteryController: BatteryController) : VisualInterruptionCondition( types = setOf(PULSE), reason = "pulsing disabled by battery saver" ) { override fun shouldSuppress() = batteryController.isAodPowerSave() } packages/SystemUI/src/com/android/systemui/statusbar/notification/interruption/VisualInterruptionDecisionProvider.kt +7 −0 Original line number Diff line number Diff line Loading @@ -51,6 +51,13 @@ interface VisualInterruptionDecisionProvider { val wouldInterruptWithoutDnd: Boolean } /** * Initializes the provider. * * Must be called before any method except [addLegacySuppressor]. */ fun start() {} /** * Adds a [component][suppressor] that can suppress visual interruptions. * Loading packages/SystemUI/src/com/android/systemui/statusbar/notification/interruption/VisualInterruptionDecisionProviderImpl.kt +19 −0 Original line number Diff line number Diff line Loading @@ -48,6 +48,18 @@ constructor( private val systemClock: SystemClock, private val userTracker: UserTracker, ) : VisualInterruptionDecisionProvider { private var started = false override fun start() { check(!started) addCondition(PeekDisabledSuppressor(globalSettings, headsUpManager, logger, mainHandler)) addCondition(PulseDisabledSuppressor(ambientDisplayConfiguration, userTracker)) addCondition(PulseBatterySaverSuppressor(batteryController)) started = true } private class DecisionImpl( override val shouldInterrupt: Boolean, override val logReason: String Loading Loading @@ -76,27 +88,33 @@ constructor( fun addCondition(condition: VisualInterruptionCondition) { conditions.add(condition) condition.start() } fun addFilter(filter: VisualInterruptionFilter) { filters.add(filter) filter.start() } override fun makeUnloggedHeadsUpDecision(entry: NotificationEntry): Decision { check(started) return makeHeadsUpDecision(entry) } override fun makeAndLogHeadsUpDecision(entry: NotificationEntry): Decision { check(started) return makeHeadsUpDecision(entry).also { logHeadsUpDecision(entry, it) } } override fun makeUnloggedFullScreenIntentDecision( entry: NotificationEntry ): FullScreenIntentDecision { check(started) return makeFullScreenDecision(entry) } override fun logFullScreenIntentDecision(decision: FullScreenIntentDecision) { check(started) val decisionImpl = decision as? FullScreenIntentDecisionImpl ?: run { Loading @@ -112,6 +130,7 @@ constructor( } override fun makeAndLogBubbleDecision(entry: NotificationEntry): Decision { check(started) return makeBubbleDecision(entry).also { logBubbleDecision(entry, it) } } Loading packages/SystemUI/src/com/android/systemui/statusbar/notification/interruption/VisualInterruptionSuppressor.kt +6 −0 Original line number Diff line number Diff line Loading @@ -51,6 +51,12 @@ sealed interface VisualInterruptionSuppressor { /** An optional UiEvent ID to be recorded when this suppresses an interruption. */ val uiEventId: UiEventEnum? /** * Called after the suppressor is added to the [VisualInterruptionDecisionProvider] but before * any other methods are called on the suppressor. */ fun start() {} } /** A reason why visual interruptions might be suppressed regardless of the notification. */ Loading packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/interruption/NotificationInterruptStateProviderWrapperTest.kt +16 −17 Original line number Diff line number Diff line Loading @@ -54,7 +54,6 @@ class NotificationInterruptStateProviderWrapperTest : VisualInterruptionDecision systemClock, globalSettings, ) .also { it.mUseHeadsUp = true } ) } Loading Loading
packages/SystemUI/src/com/android/systemui/statusbar/notification/interruption/CommonVisualInterruptionSuppressors.kt 0 → 100644 +90 −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.database.ContentObserver import android.hardware.display.AmbientDisplayConfiguration import android.os.Handler import android.provider.Settings.Global.HEADS_UP_NOTIFICATIONS_ENABLED import android.provider.Settings.Global.HEADS_UP_OFF import com.android.systemui.dagger.qualifiers.Main import com.android.systemui.settings.UserTracker 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.HeadsUpManager import com.android.systemui.util.settings.GlobalSettings class PeekDisabledSuppressor( private val globalSettings: GlobalSettings, private val headsUpManager: HeadsUpManager, private val logger: NotificationInterruptLogger, @Main private val mainHandler: Handler, ) : VisualInterruptionCondition(types = setOf(PEEK), reason = "peek setting disabled") { private var isEnabled = false override fun shouldSuppress(): Boolean = !isEnabled override fun start() { val observer = object : ContentObserver(mainHandler) { override fun onChange(selfChange: Boolean) { val wasEnabled = isEnabled isEnabled = globalSettings.getInt(HEADS_UP_NOTIFICATIONS_ENABLED, HEADS_UP_OFF) != HEADS_UP_OFF // QQQ: Do we want to log this even if it hasn't changed? logger.logHeadsUpFeatureChanged(isEnabled) // QQQ: Is there a better place for this side effect? What if HeadsUpManager // registered for it directly? if (wasEnabled && !isEnabled) { logger.logWillDismissAll() headsUpManager.releaseAllImmediately() } } } globalSettings.registerContentObserver( globalSettings.getUriFor(HEADS_UP_NOTIFICATIONS_ENABLED), /* notifyForDescendants = */ true, observer ) // QQQ: Do we need to register for SETTING_HEADS_UP_TICKER? It seems unused. observer.onChange(/* selfChange = */ true) } } class PulseDisabledSuppressor( private val ambientDisplayConfiguration: AmbientDisplayConfiguration, private val userTracker: UserTracker, ) : VisualInterruptionCondition(types = setOf(PULSE), reason = "pulse setting disabled") { override fun shouldSuppress(): Boolean = !ambientDisplayConfiguration.pulseOnNotificationEnabled(userTracker.userId) } class PulseBatterySaverSuppressor(private val batteryController: BatteryController) : VisualInterruptionCondition( types = setOf(PULSE), reason = "pulsing disabled by battery saver" ) { override fun shouldSuppress() = batteryController.isAodPowerSave() }
packages/SystemUI/src/com/android/systemui/statusbar/notification/interruption/VisualInterruptionDecisionProvider.kt +7 −0 Original line number Diff line number Diff line Loading @@ -51,6 +51,13 @@ interface VisualInterruptionDecisionProvider { val wouldInterruptWithoutDnd: Boolean } /** * Initializes the provider. * * Must be called before any method except [addLegacySuppressor]. */ fun start() {} /** * Adds a [component][suppressor] that can suppress visual interruptions. * Loading
packages/SystemUI/src/com/android/systemui/statusbar/notification/interruption/VisualInterruptionDecisionProviderImpl.kt +19 −0 Original line number Diff line number Diff line Loading @@ -48,6 +48,18 @@ constructor( private val systemClock: SystemClock, private val userTracker: UserTracker, ) : VisualInterruptionDecisionProvider { private var started = false override fun start() { check(!started) addCondition(PeekDisabledSuppressor(globalSettings, headsUpManager, logger, mainHandler)) addCondition(PulseDisabledSuppressor(ambientDisplayConfiguration, userTracker)) addCondition(PulseBatterySaverSuppressor(batteryController)) started = true } private class DecisionImpl( override val shouldInterrupt: Boolean, override val logReason: String Loading Loading @@ -76,27 +88,33 @@ constructor( fun addCondition(condition: VisualInterruptionCondition) { conditions.add(condition) condition.start() } fun addFilter(filter: VisualInterruptionFilter) { filters.add(filter) filter.start() } override fun makeUnloggedHeadsUpDecision(entry: NotificationEntry): Decision { check(started) return makeHeadsUpDecision(entry) } override fun makeAndLogHeadsUpDecision(entry: NotificationEntry): Decision { check(started) return makeHeadsUpDecision(entry).also { logHeadsUpDecision(entry, it) } } override fun makeUnloggedFullScreenIntentDecision( entry: NotificationEntry ): FullScreenIntentDecision { check(started) return makeFullScreenDecision(entry) } override fun logFullScreenIntentDecision(decision: FullScreenIntentDecision) { check(started) val decisionImpl = decision as? FullScreenIntentDecisionImpl ?: run { Loading @@ -112,6 +130,7 @@ constructor( } override fun makeAndLogBubbleDecision(entry: NotificationEntry): Decision { check(started) return makeBubbleDecision(entry).also { logBubbleDecision(entry, it) } } Loading
packages/SystemUI/src/com/android/systemui/statusbar/notification/interruption/VisualInterruptionSuppressor.kt +6 −0 Original line number Diff line number Diff line Loading @@ -51,6 +51,12 @@ sealed interface VisualInterruptionSuppressor { /** An optional UiEvent ID to be recorded when this suppresses an interruption. */ val uiEventId: UiEventEnum? /** * Called after the suppressor is added to the [VisualInterruptionDecisionProvider] but before * any other methods are called on the suppressor. */ fun start() {} } /** A reason why visual interruptions might be suppressed regardless of the notification. */ Loading
packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/interruption/NotificationInterruptStateProviderWrapperTest.kt +16 −17 Original line number Diff line number Diff line Loading @@ -54,7 +54,6 @@ class NotificationInterruptStateProviderWrapperTest : VisualInterruptionDecision systemClock, globalSettings, ) .also { it.mUseHeadsUp = true } ) } Loading