@@ -24,6 +24,10 @@ import androidx.annotation.VisibleForTesting
2424import com.android.systemui.dagger.qualifiers.Application
2525import com.android.systemui.dagger.qualifiers.Background
2626import com.android.systemui.keyguard.data.repository.KeyguardRepository
27+ import com.android.systemui.keyguard.data.repository.KeyguardTransitionRepository
28+ import com.android.systemui.keyguard.shared.model.KeyguardState
29+ import com.android.systemui.keyguard.shared.model.TransitionState
30+ import com.android.systemui.keyguard.shared.model.TransitionStep
2731import com.android.systemui.plugins.statusbar.StatusBarStateController
2832import com.android.systemui.statusbar.StatusBarState
2933import com.android.systemui.statusbar.expansionChanges
@@ -40,22 +44,26 @@ import com.android.systemui.statusbar.policy.HeadsUpManager
4044import com.android.systemui.statusbar.policy.headsUpEvents
4145import com.android.systemui.util.settings.SecureSettings
4246import com.android.systemui.util.settings.SettingsProxyExt.observerFlow
43- import javax.inject.Inject
44- import kotlin.time.Duration
45- import kotlin.time.Duration.Companion.seconds
4647import kotlinx.coroutines.CoroutineDispatcher
4748import kotlinx.coroutines.CoroutineScope
4849import kotlinx.coroutines.ExperimentalCoroutinesApi
4950import kotlinx.coroutines.coroutineScope
5051import kotlinx.coroutines.delay
52+ import kotlinx.coroutines.flow.Flow
5153import kotlinx.coroutines.flow.collectLatest
5254import kotlinx.coroutines.flow.conflate
55+ import kotlinx.coroutines.flow.distinctUntilChanged
56+ import kotlinx.coroutines.flow.emitAll
5357import kotlinx.coroutines.flow.first
5458import kotlinx.coroutines.flow.flowOn
5559import kotlinx.coroutines.flow.map
5660import kotlinx.coroutines.flow.onStart
5761import kotlinx.coroutines.flow.transformLatest
5862import kotlinx.coroutines.launch
63+ import kotlinx.coroutines.yield
64+ import javax.inject.Inject
65+ import kotlin.time.Duration
66+ import kotlin.time.Duration.Companion.seconds
5967
6068/* *
6169 * Filters low priority and privacy-sensitive notifications from the lockscreen, and hides section
@@ -69,6 +77,7 @@ constructor(
6977 private val headsUpManager: HeadsUpManager ,
7078 private val keyguardNotificationVisibilityProvider: KeyguardNotificationVisibilityProvider ,
7179 private val keyguardRepository: KeyguardRepository ,
80+ private val keyguardTransitionRepository: KeyguardTransitionRepository ,
7281 private val notifPipelineFlags: NotifPipelineFlags ,
7382 @Application private val scope: CoroutineScope ,
7483 private val sectionHeaderVisibilityProvider: SectionHeaderVisibilityProvider ,
@@ -99,21 +108,46 @@ constructor(
99108 }
100109
101110 private suspend fun trackUnseenNotificationsWhileUnlocked () {
111+ // Whether or not we're actively tracking unseen notifications to mark them as seen when
112+ // appropriate.
113+ val isTrackingUnseen: Flow <Boolean > =
114+ keyguardRepository.isKeyguardShowing
115+ // transformLatest so that we can cancel listening to keyguard transitions once
116+ // isKeyguardShowing changes (after a successful transition to the keyguard).
117+ .transformLatest { isShowing ->
118+ if (isShowing) {
119+ // If the keyguard is showing, we're not tracking unseen.
120+ emit(false )
121+ } else {
122+ // If the keyguard stops showing, then start tracking unseen notifications.
123+ emit(true )
124+ // If the screen is turning off, stop tracking, but if that transition is
125+ // cancelled, then start again.
126+ emitAll(
127+ keyguardTransitionRepository.transitions
128+ .map { step -> ! step.isScreenTurningOff }
129+ )
130+ }
131+ }
132+ // Prevent double emit of `false` caused by transition to AOD, followed by keyguard
133+ // showing
134+ .distinctUntilChanged()
135+
102136 // Use collectLatest so that trackUnseenNotifications() is cancelled when the keyguard is
103137 // showing again
104- var clearUnseenOnUnlock = false
105- keyguardRepository.isKeyguardShowing. collectLatest { isKeyguardShowing ->
106- if (isKeyguardShowing ) {
138+ var clearUnseenOnBeginTracking = false
139+ isTrackingUnseen. collectLatest { trackingUnseen ->
140+ if (! trackingUnseen ) {
107141 // Wait for the user to spend enough time on the lock screen before clearing unseen
108142 // set when unlocked
109143 awaitTimeSpentNotDozing(SEEN_TIMEOUT )
110- clearUnseenOnUnlock = true
144+ clearUnseenOnBeginTracking = true
111145 } else {
112- unseenNotifFilter.invalidateList(" keyguard no longer showing" )
113- if (clearUnseenOnUnlock) {
114- clearUnseenOnUnlock = false
146+ if (clearUnseenOnBeginTracking) {
147+ clearUnseenOnBeginTracking = false
115148 unseenNotifications.clear()
116149 }
150+ unseenNotifFilter.invalidateList(" keyguard no longer showing" )
117151 trackUnseenNotifications()
118152 }
119153 }
@@ -142,7 +176,10 @@ constructor(
142176 }
143177
144178 private suspend fun clearUnseenNotificationsWhenShadeIsExpanded () {
145- statusBarStateController.expansionChanges.collect { isExpanded ->
179+ statusBarStateController.expansionChanges.collectLatest { isExpanded ->
180+ // Give keyguard events time to propagate, in case this expansion is part of the
181+ // keyguard transition and not the user expanding the shade
182+ yield ()
146183 if (isExpanded) {
147184 unseenNotifications.clear()
148185 }
@@ -276,3 +313,6 @@ constructor(
276313 private val SEEN_TIMEOUT = 5 .seconds
277314 }
278315}
316+
317+ private val TransitionStep .isScreenTurningOff: Boolean get() =
318+ transitionState == TransitionState .STARTED && to != KeyguardState .GONE
0 commit comments