Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 46144b62 authored by Steve Elliott's avatar Steve Elliott Committed by Android (Google) Code Review
Browse files

Merge "Stop tracking unseen notifs on AOD transition init" into tm-qpr-dev

parents 66266df5 63de9cb4
Loading
Loading
Loading
Loading
+51 −11
Original line number Diff line number Diff line
@@ -24,6 +24,10 @@ import androidx.annotation.VisibleForTesting
import com.android.systemui.dagger.qualifiers.Application
import com.android.systemui.dagger.qualifiers.Background
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.statusbar.StatusBarState
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.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.Flow
import kotlinx.coroutines.flow.collectLatest
import kotlinx.coroutines.flow.conflate
import kotlinx.coroutines.flow.distinctUntilChanged
import kotlinx.coroutines.flow.emitAll
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
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
@@ -69,6 +77,7 @@ constructor(
    private val headsUpManager: HeadsUpManager,
    private val keyguardNotificationVisibilityProvider: KeyguardNotificationVisibilityProvider,
    private val keyguardRepository: KeyguardRepository,
    private val keyguardTransitionRepository: KeyguardTransitionRepository,
    private val notifPipelineFlags: NotifPipelineFlags,
    @Application private val scope: CoroutineScope,
    private val sectionHeaderVisibilityProvider: SectionHeaderVisibilityProvider,
@@ -99,21 +108,46 @@ constructor(
    }

    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
        // showing again
        var clearUnseenOnUnlock = false
        keyguardRepository.isKeyguardShowing.collectLatest { isKeyguardShowing ->
            if (isKeyguardShowing) {
        var clearUnseenOnBeginTracking = false
        isTrackingUnseen.collectLatest { trackingUnseen ->
            if (!trackingUnseen) {
                // Wait for the user to spend enough time on the lock screen before clearing unseen
                // set when unlocked
                awaitTimeSpentNotDozing(SEEN_TIMEOUT)
                clearUnseenOnUnlock = true
                clearUnseenOnBeginTracking = true
            } else {
                unseenNotifFilter.invalidateList("keyguard no longer showing")
                if (clearUnseenOnUnlock) {
                    clearUnseenOnUnlock = false
                if (clearUnseenOnBeginTracking) {
                    clearUnseenOnBeginTracking = false
                    unseenNotifications.clear()
                }
                unseenNotifFilter.invalidateList("keyguard no longer showing")
                trackUnseenNotifications()
            }
        }
@@ -142,7 +176,10 @@ constructor(
    }

    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) {
                unseenNotifications.clear()
            }
@@ -276,3 +313,6 @@ constructor(
        private val SEEN_TIMEOUT = 5.seconds
    }
}

private val TransitionStep.isScreenTurningOff: Boolean get() =
    transitionState == TransitionState.STARTED && to != KeyguardState.GONE
 No newline at end of file
+33 −0
Original line number Diff line number Diff line
@@ -25,6 +25,10 @@ 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.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.statusbar.StatusBarState
import com.android.systemui.statusbar.notification.NotifPipelineFlags
@@ -69,6 +73,7 @@ class KeyguardCoordinatorTest : SysuiTestCase() {
    private val headsUpManager: HeadsUpManager = mock()
    private val keyguardNotifVisibilityProvider: KeyguardNotificationVisibilityProvider = mock()
    private val keyguardRepository = FakeKeyguardRepository()
    private val keyguardTransitionRepository = FakeKeyguardTransitionRepository()
    private val notifPipelineFlags: NotifPipelineFlags = mock()
    private val notifPipeline: NotifPipeline = 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
    fun unseenFilter_headsUpMarkedAsSeen() {
        whenever(notifPipelineFlags.shouldFilterUnseenNotifsOnKeyguard).thenReturn(true)
@@ -373,6 +405,7 @@ class KeyguardCoordinatorTest : SysuiTestCase() {
                headsUpManager,
                keyguardNotifVisibilityProvider,
                keyguardRepository,
                keyguardTransitionRepository,
                notifPipelineFlags,
                testScope.backgroundScope,
                sectionHeaderVisibilityProvider,