Merge "Clear set of "unseen" notifications when unlocked" into tm-qpr-dev am: 9d2413c996

Original change: https://googleplex-android-review.googlesource.com/c/platform/frameworks/base/+/21407765

Change-Id: I9edd10df38644904e64f838f686777bc76f95ce9
Signed-off-by: Automerger Merge Worker <android-build-automerger-merge-worker@system.gserviceaccount.com>
This commit is contained in:
Steve Elliott
2023-02-14 22:47:49 +00:00
committed by Automerger Merge Worker
3 changed files with 74 additions and 5 deletions

View File

@@ -14,6 +14,8 @@
* limitations under the License. * limitations under the License.
*/ */
@file:OptIn(ExperimentalCoroutinesApi::class)
package com.android.systemui.statusbar.notification.collection.coordinator package com.android.systemui.statusbar.notification.collection.coordinator
import android.os.UserHandle 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.SecureSettings
import com.android.systemui.util.settings.SettingsProxyExt.observerFlow import com.android.systemui.util.settings.SettingsProxyExt.observerFlow
import javax.inject.Inject import javax.inject.Inject
import kotlin.time.Duration
import kotlin.time.Duration.Companion.seconds
import kotlinx.coroutines.CoroutineDispatcher import kotlinx.coroutines.CoroutineDispatcher
import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.coroutineScope import kotlinx.coroutines.coroutineScope
import kotlinx.coroutines.delay
import kotlinx.coroutines.flow.collectLatest import kotlinx.coroutines.flow.collectLatest
import kotlinx.coroutines.flow.conflate import kotlinx.coroutines.flow.conflate
import kotlinx.coroutines.flow.first
import kotlinx.coroutines.flow.flowOn import kotlinx.coroutines.flow.flowOn
import kotlinx.coroutines.flow.map import kotlinx.coroutines.flow.map
import kotlinx.coroutines.flow.onStart import kotlinx.coroutines.flow.onStart
import kotlinx.coroutines.flow.transformLatest
import kotlinx.coroutines.launch import kotlinx.coroutines.launch
/** /**
@@ -93,14 +101,39 @@ constructor(
private suspend fun trackUnseenNotificationsWhileUnlocked() { private suspend fun trackUnseenNotificationsWhileUnlocked() {
// Use collectLatest so that trackUnseenNotifications() is cancelled when the keyguard is // Use collectLatest so that trackUnseenNotifications() is cancelled when the keyguard is
// showing again // showing again
var clearUnseenOnUnlock = false
keyguardRepository.isKeyguardShowing.collectLatest { isKeyguardShowing -> 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") unseenNotifFilter.invalidateList("keyguard no longer showing")
if (clearUnseenOnUnlock) {
clearUnseenOnUnlock = false
unseenNotifications.clear()
}
trackUnseenNotifications() 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() { private suspend fun trackUnseenNotifications() {
coroutineScope { coroutineScope {
launch { clearUnseenNotificationsWhenShadeIsExpanded() } launch { clearUnseenNotificationsWhenShadeIsExpanded() }
@@ -240,5 +273,6 @@ constructor(
companion object { companion object {
private const val TAG = "KeyguardCoordinator" private const val TAG = "KeyguardCoordinator"
private val SEEN_TIMEOUT = 5.seconds
} }
} }

View File

@@ -23,6 +23,7 @@ import android.provider.Settings
import android.testing.AndroidTestingRunner import android.testing.AndroidTestingRunner
import androidx.test.filters.SmallTest import androidx.test.filters.SmallTest
import com.android.systemui.SysuiTestCase import com.android.systemui.SysuiTestCase
import com.android.systemui.coroutines.advanceTimeBy
import com.android.systemui.keyguard.data.repository.FakeKeyguardRepository import com.android.systemui.keyguard.data.repository.FakeKeyguardRepository
import com.android.systemui.plugins.statusbar.StatusBarStateController import com.android.systemui.plugins.statusbar.StatusBarStateController
import com.android.systemui.statusbar.StatusBarState import com.android.systemui.statusbar.StatusBarState
@@ -311,17 +312,20 @@ class KeyguardCoordinatorTest : SysuiTestCase() {
fun unseenNotificationIsMarkedAsSeenWhenKeyguardGoesAway() { fun unseenNotificationIsMarkedAsSeenWhenKeyguardGoesAway() {
whenever(notifPipelineFlags.shouldFilterUnseenNotifsOnKeyguard).thenReturn(true) 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.setKeyguardShowing(true)
keyguardRepository.setDozing(false)
runKeyguardCoordinatorTest { runKeyguardCoordinatorTest {
val fakeEntry = NotificationEntryBuilder().build() val fakeEntry = NotificationEntryBuilder().build()
collectionListener.onEntryAdded(fakeEntry) collectionListener.onEntryAdded(fakeEntry)
// WHEN: five seconds have passed
testScheduler.advanceTimeBy(5.seconds)
testScheduler.runCurrent()
// WHEN: Keyguard is no longer showing // WHEN: Keyguard is no longer showing
keyguardRepository.setKeyguardShowing(false) keyguardRepository.setKeyguardShowing(false)
testScheduler.runCurrent()
// When: Shade is expanded
statusBarStateListener.onExpandedChanged(true)
// WHEN: Keyguard is shown again // WHEN: Keyguard is shown again
keyguardRepository.setKeyguardShowing(true) keyguardRepository.setKeyguardShowing(true)

View File

@@ -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)
}