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

Commit 90894a04 authored by Jeff DeCew's avatar Jeff DeCew
Browse files

Extract isShowingOnLockscreen from NotificationListViewModel to NotificationStackInteractor

Bug: 309146176
Flag: None
Test: atest SystemUITests
Change-Id: I511f21fe44a11eba45649951aeeabc87e8185f93
parent b693882e
Loading
Loading
Loading
Loading
+54 −0
Original line number Original line Diff line number Diff line
/*
 * Copyright (C) 2024 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.
 */

package com.android.systemui.statusbar.notification.stack.domain.interactor

import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.keyguard.domain.interactor.KeyguardInteractor
import com.android.systemui.keyguard.shared.model.StatusBarState
import com.android.systemui.power.domain.interactor.PowerInteractor
import javax.inject.Inject
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.combine
import kotlinx.coroutines.flow.distinctUntilChanged
import kotlinx.coroutines.flow.map

/** Interactor exposing states related to the stack's context */
@SysUISingleton
class NotificationStackInteractor
@Inject
constructor(
    keyguardInteractor: KeyguardInteractor,
    powerInteractor: PowerInteractor,
) {
    val isShowingOnLockscreen: Flow<Boolean> =
        combine(
                // Non-notification UI elements of the notification list should not be visible
                // on the lockscreen (incl. AOD and bouncer), except if the shade is opened on
                // top. See b/219680200 for the footer and b/228790482, b/267060171 for the
                // empty shade.
                // TODO(b/323187006): There's a plan to eventually get rid of StatusBarState
                //  entirely, so this will have to be replaced at some point.
                keyguardInteractor.statusBarState.map { it == StatusBarState.KEYGUARD },
                // The StatusBarState is unfortunately not updated quickly enough when the power
                // button is pressed, so this is necessary in addition to the KEYGUARD check to
                // cover the transition to AOD while going to sleep (b/190227875).
                powerInteractor.isAsleep,
            ) { (isOnKeyguard, isAsleep) ->
                isOnKeyguard || isAsleep
            }
            .distinctUntilChanged()
}
+5 −31
Original line number Original line Diff line number Diff line
@@ -16,9 +16,6 @@


package com.android.systemui.statusbar.notification.stack.ui.viewmodel
package com.android.systemui.statusbar.notification.stack.ui.viewmodel


import com.android.systemui.keyguard.domain.interactor.KeyguardInteractor
import com.android.systemui.keyguard.shared.model.StatusBarState
import com.android.systemui.power.domain.interactor.PowerInteractor
import com.android.systemui.shade.domain.interactor.ShadeInteractor
import com.android.systemui.shade.domain.interactor.ShadeInteractor
import com.android.systemui.statusbar.domain.interactor.RemoteInputInteractor
import com.android.systemui.statusbar.domain.interactor.RemoteInputInteractor
import com.android.systemui.statusbar.notification.domain.interactor.ActiveNotificationsInteractor
import com.android.systemui.statusbar.notification.domain.interactor.ActiveNotificationsInteractor
@@ -26,6 +23,7 @@ import com.android.systemui.statusbar.notification.domain.interactor.SeenNotific
import com.android.systemui.statusbar.notification.footer.shared.FooterViewRefactor
import com.android.systemui.statusbar.notification.footer.shared.FooterViewRefactor
import com.android.systemui.statusbar.notification.footer.ui.viewmodel.FooterViewModel
import com.android.systemui.statusbar.notification.footer.ui.viewmodel.FooterViewModel
import com.android.systemui.statusbar.notification.shelf.ui.viewmodel.NotificationShelfViewModel
import com.android.systemui.statusbar.notification.shelf.ui.viewmodel.NotificationShelfViewModel
import com.android.systemui.statusbar.notification.stack.domain.interactor.NotificationStackInteractor
import com.android.systemui.statusbar.policy.domain.interactor.UserSetupInteractor
import com.android.systemui.statusbar.policy.domain.interactor.UserSetupInteractor
import com.android.systemui.statusbar.policy.domain.interactor.ZenModeInteractor
import com.android.systemui.statusbar.policy.domain.interactor.ZenModeInteractor
import com.android.systemui.util.kotlin.combine
import com.android.systemui.util.kotlin.combine
@@ -51,8 +49,7 @@ constructor(
    val footer: Optional<FooterViewModel>,
    val footer: Optional<FooterViewModel>,
    val logger: Optional<NotificationLoggerViewModel>,
    val logger: Optional<NotificationLoggerViewModel>,
    activeNotificationsInteractor: ActiveNotificationsInteractor,
    activeNotificationsInteractor: ActiveNotificationsInteractor,
    keyguardInteractor: KeyguardInteractor,
    notificationStackInteractor: NotificationStackInteractor,
    powerInteractor: PowerInteractor,
    remoteInputInteractor: RemoteInputInteractor,
    remoteInputInteractor: RemoteInputInteractor,
    seenNotificationsInteractor: SeenNotificationsInteractor,
    seenNotificationsInteractor: SeenNotificationsInteractor,
    shadeInteractor: ShadeInteractor,
    shadeInteractor: ShadeInteractor,
@@ -71,7 +68,7 @@ constructor(
        } else {
        } else {
            combine(
            combine(
                    activeNotificationsInteractor.areAnyNotificationsPresent,
                    activeNotificationsInteractor.areAnyNotificationsPresent,
                    isShowingOnLockscreen,
                    notificationStackInteractor.isShowingOnLockscreen,
                ) { hasNotifications, isShowingOnLockscreen ->
                ) { hasNotifications, isShowingOnLockscreen ->
                    hasNotifications || !isShowingOnLockscreen
                    hasNotifications || !isShowingOnLockscreen
                }
                }
@@ -86,7 +83,7 @@ constructor(
            combine(
            combine(
                    activeNotificationsInteractor.areAnyNotificationsPresent,
                    activeNotificationsInteractor.areAnyNotificationsPresent,
                    shadeInteractor.isQsFullscreen,
                    shadeInteractor.isQsFullscreen,
                    isShowingOnLockscreen,
                    notificationStackInteractor.isShowingOnLockscreen,
                ) { hasNotifications, isQsFullScreen, isShowingOnLockscreen ->
                ) { hasNotifications, isQsFullScreen, isShowingOnLockscreen ->
                    when {
                    when {
                        hasNotifications -> false
                        hasNotifications -> false
@@ -109,7 +106,7 @@ constructor(
            combine(
            combine(
                    activeNotificationsInteractor.areAnyNotificationsPresent,
                    activeNotificationsInteractor.areAnyNotificationsPresent,
                    userSetupInteractor.isUserSetUp,
                    userSetupInteractor.isUserSetUp,
                    isShowingOnLockscreen,
                    notificationStackInteractor.isShowingOnLockscreen,
                    shadeInteractor.qsExpansion,
                    shadeInteractor.qsExpansion,
                    shadeInteractor.isQsFullscreen,
                    shadeInteractor.isQsFullscreen,
                    remoteInputInteractor.isRemoteInputActive,
                    remoteInputInteractor.isRemoteInputActive,
@@ -177,29 +174,6 @@ constructor(
        SHOW_WITH_ANIMATION(visible = true, canAnimate = true)
        SHOW_WITH_ANIMATION(visible = true, canAnimate = true)
    }
    }


    private val isShowingOnLockscreen: Flow<Boolean> by lazy {
        if (FooterViewRefactor.isUnexpectedlyInLegacyMode()) {
            flowOf(false)
        } else {
            combine(
                    // Non-notification UI elements of the notification list should not be visible
                    // on the lockscreen (incl. AOD and bouncer), except if the shade is opened on
                    // top. See b/219680200 for the footer and b/228790482, b/267060171 for the
                    // empty shade.
                    // TODO(b/323187006): There's a plan to eventually get rid of StatusBarState
                    //  entirely, so this will have to be replaced at some point.
                    keyguardInteractor.statusBarState.map { it == StatusBarState.KEYGUARD },
                    // The StatusBarState is unfortunately not updated quickly enough when the power
                    // button is pressed, so this is necessary in addition to the KEYGUARD check to
                    // cover the transition to AOD while going to sleep (b/190227875).
                    powerInteractor.isAsleep,
                ) { (isOnKeyguard, isAsleep) ->
                    isOnKeyguard || isAsleep
                }
                .distinctUntilChanged()
        }
    }

    // TODO(b/308591475): This should be tracked separately by the empty shade.
    // TODO(b/308591475): This should be tracked separately by the empty shade.
    val areNotificationsHiddenInShade: Flow<Boolean> by lazy {
    val areNotificationsHiddenInShade: Flow<Boolean> by lazy {
        if (FooterViewRefactor.isUnexpectedlyInLegacyMode()) {
        if (FooterViewRefactor.isUnexpectedlyInLegacyMode()) {
+85 −0
Original line number Original line Diff line number Diff line
/*
 * Copyright (C) 2024 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.statusbar.notification.stack.domain.interactor

import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
import com.android.systemui.SysuiTestCase
import com.android.systemui.coroutines.collectLastValue
import com.android.systemui.keyguard.data.repository.fakeKeyguardRepository
import com.android.systemui.keyguard.shared.model.StatusBarState
import com.android.systemui.kosmos.testScope
import com.android.systemui.power.data.repository.fakePowerRepository
import com.android.systemui.power.shared.model.WakefulnessState
import com.android.systemui.testKosmos
import com.google.common.truth.Truth.assertThat
import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.test.runCurrent
import kotlinx.coroutines.test.runTest
import org.junit.Test
import org.junit.runner.RunWith

@SmallTest
@RunWith(AndroidJUnit4::class)
class NotificationStackInteractorTest : SysuiTestCase() {
    private val kosmos = testKosmos()
    val underTest
        get() = kosmos.notificationStackInteractor

    @Test
    fun testIsShowingOnLockscreen_falseWhenViewingShade() =
        kosmos.testScope.runTest {
            val onLockscreen by collectLastValue(underTest.isShowingOnLockscreen)

            // WHEN shade is open
            kosmos.fakeKeyguardRepository.setStatusBarState(StatusBarState.SHADE)
            runCurrent()

            // THEN notifications are not showing on lockscreen
            assertThat(onLockscreen).isFalse()
        }

    @Test
    fun testIsShowingOnLockscreen_trueWhenViewingKeyguard() =
        kosmos.testScope.runTest {
            val onLockscreen by collectLastValue(underTest.isShowingOnLockscreen)

            // WHEN on keyguard
            kosmos.fakeKeyguardRepository.setStatusBarState(StatusBarState.KEYGUARD)
            runCurrent()

            // THEN notifications are showing on lockscreen
            assertThat(onLockscreen).isTrue()
        }

    @Test
    fun testIsShowingOnLockscreen_trueWhenStartingToSleep() =
        kosmos.testScope.runTest {
            val onLockscreen by collectLastValue(underTest.isShowingOnLockscreen)

            // WHEN shade is open
            kosmos.fakeKeyguardRepository.setStatusBarState(StatusBarState.SHADE)
            // AND device is starting to go to sleep
            kosmos.fakePowerRepository.updateWakefulness(WakefulnessState.STARTING_TO_SLEEP)
            runCurrent()

            // THEN notifications are showing on lockscreen
            assertThat(onLockscreen).isTrue()
        }
}
+29 −0
Original line number Original line Diff line number Diff line
/*
 * Copyright (C) 2024 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.
 */

package com.android.systemui.statusbar.notification.stack.domain.interactor

import com.android.systemui.keyguard.domain.interactor.keyguardInteractor
import com.android.systemui.kosmos.Kosmos
import com.android.systemui.kosmos.Kosmos.Fixture
import com.android.systemui.power.domain.interactor.powerInteractor

val Kosmos.notificationStackInteractor by Fixture {
    NotificationStackInteractor(
        keyguardInteractor = keyguardInteractor,
        powerInteractor = powerInteractor,
    )
}
+2 −4
Original line number Original line Diff line number Diff line
@@ -16,16 +16,15 @@


package com.android.systemui.statusbar.notification.stack.ui.viewmodel
package com.android.systemui.statusbar.notification.stack.ui.viewmodel


import com.android.systemui.keyguard.domain.interactor.keyguardInteractor
import com.android.systemui.kosmos.Kosmos
import com.android.systemui.kosmos.Kosmos
import com.android.systemui.kosmos.Kosmos.Fixture
import com.android.systemui.kosmos.Kosmos.Fixture
import com.android.systemui.power.domain.interactor.powerInteractor
import com.android.systemui.shade.domain.interactor.shadeInteractor
import com.android.systemui.shade.domain.interactor.shadeInteractor
import com.android.systemui.statusbar.domain.interactor.remoteInputInteractor
import com.android.systemui.statusbar.domain.interactor.remoteInputInteractor
import com.android.systemui.statusbar.notification.domain.interactor.activeNotificationsInteractor
import com.android.systemui.statusbar.notification.domain.interactor.activeNotificationsInteractor
import com.android.systemui.statusbar.notification.domain.interactor.seenNotificationsInteractor
import com.android.systemui.statusbar.notification.domain.interactor.seenNotificationsInteractor
import com.android.systemui.statusbar.notification.footer.ui.viewmodel.footerViewModel
import com.android.systemui.statusbar.notification.footer.ui.viewmodel.footerViewModel
import com.android.systemui.statusbar.notification.shelf.ui.viewmodel.notificationShelfViewModel
import com.android.systemui.statusbar.notification.shelf.ui.viewmodel.notificationShelfViewModel
import com.android.systemui.statusbar.notification.stack.domain.interactor.notificationStackInteractor
import com.android.systemui.statusbar.policy.domain.interactor.userSetupInteractor
import com.android.systemui.statusbar.policy.domain.interactor.userSetupInteractor
import com.android.systemui.statusbar.policy.domain.interactor.zenModeInteractor
import com.android.systemui.statusbar.policy.domain.interactor.zenModeInteractor
import java.util.Optional
import java.util.Optional
@@ -37,8 +36,7 @@ val Kosmos.notificationListViewModel by Fixture {
        Optional.of(footerViewModel),
        Optional.of(footerViewModel),
        Optional.of(notificationListLoggerViewModel),
        Optional.of(notificationListLoggerViewModel),
        activeNotificationsInteractor,
        activeNotificationsInteractor,
        keyguardInteractor,
        notificationStackInteractor,
        powerInteractor,
        remoteInputInteractor,
        remoteInputInteractor,
        seenNotificationsInteractor,
        seenNotificationsInteractor,
        shadeInteractor,
        shadeInteractor,