Merge "Clear set of "unseen" notifications when unlocked" into tm-qpr-dev am: 9d2413c996 am: 9cf90b5670
Original change: https://googleplex-android-review.googlesource.com/c/platform/frameworks/base/+/21407765
Change-Id: I6a17dba47c8e48a5e1e9e31729bab75fe287e9ab
Signed-off-by: Automerger Merge Worker <android-build-automerger-merge-worker@system.gserviceaccount.com>
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 6bf7668..82bd45c 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.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 @@
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 @@
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 49da848..8109e24 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.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 @@
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 0000000..84e2a5c
--- /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)
+}