diff options
3 files changed, 83 insertions, 40 deletions
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/HeadsUpCoordinator.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/HeadsUpCoordinator.kt index 0529c94ed59a..23b5241b79ef 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/HeadsUpCoordinator.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/HeadsUpCoordinator.kt @@ -38,8 +38,7 @@ import com.android.systemui.statusbar.notification.collection.provider.LaunchFul import com.android.systemui.statusbar.notification.collection.render.NodeController import com.android.systemui.statusbar.notification.dagger.IncomingHeader import com.android.systemui.statusbar.notification.interruption.HeadsUpViewBinder -import com.android.systemui.statusbar.notification.interruption.NotificationInterruptStateProvider -import com.android.systemui.statusbar.notification.interruption.NotificationInterruptStateProvider.FullScreenIntentDecision +import com.android.systemui.statusbar.notification.interruption.VisualInterruptionDecisionProvider import com.android.systemui.statusbar.notification.logKey import com.android.systemui.statusbar.notification.stack.BUCKET_HEADS_UP import com.android.systemui.statusbar.policy.HeadsUpManager @@ -69,12 +68,12 @@ class HeadsUpCoordinator @Inject constructor( private val mSystemClock: SystemClock, private val mHeadsUpManager: HeadsUpManager, private val mHeadsUpViewBinder: HeadsUpViewBinder, - private val mNotificationInterruptStateProvider: NotificationInterruptStateProvider, + private val mVisualInterruptionDecisionProvider: VisualInterruptionDecisionProvider, private val mRemoteInputManager: NotificationRemoteInputManager, private val mLaunchFullScreenIntentProvider: LaunchFullScreenIntentProvider, private val mFlags: NotifPipelineFlags, @IncomingHeader private val mIncomingHeaderController: NodeController, - @Main private val mExecutor: DelayableExecutor, + @Main private val mExecutor: DelayableExecutor ) : Coordinator { private val mEntriesBindingUntil = ArrayMap<String, Long>() private val mEntriesUpdateTimes = ArrayMap<String, Long>() @@ -388,18 +387,21 @@ class HeadsUpCoordinator @Inject constructor( override fun onEntryAdded(entry: NotificationEntry) { // First check whether this notification should launch a full screen intent, and // launch it if needed. - val fsiDecision = mNotificationInterruptStateProvider.getFullScreenIntentDecision(entry) - mNotificationInterruptStateProvider.logFullScreenIntentDecision(entry, fsiDecision) - if (fsiDecision.shouldLaunch) { + val fsiDecision = + mVisualInterruptionDecisionProvider.makeUnloggedFullScreenIntentDecision(entry) + mVisualInterruptionDecisionProvider.logFullScreenIntentDecision(fsiDecision) + if (fsiDecision.shouldInterrupt) { mLaunchFullScreenIntentProvider.launchFullScreenIntent(entry) - } else if (fsiDecision == FullScreenIntentDecision.NO_FSI_SUPPRESSED_ONLY_BY_DND) { + } else if (fsiDecision.wouldInterruptWithoutDnd) { // If DND was the only reason this entry was suppressed, note it for potential // reconsideration on later ranking updates. addForFSIReconsideration(entry, mSystemClock.currentTimeMillis()) } - // shouldHeadsUp includes check for whether this notification should be filtered - val shouldHeadsUpEver = mNotificationInterruptStateProvider.shouldHeadsUp(entry) + // makeAndLogHeadsUpDecision includes check for whether this notification should be + // filtered + val shouldHeadsUpEver = + mVisualInterruptionDecisionProvider.makeAndLogHeadsUpDecision(entry).shouldInterrupt mPostedEntries[entry.key] = PostedEntry( entry, wasAdded = true, @@ -420,7 +422,8 @@ class HeadsUpCoordinator @Inject constructor( * up again. */ override fun onEntryUpdated(entry: NotificationEntry) { - val shouldHeadsUpEver = mNotificationInterruptStateProvider.shouldHeadsUp(entry) + val shouldHeadsUpEver = + mVisualInterruptionDecisionProvider.makeAndLogHeadsUpDecision(entry).shouldInterrupt val shouldHeadsUpAgain = shouldHunAgain(entry) val isAlerting = mHeadsUpManager.isAlerting(entry.key) val isBinding = isEntryBinding(entry) @@ -510,26 +513,26 @@ class HeadsUpCoordinator @Inject constructor( // If any of these entries are no longer suppressed, launch the FSI now. if (isCandidateForFSIReconsideration(entry)) { val decision = - mNotificationInterruptStateProvider.getFullScreenIntentDecision(entry) - if (decision.shouldLaunch) { + mVisualInterruptionDecisionProvider.makeUnloggedFullScreenIntentDecision( + entry + ) + if (decision.shouldInterrupt) { // Log both the launch of the full screen and also that this was via a // ranking update, and finally revoke candidacy for FSI reconsideration - mLogger.logEntryUpdatedToFullScreen(entry.key, decision.name) - mNotificationInterruptStateProvider.logFullScreenIntentDecision( - entry, decision) + mLogger.logEntryUpdatedToFullScreen(entry.key, decision.logReason) + mVisualInterruptionDecisionProvider.logFullScreenIntentDecision(decision) mLaunchFullScreenIntentProvider.launchFullScreenIntent(entry) mFSIUpdateCandidates.remove(entry.key) // if we launch the FSI then this is no longer a candidate for HUN continue - } else if (decision == FullScreenIntentDecision.NO_FSI_SUPPRESSED_ONLY_BY_DND) { + } else if (decision.wouldInterruptWithoutDnd) { // decision has not changed; no need to log } else { // some other condition is now blocking FSI; log that and revoke candidacy // for FSI reconsideration - mLogger.logEntryDisqualifiedFromFullScreen(entry.key, decision.name) - mNotificationInterruptStateProvider.logFullScreenIntentDecision( - entry, decision) + mLogger.logEntryDisqualifiedFromFullScreen(entry.key, decision.logReason) + mVisualInterruptionDecisionProvider.logFullScreenIntentDecision(decision) mFSIUpdateCandidates.remove(entry.key) } } @@ -539,13 +542,18 @@ class HeadsUpCoordinator @Inject constructor( // state // - if it is present in PostedEntries and the previous state of shouldHeadsUp // differs from the updated one - val shouldHeadsUpEver = mNotificationInterruptStateProvider.checkHeadsUp(entry, - /* log= */ false) + val decision = + mVisualInterruptionDecisionProvider.makeUnloggedHeadsUpDecision(entry) + val shouldHeadsUpEver = decision.shouldInterrupt val postedShouldHeadsUpEver = mPostedEntries[entry.key]?.shouldHeadsUpEver ?: false val shouldUpdateEntry = postedShouldHeadsUpEver != shouldHeadsUpEver if (shouldUpdateEntry) { - mLogger.logEntryUpdatedByRanking(entry.key, shouldHeadsUpEver) + mLogger.logEntryUpdatedByRanking( + entry.key, + shouldHeadsUpEver, + decision.logReason + ) onEntryUpdated(entry) } } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/HeadsUpCoordinatorLogger.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/HeadsUpCoordinatorLogger.kt index e9365594fad7..32c3c6665b6f 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/HeadsUpCoordinatorLogger.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/HeadsUpCoordinatorLogger.kt @@ -61,12 +61,13 @@ class HeadsUpCoordinatorLogger constructor( }) } - fun logEntryUpdatedByRanking(key: String, shouldHun: Boolean) { + fun logEntryUpdatedByRanking(key: String, shouldHun: Boolean, reason: String) { buffer.log(TAG, LogLevel.DEBUG, { str1 = key bool1 = shouldHun + str2 = reason }, { - "updating entry via ranking applied: $str1 updated shouldHeadsUp=$bool1" + "updating entry via ranking applied: $str1 updated shouldHeadsUp=$bool1 because $str2" }) } diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/HeadsUpCoordinatorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/HeadsUpCoordinatorTest.kt index 67128ff5624a..283efe263f04 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/HeadsUpCoordinatorTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/HeadsUpCoordinatorTest.kt @@ -38,8 +38,10 @@ import com.android.systemui.statusbar.notification.collection.notifcollection.No import com.android.systemui.statusbar.notification.collection.provider.LaunchFullScreenIntentProvider import com.android.systemui.statusbar.notification.collection.render.NodeController import com.android.systemui.statusbar.notification.interruption.HeadsUpViewBinder -import com.android.systemui.statusbar.notification.interruption.NotificationInterruptStateProvider import com.android.systemui.statusbar.notification.interruption.NotificationInterruptStateProvider.FullScreenIntentDecision +import com.android.systemui.statusbar.notification.interruption.NotificationInterruptStateProviderWrapper.DecisionImpl +import com.android.systemui.statusbar.notification.interruption.NotificationInterruptStateProviderWrapper.FullScreenIntentDecisionImpl +import com.android.systemui.statusbar.notification.interruption.VisualInterruptionDecisionProvider import com.android.systemui.statusbar.notification.row.NotifBindPipeline.BindCallback import com.android.systemui.statusbar.phone.NotificationGroupTestHelper import com.android.systemui.statusbar.policy.HeadsUpManager @@ -52,6 +54,7 @@ import com.android.systemui.util.mockito.withArgCaptor import com.android.systemui.util.time.FakeSystemClock import java.util.ArrayList import java.util.function.Consumer +import org.junit.Assert.assertEquals import org.junit.Assert.assertFalse import org.junit.Assert.assertTrue import org.junit.Before @@ -86,7 +89,7 @@ class HeadsUpCoordinatorTest : SysuiTestCase() { private val logger = HeadsUpCoordinatorLogger(logcatLogBuffer(), verbose = true) private val headsUpManager: HeadsUpManager = mock() private val headsUpViewBinder: HeadsUpViewBinder = mock() - private val notificationInterruptStateProvider: NotificationInterruptStateProvider = mock() + private val visualInterruptionDecisionProvider: VisualInterruptionDecisionProvider = mock() private val remoteInputManager: NotificationRemoteInputManager = mock() private val endLifetimeExtension: OnEndLifetimeExtensionCallback = mock() private val headerController: NodeController = mock() @@ -114,7 +117,7 @@ class HeadsUpCoordinatorTest : SysuiTestCase() { systemClock, headsUpManager, headsUpViewBinder, - notificationInterruptStateProvider, + visualInterruptionDecisionProvider, remoteInputManager, launchFullScreenIntentProvider, flags, @@ -168,8 +171,11 @@ class HeadsUpCoordinatorTest : SysuiTestCase() { groupChild2 = helper.createChildNotification(GROUP_ALERT_ALL, 2, "child", 250) groupChild3 = helper.createChildNotification(GROUP_ALERT_ALL, 3, "child", 150) + // Set the default HUN decision + setDefaultShouldHeadsUp(false) + // Set the default FSI decision - setShouldFullScreen(any(), FullScreenIntentDecision.NO_FULL_SCREEN_INTENT) + setDefaultShouldFullScreen(FullScreenIntentDecision.NO_FULL_SCREEN_INTENT) } @Test @@ -1006,31 +1012,59 @@ class HeadsUpCoordinatorTest : SysuiTestCase() { verify(launchFullScreenIntentProvider, never()).launchFullScreenIntent(entry) } + private fun setDefaultShouldHeadsUp(should: Boolean) { + whenever(visualInterruptionDecisionProvider.makeAndLogHeadsUpDecision(any())) + .thenReturn(DecisionImpl.of(should)) + whenever(visualInterruptionDecisionProvider.makeUnloggedHeadsUpDecision(any())) + .thenReturn(DecisionImpl.of(should)) + } + private fun setShouldHeadsUp(entry: NotificationEntry, should: Boolean = true) { - whenever(notificationInterruptStateProvider.shouldHeadsUp(entry)).thenReturn(should) - whenever(notificationInterruptStateProvider.checkHeadsUp(eq(entry), any())) - .thenReturn(should) + whenever(visualInterruptionDecisionProvider.makeAndLogHeadsUpDecision(entry)) + .thenReturn(DecisionImpl.of(should)) + whenever(visualInterruptionDecisionProvider.makeUnloggedHeadsUpDecision(entry)) + .thenReturn(DecisionImpl.of(should)) } - private fun setShouldFullScreen(entry: NotificationEntry, decision: FullScreenIntentDecision) { - whenever(notificationInterruptStateProvider.getFullScreenIntentDecision(entry)) - .thenReturn(decision) + private fun setDefaultShouldFullScreen( + originalDecision: FullScreenIntentDecision + ) { + val provider = visualInterruptionDecisionProvider + whenever(provider.makeUnloggedFullScreenIntentDecision(any())).thenAnswer { + val entry: NotificationEntry = it.getArgument(0) + FullScreenIntentDecisionImpl(entry, originalDecision) + } + } + + private fun setShouldFullScreen( + entry: NotificationEntry, + originalDecision: FullScreenIntentDecision + ) { + whenever( + visualInterruptionDecisionProvider.makeUnloggedFullScreenIntentDecision(entry) + ).thenAnswer { + FullScreenIntentDecisionImpl(entry, originalDecision) + } } private fun verifyLoggedFullScreenIntentDecision( entry: NotificationEntry, - decision: FullScreenIntentDecision + originalDecision: FullScreenIntentDecision ) { - verify(notificationInterruptStateProvider).logFullScreenIntentDecision(entry, decision) + val decision = withArgCaptor { + verify(visualInterruptionDecisionProvider).logFullScreenIntentDecision(capture()) + } + check(decision is FullScreenIntentDecisionImpl) + assertEquals(entry, decision.originalEntry) + assertEquals(originalDecision, decision.originalDecision) } private fun verifyNoFullScreenIntentDecisionLogged() { - verify(notificationInterruptStateProvider, never()) - .logFullScreenIntentDecision(any(), any()) + verify(visualInterruptionDecisionProvider, never()).logFullScreenIntentDecision(any()) } private fun clearInterruptionProviderInvocations() { - clearInvocations(notificationInterruptStateProvider) + clearInvocations(visualInterruptionDecisionProvider) } private fun finishBind(entry: NotificationEntry) { |