Merge "Stop tracking unseen notifs on AOD transition init" into tm-qpr-dev
This commit is contained in:
committed by
Android (Google) Code Review
commit
46144b62c1
@@ -24,6 +24,10 @@ import androidx.annotation.VisibleForTesting
|
|||||||
import com.android.systemui.dagger.qualifiers.Application
|
import com.android.systemui.dagger.qualifiers.Application
|
||||||
import com.android.systemui.dagger.qualifiers.Background
|
import com.android.systemui.dagger.qualifiers.Background
|
||||||
import com.android.systemui.keyguard.data.repository.KeyguardRepository
|
import com.android.systemui.keyguard.data.repository.KeyguardRepository
|
||||||
|
import com.android.systemui.keyguard.data.repository.KeyguardTransitionRepository
|
||||||
|
import com.android.systemui.keyguard.shared.model.KeyguardState
|
||||||
|
import com.android.systemui.keyguard.shared.model.TransitionState
|
||||||
|
import com.android.systemui.keyguard.shared.model.TransitionStep
|
||||||
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
|
||||||
import com.android.systemui.statusbar.expansionChanges
|
import com.android.systemui.statusbar.expansionChanges
|
||||||
@@ -40,22 +44,26 @@ import com.android.systemui.statusbar.policy.HeadsUpManager
|
|||||||
import com.android.systemui.statusbar.policy.headsUpEvents
|
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 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.ExperimentalCoroutinesApi
|
||||||
import kotlinx.coroutines.coroutineScope
|
import kotlinx.coroutines.coroutineScope
|
||||||
import kotlinx.coroutines.delay
|
import kotlinx.coroutines.delay
|
||||||
|
import kotlinx.coroutines.flow.Flow
|
||||||
import kotlinx.coroutines.flow.collectLatest
|
import kotlinx.coroutines.flow.collectLatest
|
||||||
import kotlinx.coroutines.flow.conflate
|
import kotlinx.coroutines.flow.conflate
|
||||||
|
import kotlinx.coroutines.flow.distinctUntilChanged
|
||||||
|
import kotlinx.coroutines.flow.emitAll
|
||||||
import kotlinx.coroutines.flow.first
|
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.flow.transformLatest
|
||||||
import kotlinx.coroutines.launch
|
import kotlinx.coroutines.launch
|
||||||
|
import kotlinx.coroutines.yield
|
||||||
|
import javax.inject.Inject
|
||||||
|
import kotlin.time.Duration
|
||||||
|
import kotlin.time.Duration.Companion.seconds
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Filters low priority and privacy-sensitive notifications from the lockscreen, and hides section
|
* Filters low priority and privacy-sensitive notifications from the lockscreen, and hides section
|
||||||
@@ -69,6 +77,7 @@ constructor(
|
|||||||
private val headsUpManager: HeadsUpManager,
|
private val headsUpManager: HeadsUpManager,
|
||||||
private val keyguardNotificationVisibilityProvider: KeyguardNotificationVisibilityProvider,
|
private val keyguardNotificationVisibilityProvider: KeyguardNotificationVisibilityProvider,
|
||||||
private val keyguardRepository: KeyguardRepository,
|
private val keyguardRepository: KeyguardRepository,
|
||||||
|
private val keyguardTransitionRepository: KeyguardTransitionRepository,
|
||||||
private val notifPipelineFlags: NotifPipelineFlags,
|
private val notifPipelineFlags: NotifPipelineFlags,
|
||||||
@Application private val scope: CoroutineScope,
|
@Application private val scope: CoroutineScope,
|
||||||
private val sectionHeaderVisibilityProvider: SectionHeaderVisibilityProvider,
|
private val sectionHeaderVisibilityProvider: SectionHeaderVisibilityProvider,
|
||||||
@@ -99,21 +108,46 @@ constructor(
|
|||||||
}
|
}
|
||||||
|
|
||||||
private suspend fun trackUnseenNotificationsWhileUnlocked() {
|
private suspend fun trackUnseenNotificationsWhileUnlocked() {
|
||||||
|
// Whether or not we're actively tracking unseen notifications to mark them as seen when
|
||||||
|
// appropriate.
|
||||||
|
val isTrackingUnseen: Flow<Boolean> =
|
||||||
|
keyguardRepository.isKeyguardShowing
|
||||||
|
// transformLatest so that we can cancel listening to keyguard transitions once
|
||||||
|
// isKeyguardShowing changes (after a successful transition to the keyguard).
|
||||||
|
.transformLatest { isShowing ->
|
||||||
|
if (isShowing) {
|
||||||
|
// If the keyguard is showing, we're not tracking unseen.
|
||||||
|
emit(false)
|
||||||
|
} else {
|
||||||
|
// If the keyguard stops showing, then start tracking unseen notifications.
|
||||||
|
emit(true)
|
||||||
|
// If the screen is turning off, stop tracking, but if that transition is
|
||||||
|
// cancelled, then start again.
|
||||||
|
emitAll(
|
||||||
|
keyguardTransitionRepository.transitions
|
||||||
|
.map { step -> !step.isScreenTurningOff }
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// Prevent double emit of `false` caused by transition to AOD, followed by keyguard
|
||||||
|
// showing
|
||||||
|
.distinctUntilChanged()
|
||||||
|
|
||||||
// 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
|
var clearUnseenOnBeginTracking = false
|
||||||
keyguardRepository.isKeyguardShowing.collectLatest { isKeyguardShowing ->
|
isTrackingUnseen.collectLatest { trackingUnseen ->
|
||||||
if (isKeyguardShowing) {
|
if (!trackingUnseen) {
|
||||||
// Wait for the user to spend enough time on the lock screen before clearing unseen
|
// Wait for the user to spend enough time on the lock screen before clearing unseen
|
||||||
// set when unlocked
|
// set when unlocked
|
||||||
awaitTimeSpentNotDozing(SEEN_TIMEOUT)
|
awaitTimeSpentNotDozing(SEEN_TIMEOUT)
|
||||||
clearUnseenOnUnlock = true
|
clearUnseenOnBeginTracking = true
|
||||||
} else {
|
} else {
|
||||||
unseenNotifFilter.invalidateList("keyguard no longer showing")
|
if (clearUnseenOnBeginTracking) {
|
||||||
if (clearUnseenOnUnlock) {
|
clearUnseenOnBeginTracking = false
|
||||||
clearUnseenOnUnlock = false
|
|
||||||
unseenNotifications.clear()
|
unseenNotifications.clear()
|
||||||
}
|
}
|
||||||
|
unseenNotifFilter.invalidateList("keyguard no longer showing")
|
||||||
trackUnseenNotifications()
|
trackUnseenNotifications()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -142,7 +176,10 @@ constructor(
|
|||||||
}
|
}
|
||||||
|
|
||||||
private suspend fun clearUnseenNotificationsWhenShadeIsExpanded() {
|
private suspend fun clearUnseenNotificationsWhenShadeIsExpanded() {
|
||||||
statusBarStateController.expansionChanges.collect { isExpanded ->
|
statusBarStateController.expansionChanges.collectLatest { isExpanded ->
|
||||||
|
// Give keyguard events time to propagate, in case this expansion is part of the
|
||||||
|
// keyguard transition and not the user expanding the shade
|
||||||
|
yield()
|
||||||
if (isExpanded) {
|
if (isExpanded) {
|
||||||
unseenNotifications.clear()
|
unseenNotifications.clear()
|
||||||
}
|
}
|
||||||
@@ -276,3 +313,6 @@ constructor(
|
|||||||
private val SEEN_TIMEOUT = 5.seconds
|
private val SEEN_TIMEOUT = 5.seconds
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private val TransitionStep.isScreenTurningOff: Boolean get() =
|
||||||
|
transitionState == TransitionState.STARTED && to != KeyguardState.GONE
|
||||||
@@ -25,6 +25,10 @@ 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.coroutines.advanceTimeBy
|
||||||
import com.android.systemui.keyguard.data.repository.FakeKeyguardRepository
|
import com.android.systemui.keyguard.data.repository.FakeKeyguardRepository
|
||||||
|
import com.android.systemui.keyguard.data.repository.FakeKeyguardTransitionRepository
|
||||||
|
import com.android.systemui.keyguard.shared.model.KeyguardState
|
||||||
|
import com.android.systemui.keyguard.shared.model.TransitionState
|
||||||
|
import com.android.systemui.keyguard.shared.model.TransitionStep
|
||||||
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
|
||||||
import com.android.systemui.statusbar.notification.NotifPipelineFlags
|
import com.android.systemui.statusbar.notification.NotifPipelineFlags
|
||||||
@@ -69,6 +73,7 @@ class KeyguardCoordinatorTest : SysuiTestCase() {
|
|||||||
private val headsUpManager: HeadsUpManager = mock()
|
private val headsUpManager: HeadsUpManager = mock()
|
||||||
private val keyguardNotifVisibilityProvider: KeyguardNotificationVisibilityProvider = mock()
|
private val keyguardNotifVisibilityProvider: KeyguardNotificationVisibilityProvider = mock()
|
||||||
private val keyguardRepository = FakeKeyguardRepository()
|
private val keyguardRepository = FakeKeyguardRepository()
|
||||||
|
private val keyguardTransitionRepository = FakeKeyguardTransitionRepository()
|
||||||
private val notifPipelineFlags: NotifPipelineFlags = mock()
|
private val notifPipelineFlags: NotifPipelineFlags = mock()
|
||||||
private val notifPipeline: NotifPipeline = mock()
|
private val notifPipeline: NotifPipeline = mock()
|
||||||
private val sectionHeaderVisibilityProvider: SectionHeaderVisibilityProvider = mock()
|
private val sectionHeaderVisibilityProvider: SectionHeaderVisibilityProvider = mock()
|
||||||
@@ -117,6 +122,33 @@ class KeyguardCoordinatorTest : SysuiTestCase() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun unseenFilterStopsMarkingSeenNotifWhenTransitionToAod() {
|
||||||
|
whenever(notifPipelineFlags.shouldFilterUnseenNotifsOnKeyguard).thenReturn(true)
|
||||||
|
|
||||||
|
// GIVEN: Keyguard is not showing, shade is not expanded, and a notification is present
|
||||||
|
keyguardRepository.setKeyguardShowing(false)
|
||||||
|
whenever(statusBarStateController.isExpanded).thenReturn(false)
|
||||||
|
runKeyguardCoordinatorTest {
|
||||||
|
val fakeEntry = NotificationEntryBuilder().build()
|
||||||
|
collectionListener.onEntryAdded(fakeEntry)
|
||||||
|
|
||||||
|
// WHEN: The device transitions to AOD
|
||||||
|
keyguardTransitionRepository.sendTransitionStep(
|
||||||
|
TransitionStep(to = KeyguardState.AOD, transitionState = TransitionState.STARTED),
|
||||||
|
)
|
||||||
|
testScheduler.runCurrent()
|
||||||
|
|
||||||
|
// WHEN: The shade is expanded
|
||||||
|
whenever(statusBarStateController.isExpanded).thenReturn(true)
|
||||||
|
statusBarStateListener.onExpandedChanged(true)
|
||||||
|
testScheduler.runCurrent()
|
||||||
|
|
||||||
|
// THEN: The notification is still treated as "unseen" and is not filtered out.
|
||||||
|
assertThat(unseenFilter.shouldFilterOut(fakeEntry, 0L)).isFalse()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun unseenFilter_headsUpMarkedAsSeen() {
|
fun unseenFilter_headsUpMarkedAsSeen() {
|
||||||
whenever(notifPipelineFlags.shouldFilterUnseenNotifsOnKeyguard).thenReturn(true)
|
whenever(notifPipelineFlags.shouldFilterUnseenNotifsOnKeyguard).thenReturn(true)
|
||||||
@@ -373,6 +405,7 @@ class KeyguardCoordinatorTest : SysuiTestCase() {
|
|||||||
headsUpManager,
|
headsUpManager,
|
||||||
keyguardNotifVisibilityProvider,
|
keyguardNotifVisibilityProvider,
|
||||||
keyguardRepository,
|
keyguardRepository,
|
||||||
|
keyguardTransitionRepository,
|
||||||
notifPipelineFlags,
|
notifPipelineFlags,
|
||||||
testScope.backgroundScope,
|
testScope.backgroundScope,
|
||||||
sectionHeaderVisibilityProvider,
|
sectionHeaderVisibilityProvider,
|
||||||
|
|||||||
Reference in New Issue
Block a user