Merge "Clear set of "unseen" notifications when unlocked" into tm-qpr-dev
This commit is contained in:
committed by
Android (Google) Code Review
commit
9d2413c996
@@ -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
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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)
|
||||||
|
|||||||
@@ -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)
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user