diff options
3 files changed, 74 insertions, 5 deletions
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/KeyguardCoordinator.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/KeyguardCoordinator.kt index 6bf7668f080c..82bd45ce2279 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/KeyguardCoordinator.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/KeyguardCoordinator.kt @@ -14,6 +14,8 @@ * limitations under the License. */ +@file:OptIn(ExperimentalCoroutinesApi::class) + package com.android.systemui.statusbar.notification.collection.coordinator import android.os.UserHandle @@ -39,14 +41,20 @@ import com.android.systemui.statusbar.policy.headsUpEvents import com.android.systemui.util.settings.SecureSettings import com.android.systemui.util.settings.SettingsProxyExt.observerFlow import javax.inject.Inject +import kotlin.time.Duration +import kotlin.time.Duration.Companion.seconds import kotlinx.coroutines.CoroutineDispatcher import kotlinx.coroutines.CoroutineScope +import kotlinx.coroutines.ExperimentalCoroutinesApi import kotlinx.coroutines.coroutineScope +import kotlinx.coroutines.delay import kotlinx.coroutines.flow.collectLatest import kotlinx.coroutines.flow.conflate +import kotlinx.coroutines.flow.first import kotlinx.coroutines.flow.flowOn import kotlinx.coroutines.flow.map import kotlinx.coroutines.flow.onStart +import kotlinx.coroutines.flow.transformLatest import kotlinx.coroutines.launch /** @@ -93,14 +101,39 @@ constructor( private suspend fun trackUnseenNotificationsWhileUnlocked() { // Use collectLatest so that trackUnseenNotifications() is cancelled when the keyguard is // showing again + var clearUnseenOnUnlock = false keyguardRepository.isKeyguardShowing.collectLatest { isKeyguardShowing -> - if (!isKeyguardShowing) { + if (isKeyguardShowing) { + // Wait for the user to spend enough time on the lock screen before clearing unseen + // set when unlocked + awaitTimeSpentNotDozing(SEEN_TIMEOUT) + clearUnseenOnUnlock = true + } else { unseenNotifFilter.invalidateList("keyguard no longer showing") + if (clearUnseenOnUnlock) { + clearUnseenOnUnlock = false + unseenNotifications.clear() + } trackUnseenNotifications() } } } + private suspend fun awaitTimeSpentNotDozing(duration: Duration) { + keyguardRepository.isDozing + // Use transformLatest so that the timeout delay is cancelled if the device enters doze, + // and is restarted when doze ends. + .transformLatest { isDozing -> + if (!isDozing) { + delay(duration) + // Signal timeout has completed + emit(Unit) + } + } + // Suspend until the first emission + .first() + } + private suspend fun trackUnseenNotifications() { coroutineScope { launch { clearUnseenNotificationsWhenShadeIsExpanded() } @@ -240,5 +273,6 @@ constructor( companion object { private const val TAG = "KeyguardCoordinator" + private val SEEN_TIMEOUT = 5.seconds } } diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/KeyguardCoordinatorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/KeyguardCoordinatorTest.kt index 49da848baca7..8109e24a1e52 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/KeyguardCoordinatorTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/KeyguardCoordinatorTest.kt @@ -23,6 +23,7 @@ import android.provider.Settings import android.testing.AndroidTestingRunner import androidx.test.filters.SmallTest import com.android.systemui.SysuiTestCase +import com.android.systemui.coroutines.advanceTimeBy import com.android.systemui.keyguard.data.repository.FakeKeyguardRepository import com.android.systemui.plugins.statusbar.StatusBarStateController import com.android.systemui.statusbar.StatusBarState @@ -311,17 +312,20 @@ class KeyguardCoordinatorTest : SysuiTestCase() { fun unseenNotificationIsMarkedAsSeenWhenKeyguardGoesAway() { whenever(notifPipelineFlags.shouldFilterUnseenNotifsOnKeyguard).thenReturn(true) - // GIVEN: Keyguard is showing, unseen notification is present + // GIVEN: Keyguard is showing, not dozing, unseen notification is present keyguardRepository.setKeyguardShowing(true) + keyguardRepository.setDozing(false) runKeyguardCoordinatorTest { val fakeEntry = NotificationEntryBuilder().build() collectionListener.onEntryAdded(fakeEntry) + // WHEN: five seconds have passed + testScheduler.advanceTimeBy(5.seconds) + testScheduler.runCurrent() + // WHEN: Keyguard is no longer showing keyguardRepository.setKeyguardShowing(false) - - // When: Shade is expanded - statusBarStateListener.onExpandedChanged(true) + testScheduler.runCurrent() // WHEN: Keyguard is shown again keyguardRepository.setKeyguardShowing(true) diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/coroutines/TestCoroutineSchedulerUtils.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/coroutines/TestCoroutineSchedulerUtils.kt new file mode 100644 index 000000000000..84e2a5c7d4c2 --- /dev/null +++ b/packages/SystemUI/tests/utils/src/com/android/systemui/coroutines/TestCoroutineSchedulerUtils.kt @@ -0,0 +1,31 @@ +/* + * 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. + */ +@file:OptIn(ExperimentalCoroutinesApi::class) + +package com.android.systemui.coroutines + +import kotlin.time.Duration +import kotlinx.coroutines.ExperimentalCoroutinesApi +import kotlinx.coroutines.test.TestCoroutineScheduler + +/** + * Moves the virtual clock of this dispatcher forward by the specified [Duration]. + * + * @see [TestCoroutineScheduler.advanceTimeBy] + */ +fun TestCoroutineScheduler.advanceTimeBy(duration: Duration) { + advanceTimeBy(duration.inWholeMilliseconds) +} |