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

Commit 8dddab91 authored by Ioana Alexandru's avatar Ioana Alexandru
Browse files

[flexiglass] Replace the two footer vis flows with only one

This distinction is no longer necessary in flexiglass, so let's simplify
things.

I didn't want to duplicate a ton of tests, so instead I'm just
collecting from the correct flow depending on the state of
SceneContainerFlag.

Bug: 364028944
Test: manually verified that the QS bug that required this distinction
(b/329432313) doesn't happen in flexiglass
Flag: com.android.systemui.scene_container

Change-Id: I04a2e19f3fd27367ec3e375d510238e876c01778
parent 3af109a0
Loading
Loading
Loading
Loading
+40 −13
Original line number Diff line number Diff line
@@ -26,6 +26,7 @@ import androidx.test.filters.SmallTest
import com.android.settingslib.notification.data.repository.updateNotificationPolicy
import com.android.systemui.SysuiTestCase
import com.android.systemui.coroutines.collectLastValue
import com.android.systemui.flags.DisableSceneContainer
import com.android.systemui.flags.EnableSceneContainer
import com.android.systemui.flags.Flags
import com.android.systemui.flags.andSceneContainer
@@ -36,6 +37,7 @@ 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.res.R
import com.android.systemui.scene.shared.flag.SceneContainerFlag
import com.android.systemui.shade.shadeTestUtil
import com.android.systemui.statusbar.data.repository.fakeRemoteInputRepository
import com.android.systemui.statusbar.notification.data.repository.FakeHeadsUpRowRepository
@@ -51,6 +53,7 @@ import com.android.systemui.util.ui.isAnimating
import com.android.systemui.util.ui.value
import com.google.common.truth.Truth.assertThat
import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.test.TestScope
import kotlinx.coroutines.test.runCurrent
import kotlinx.coroutines.test.runTest
import org.junit.Before
@@ -153,7 +156,7 @@ class NotificationListViewModelTest(flags: FlagsParameterization) : SysuiTestCas
    fun shouldShowEmptyShadeView_trueWhenNoNotifs() =
        testScope.runTest {
            val shouldShowEmptyShadeView by collectLastValue(underTest.shouldShowEmptyShadeView)
            val shouldIncludeFooterView by collectLastValue(underTest.shouldIncludeFooterView)
            val shouldIncludeFooterView by collectFooterViewVisibility()

            // WHEN has no notifs
            activeNotificationListRepository.setActiveNotifs(count = 0)
@@ -196,7 +199,7 @@ class NotificationListViewModelTest(flags: FlagsParameterization) : SysuiTestCas
    fun shouldShowEmptyShadeView_trueWhenQsExpandedInSplitShade() =
        testScope.runTest {
            val shouldShowEmptyShadeView by collectLastValue(underTest.shouldShowEmptyShadeView)
            val shouldIncludeFooterView by collectLastValue(underTest.shouldIncludeFooterView)
            val shouldIncludeFooterView by collectFooterViewVisibility()

            // WHEN has no notifs
            activeNotificationListRepository.setActiveNotifs(count = 0)
@@ -217,7 +220,7 @@ class NotificationListViewModelTest(flags: FlagsParameterization) : SysuiTestCas
    fun shouldShowEmptyShadeView_trueWhenLockedShade() =
        testScope.runTest {
            val shouldShowEmptyShadeView by collectLastValue(underTest.shouldShowEmptyShadeView)
            val shouldIncludeFooterView by collectLastValue(underTest.shouldIncludeFooterView)
            val shouldIncludeFooterView by collectFooterViewVisibility()

            // WHEN has no notifs
            activeNotificationListRepository.setActiveNotifs(count = 0)
@@ -315,7 +318,7 @@ class NotificationListViewModelTest(flags: FlagsParameterization) : SysuiTestCas
    @Test
    fun shouldIncludeFooterView_trueWhenShade() =
        testScope.runTest {
            val shouldIncludeFooterView by collectLastValue(underTest.shouldIncludeFooterView)
            val shouldIncludeFooterView by collectFooterViewVisibility()
            val shouldShowEmptyShadeView by collectLastValue(underTest.shouldShowEmptyShadeView)

            // WHEN has notifs
@@ -333,7 +336,7 @@ class NotificationListViewModelTest(flags: FlagsParameterization) : SysuiTestCas
    @Test
    fun shouldIncludeFooterView_trueWhenLockedShade() =
        testScope.runTest {
            val shouldIncludeFooterView by collectLastValue(underTest.shouldIncludeFooterView)
            val shouldIncludeFooterView by collectFooterViewVisibility()
            val shouldShowEmptyShadeView by collectLastValue(underTest.shouldShowEmptyShadeView)

            // WHEN has notifs
@@ -351,7 +354,7 @@ class NotificationListViewModelTest(flags: FlagsParameterization) : SysuiTestCas
    @Test
    fun shouldIncludeFooterView_falseWhenKeyguard() =
        testScope.runTest {
            val shouldInclude by collectLastValue(underTest.shouldIncludeFooterView)
            val shouldInclude by collectFooterViewVisibility()

            // WHEN has notifs
            activeNotificationListRepository.setActiveNotifs(count = 2)
@@ -366,7 +369,7 @@ class NotificationListViewModelTest(flags: FlagsParameterization) : SysuiTestCas
    @Test
    fun shouldIncludeFooterView_falseWhenUserNotSetUp() =
        testScope.runTest {
            val shouldInclude by collectLastValue(underTest.shouldIncludeFooterView)
            val shouldInclude by collectFooterViewVisibility()

            // WHEN has notifs
            activeNotificationListRepository.setActiveNotifs(count = 2)
@@ -384,7 +387,7 @@ class NotificationListViewModelTest(flags: FlagsParameterization) : SysuiTestCas
    @Test
    fun shouldIncludeFooterView_falseWhenStartingToSleep() =
        testScope.runTest {
            val shouldInclude by collectLastValue(underTest.shouldIncludeFooterView)
            val shouldInclude by collectFooterViewVisibility()

            // WHEN has notifs
            activeNotificationListRepository.setActiveNotifs(count = 2)
@@ -402,7 +405,7 @@ class NotificationListViewModelTest(flags: FlagsParameterization) : SysuiTestCas
    @Test
    fun shouldIncludeFooterView_falseWhenQsExpandedDefault() =
        testScope.runTest {
            val shouldInclude by collectLastValue(underTest.shouldIncludeFooterView)
            val shouldInclude by collectFooterViewVisibility()

            // WHEN has notifs
            activeNotificationListRepository.setActiveNotifs(count = 2)
@@ -421,7 +424,7 @@ class NotificationListViewModelTest(flags: FlagsParameterization) : SysuiTestCas
    @Test
    fun shouldIncludeFooterView_trueWhenQsExpandedSplitShade() =
        testScope.runTest {
            val shouldIncludeFooterView by collectLastValue(underTest.shouldIncludeFooterView)
            val shouldIncludeFooterView by collectFooterViewVisibility()
            val shouldShowEmptyShadeView by collectLastValue(underTest.shouldShowEmptyShadeView)

            // WHEN has notifs
@@ -444,7 +447,7 @@ class NotificationListViewModelTest(flags: FlagsParameterization) : SysuiTestCas
    @Test
    fun shouldIncludeFooterView_falseWhenRemoteInputActive() =
        testScope.runTest {
            val shouldInclude by collectLastValue(underTest.shouldIncludeFooterView)
            val shouldInclude by collectFooterViewVisibility()

            // WHEN has notifs
            activeNotificationListRepository.setActiveNotifs(count = 2)
@@ -462,7 +465,7 @@ class NotificationListViewModelTest(flags: FlagsParameterization) : SysuiTestCas
    @Test
    fun shouldIncludeFooterView_animatesWhenShade() =
        testScope.runTest {
            val shouldInclude by collectLastValue(underTest.shouldIncludeFooterView)
            val shouldInclude by collectFooterViewVisibility()

            // WHEN has notifs
            activeNotificationListRepository.setActiveNotifs(count = 2)
@@ -478,7 +481,7 @@ class NotificationListViewModelTest(flags: FlagsParameterization) : SysuiTestCas
    @Test
    fun shouldIncludeFooterView_notAnimatingOnKeyguard() =
        testScope.runTest {
            val shouldInclude by collectLastValue(underTest.shouldIncludeFooterView)
            val shouldInclude by collectFooterViewVisibility()

            // WHEN has notifs
            activeNotificationListRepository.setActiveNotifs(count = 2)
@@ -492,6 +495,22 @@ class NotificationListViewModelTest(flags: FlagsParameterization) : SysuiTestCas
        }

    @Test
    @EnableSceneContainer
    fun shouldShowFooterView_falseWhenShadeIsClosed() =
        testScope.runTest {
            val shouldShow by collectLastValue(underTest.shouldShowFooterView)

            // WHEN shade is closed
            fakeKeyguardRepository.setStatusBarState(StatusBarState.SHADE)
            shadeTestUtil.setShadeExpansion(0f)
            runCurrent()

            // THEN footer is hidden
            assertThat(shouldShow?.value).isFalse()
        }

    @Test
    @DisableSceneContainer
    fun shouldHideFooterView_trueWhenShadeIsClosed() =
        testScope.runTest {
            val shouldHide by collectLastValue(underTest.shouldHideFooterView)
@@ -506,6 +525,7 @@ class NotificationListViewModelTest(flags: FlagsParameterization) : SysuiTestCas
        }

    @Test
    @DisableSceneContainer
    fun shouldHideFooterView_falseWhenShadeIsOpen() =
        testScope.runTest {
            val shouldHide by collectLastValue(underTest.shouldHideFooterView)
@@ -520,6 +540,7 @@ class NotificationListViewModelTest(flags: FlagsParameterization) : SysuiTestCas
        }

    @Test
    @DisableSceneContainer
    fun shouldHideFooterView_falseWhenQSPartiallyOpen() =
        testScope.runTest {
            val shouldHide by collectLastValue(underTest.shouldHideFooterView)
@@ -642,4 +663,10 @@ class NotificationListViewModelTest(flags: FlagsParameterization) : SysuiTestCas

            assertThat(animationsEnabled).isTrue()
        }

    private fun TestScope.collectFooterViewVisibility() =
        collectLastValue(
            if (SceneContainerFlag.isEnabled) underTest.shouldShowFooterView
            else underTest.shouldIncludeFooterView
        )
}
+4 −2
Original line number Diff line number Diff line
@@ -682,7 +682,10 @@ public class StackScrollAlgorithm {
                //  doesn't get updated quickly enough and can cause the footer to flash when
                //  closing the shade. As such, we temporarily also check the ambientState directly.
                if (((FooterView) view).shouldBeHidden() || !ambientState.isShadeExpanded()) {
                    // Note: This is no longer necessary in flexiglass.
                    if (!SceneContainerFlag.isEnabled()) {
                        viewState.hidden = true;
                    }
                } else {
                    final float footerEnd = algorithmState.mCurrentExpandedYPosition
                            + view.getIntrinsicHeight();
@@ -691,7 +694,6 @@ public class StackScrollAlgorithm {
                            noSpaceForFooter || (ambientState.isClearAllInProgress()
                                    && !hasNonClearableNotifs(algorithmState));
                }

            } else {
                final boolean shadeClosed = !ambientState.isShadeExpanded();
                final boolean isShelfShowing = algorithmState.firstViewInShelf != null;
+18 −7
Original line number Diff line number Diff line
@@ -188,6 +188,16 @@ constructor(
                        .startHistoryIntent(view, /* showHistory= */ true)
                },
            )
        if (SceneContainerFlag.isEnabled) {
            launch {
                viewModel.shouldShowFooterView.collect { animatedVisibility ->
                    footerView.setVisible(
                        /* visible = */ animatedVisibility.value,
                        /* animate = */ animatedVisibility.isAnimating,
                    )
                }
            }
        } else {
            launch {
                viewModel.shouldIncludeFooterView.collect { animatedVisibility ->
                    footerView.setVisible(
@@ -197,6 +207,7 @@ constructor(
                }
            }
            launch { viewModel.shouldHideFooterView.collect { footerView.setShouldBeHidden(it) } }
        }
        disposableHandle.awaitCancellationThenDispose()
    }

+73 −0
Original line number Diff line number Diff line
@@ -32,6 +32,7 @@ import com.android.systemui.statusbar.notification.stack.domain.interactor.Notif
import com.android.systemui.statusbar.policy.domain.interactor.UserSetupInteractor
import com.android.systemui.statusbar.policy.domain.interactor.ZenModeInteractor
import com.android.systemui.util.kotlin.FlowDumperImpl
import com.android.systemui.util.kotlin.combine
import com.android.systemui.util.kotlin.sample
import com.android.systemui.util.ui.AnimatableEvent
import com.android.systemui.util.ui.AnimatedValue
@@ -120,6 +121,7 @@ constructor(
     * This essentially corresponds to having the view set to INVISIBLE.
     */
    val shouldHideFooterView: Flow<Boolean> by lazy {
        SceneContainerFlag.assertInLegacyMode()
        if (FooterViewRefactor.isUnexpectedlyInLegacyMode()) {
            flowOf(false)
        } else {
@@ -143,6 +145,7 @@ constructor(
     * be hidden by another condition (see [shouldHideFooterView] above).
     */
    val shouldIncludeFooterView: Flow<AnimatedValue<Boolean>> by lazy {
        SceneContainerFlag.assertInLegacyMode()
        if (FooterViewRefactor.isUnexpectedlyInLegacyMode()) {
            flowOf(AnimatedValue.NotAnimating(false))
        } else {
@@ -207,6 +210,76 @@ constructor(
        }
    }

    // This flow replaces shouldHideFooterView+shouldIncludeFooterView in flexiglass.
    val shouldShowFooterView: Flow<AnimatedValue<Boolean>> by lazy {
        if (SceneContainerFlag.isUnexpectedlyInLegacyMode()) {
            flowOf(AnimatedValue.NotAnimating(false))
        } else {
            combine(
                    activeNotificationsInteractor.areAnyNotificationsPresent,
                    userSetupInteractor.isUserSetUp,
                    notificationStackInteractor.isShowingOnLockscreen,
                    shadeInteractor.isQsFullscreen,
                    remoteInputInteractor.isRemoteInputActive,
                    shadeInteractor.shadeExpansion.map { it < 0.5f }.distinctUntilChanged(),
                ) {
                    hasNotifications,
                    isUserSetUp,
                    isShowingOnLockscreen,
                    qsFullScreen,
                    isRemoteInputActive,
                    shadeLessThanHalfwayExpanded ->
                    when {
                        !hasNotifications -> VisibilityChange.DISAPPEAR_WITH_ANIMATION
                        // Hide the footer until the user setup is complete, to prevent access
                        // to settings (b/193149550).
                        !isUserSetUp -> VisibilityChange.DISAPPEAR_WITH_ANIMATION
                        // Do not show the footer if the lockscreen is visible (incl. AOD),
                        // except if the shade is opened on top. See also b/219680200.
                        // Do not animate, as that makes the footer appear briefly when
                        // transitioning between the shade and keyguard.
                        isShowingOnLockscreen -> VisibilityChange.DISAPPEAR_WITHOUT_ANIMATION
                        // Do not show the footer if quick settings are fully expanded (except
                        // for the foldable split shade view). See b/201427195 && b/222699879.
                        qsFullScreen -> VisibilityChange.DISAPPEAR_WITH_ANIMATION
                        // Hide the footer if remote input is active (i.e. user is replying to a
                        // notification). See b/75984847.
                        isRemoteInputActive -> VisibilityChange.DISAPPEAR_WITH_ANIMATION
                        // If the shade is not expanded enough, the footer shouldn't be visible.
                        shadeLessThanHalfwayExpanded -> VisibilityChange.DISAPPEAR_WITH_ANIMATION
                        else -> VisibilityChange.APPEAR_WITH_ANIMATION
                    }
                }
                .distinctUntilChanged(
                    // Equivalent unless visibility changes
                    areEquivalent = { a: VisibilityChange, b: VisibilityChange ->
                        a.visible == b.visible
                    }
                )
                // Should we animate the visibility change?
                .sample(
                    // TODO(b/322167853): This check is currently duplicated in FooterViewModel,
                    //  but instead it should be a field in ShadeAnimationInteractor.
                    combine(
                            shadeInteractor.isShadeFullyExpanded,
                            shadeInteractor.isShadeTouchable,
                            ::Pair
                        )
                        .onStart { emit(Pair(false, false)) }
                ) { visibilityChange, (isShadeFullyExpanded, animationsEnabled) ->
                    // Animate if the shade is interactive, but NOT on the lockscreen. Having
                    // animations enabled while on the lockscreen makes the footer appear briefly
                    // when transitioning between the shade and keyguard.
                    val shouldAnimate =
                        isShadeFullyExpanded && animationsEnabled && visibilityChange.canAnimate
                    AnimatableEvent(visibilityChange.visible, shouldAnimate)
                }
                .toAnimatedValueFlow()
                .dumpWhileCollecting("shouldShowFooterView")
                .flowOn(bgDispatcher)
        }
    }

    enum class VisibilityChange(val visible: Boolean, val canAnimate: Boolean) {
        DISAPPEAR_WITHOUT_ANIMATION(visible = false, canAnimate = false),
        DISAPPEAR_WITH_ANIMATION(visible = false, canAnimate = true),