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

Commit 3f80d764 authored by Ioana Alexandru's avatar Ioana Alexandru
Browse files

Don't update footer if empty shade also visible

If the empty shade is visible, or the footer happens to be the first
visible view in the stack (notGoneIndex == 0), that means we're in a
transitory state so we don't need to update the position of the footer
in that run of the algorithm. This fixes the issue with the footer
jumping in weird ways when a notification is dismissed.

This change is flagged, but I tested it without the flag and it actually
also fixes an issue with the footer jumping when a notification appears
while the shade is open.

Fix: 332430751
Test: manually dismissed notification, posted notification and pressed
clear all with only one notif present
Flag: com.android.systemui.notifications_footer_view_refactor

Change-Id: I79e46622d782d93cab6fb98dfbbf8d11e6bf4b73
parent 183e7f3c
Loading
Loading
Loading
Loading
+41 −27
Original line number Diff line number Diff line
@@ -146,36 +146,40 @@ class NotificationListViewModelTest(flags: FlagsParameterization) : SysuiTestCas
            assertThat(important).isTrue()
        }

    // NOTE: The empty shade view and the footer view should be mutually exclusive.

    @Test
    fun shouldIncludeEmptyShadeView_trueWhenNoNotifs() =
    fun shouldShowEmptyShadeView_trueWhenNoNotifs() =
        testScope.runTest {
            val shouldInclude by collectLastValue(underTest.shouldShowEmptyShadeView)
            val shouldShowEmptyShadeView by collectLastValue(underTest.shouldShowEmptyShadeView)
            val shouldIncludeFooterView by collectLastValue(underTest.shouldIncludeFooterView)

            // WHEN has no notifs
            activeNotificationListRepository.setActiveNotifs(count = 0)
            runCurrent()

            // THEN empty shade is visible
            assertThat(shouldInclude).isTrue()
            assertThat(shouldShowEmptyShadeView).isTrue()
            assertThat(shouldIncludeFooterView?.value).isFalse()
        }

    @Test
    fun shouldIncludeEmptyShadeView_falseWhenNotifs() =
    fun shouldShowEmptyShadeView_falseWhenNotifs() =
        testScope.runTest {
            val shouldInclude by collectLastValue(underTest.shouldShowEmptyShadeView)
            val shouldShowEmptyShadeView by collectLastValue(underTest.shouldShowEmptyShadeView)

            // WHEN has notifs
            activeNotificationListRepository.setActiveNotifs(count = 2)
            runCurrent()

            // THEN empty shade is not visible
            assertThat(shouldInclude).isFalse()
            assertThat(shouldShowEmptyShadeView).isFalse()
        }

    @Test
    fun shouldIncludeEmptyShadeView_falseWhenQsExpandedDefault() =
    fun shouldShowEmptyShadeView_falseWhenQsExpandedDefault() =
        testScope.runTest {
            val shouldInclude by collectLastValue(underTest.shouldShowEmptyShadeView)
            val shouldShow by collectLastValue(underTest.shouldShowEmptyShadeView)

            // WHEN has no notifs
            activeNotificationListRepository.setActiveNotifs(count = 0)
@@ -184,13 +188,14 @@ class NotificationListViewModelTest(flags: FlagsParameterization) : SysuiTestCas
            runCurrent()

            // THEN empty shade is not visible
            assertThat(shouldInclude).isFalse()
            assertThat(shouldShow).isFalse()
        }

    @Test
    fun shouldIncludeEmptyShadeView_trueWhenQsExpandedInSplitShade() =
    fun shouldShowEmptyShadeView_trueWhenQsExpandedInSplitShade() =
        testScope.runTest {
            val shouldInclude by collectLastValue(underTest.shouldShowEmptyShadeView)
            val shouldShowEmptyShadeView by collectLastValue(underTest.shouldShowEmptyShadeView)
            val shouldIncludeFooterView by collectLastValue(underTest.shouldIncludeFooterView)

            // WHEN has no notifs
            activeNotificationListRepository.setActiveNotifs(count = 0)
@@ -203,13 +208,15 @@ class NotificationListViewModelTest(flags: FlagsParameterization) : SysuiTestCas
            runCurrent()

            // THEN empty shade is visible
            assertThat(shouldInclude).isTrue()
            assertThat(shouldShowEmptyShadeView).isTrue()
            assertThat(shouldIncludeFooterView?.value).isFalse()
        }

    @Test
    fun shouldIncludeEmptyShadeView_trueWhenLockedShade() =
    fun shouldShowEmptyShadeView_trueWhenLockedShade() =
        testScope.runTest {
            val shouldInclude by collectLastValue(underTest.shouldShowEmptyShadeView)
            val shouldShowEmptyShadeView by collectLastValue(underTest.shouldShowEmptyShadeView)
            val shouldIncludeFooterView by collectLastValue(underTest.shouldIncludeFooterView)

            // WHEN has no notifs
            activeNotificationListRepository.setActiveNotifs(count = 0)
@@ -218,13 +225,14 @@ class NotificationListViewModelTest(flags: FlagsParameterization) : SysuiTestCas
            runCurrent()

            // THEN empty shade is visible
            assertThat(shouldInclude).isTrue()
            assertThat(shouldShowEmptyShadeView).isTrue()
            assertThat(shouldIncludeFooterView?.value).isFalse()
        }

    @Test
    fun shouldIncludeEmptyShadeView_falseWhenKeyguard() =
    fun shouldShowEmptyShadeView_falseWhenKeyguard() =
        testScope.runTest {
            val shouldInclude by collectLastValue(underTest.shouldShowEmptyShadeView)
            val shouldShow by collectLastValue(underTest.shouldShowEmptyShadeView)

            // WHEN has no notifs
            activeNotificationListRepository.setActiveNotifs(count = 0)
@@ -233,13 +241,13 @@ class NotificationListViewModelTest(flags: FlagsParameterization) : SysuiTestCas
            runCurrent()

            // THEN empty shade is not visible
            assertThat(shouldInclude).isFalse()
            assertThat(shouldShow).isFalse()
        }

    @Test
    fun shouldIncludeEmptyShadeView_falseWhenStartingToSleep() =
    fun shouldShowEmptyShadeView_falseWhenStartingToSleep() =
        testScope.runTest {
            val shouldInclude by collectLastValue(underTest.shouldShowEmptyShadeView)
            val shouldShow by collectLastValue(underTest.shouldShowEmptyShadeView)

            // WHEN has no notifs
            activeNotificationListRepository.setActiveNotifs(count = 0)
@@ -250,7 +258,7 @@ class NotificationListViewModelTest(flags: FlagsParameterization) : SysuiTestCas
            runCurrent()

            // THEN empty shade is not visible
            assertThat(shouldInclude).isFalse()
            assertThat(shouldShow).isFalse()
        }

    @Test
@@ -302,7 +310,8 @@ class NotificationListViewModelTest(flags: FlagsParameterization) : SysuiTestCas
    @Test
    fun shouldIncludeFooterView_trueWhenShade() =
        testScope.runTest {
            val shouldInclude by collectLastValue(underTest.shouldIncludeFooterView)
            val shouldIncludeFooterView by collectLastValue(underTest.shouldIncludeFooterView)
            val shouldShowEmptyShadeView by collectLastValue(underTest.shouldShowEmptyShadeView)

            // WHEN has notifs
            activeNotificationListRepository.setActiveNotifs(count = 2)
@@ -312,13 +321,15 @@ class NotificationListViewModelTest(flags: FlagsParameterization) : SysuiTestCas
            runCurrent()

            // THEN footer is visible
            assertThat(shouldInclude?.value).isTrue()
            assertThat(shouldIncludeFooterView?.value).isTrue()
            assertThat(shouldShowEmptyShadeView).isFalse()
        }

    @Test
    fun shouldIncludeFooterView_trueWhenLockedShade() =
        testScope.runTest {
            val shouldInclude by collectLastValue(underTest.shouldIncludeFooterView)
            val shouldIncludeFooterView by collectLastValue(underTest.shouldIncludeFooterView)
            val shouldShowEmptyShadeView by collectLastValue(underTest.shouldShowEmptyShadeView)

            // WHEN has notifs
            activeNotificationListRepository.setActiveNotifs(count = 2)
@@ -328,7 +339,8 @@ class NotificationListViewModelTest(flags: FlagsParameterization) : SysuiTestCas
            runCurrent()

            // THEN footer is visible
            assertThat(shouldInclude?.value).isTrue()
            assertThat(shouldIncludeFooterView?.value).isTrue()
            assertThat(shouldShowEmptyShadeView).isFalse()
        }

    @Test
@@ -404,7 +416,8 @@ class NotificationListViewModelTest(flags: FlagsParameterization) : SysuiTestCas
    @Test
    fun shouldIncludeFooterView_trueWhenQsExpandedSplitShade() =
        testScope.runTest {
            val shouldInclude by collectLastValue(underTest.shouldIncludeFooterView)
            val shouldIncludeFooterView by collectLastValue(underTest.shouldIncludeFooterView)
            val shouldShowEmptyShadeView by collectLastValue(underTest.shouldShowEmptyShadeView)

            // WHEN has notifs
            activeNotificationListRepository.setActiveNotifs(count = 2)
@@ -419,7 +432,8 @@ class NotificationListViewModelTest(flags: FlagsParameterization) : SysuiTestCas
            runCurrent()

            // THEN footer is visible
            assertThat(shouldInclude?.value).isTrue()
            assertThat(shouldIncludeFooterView?.value).isTrue()
            assertThat(shouldShowEmptyShadeView).isFalse()
        }

    @Test
+14 −0
Original line number Diff line number Diff line
@@ -442,12 +442,26 @@ public class StackScrollAlgorithm {
        state.visibleChildren.clear();
        state.visibleChildren.ensureCapacity(childCount);
        int notGoneIndex = 0;
        boolean emptyShadeVisible = false;
        for (int i = 0; i < childCount; i++) {
            ExpandableView v = (ExpandableView) mHostView.getChildAt(i);
            if (v.getVisibility() != View.GONE) {
                if (v == ambientState.getShelf()) {
                    continue;
                }
                if (FooterViewRefactor.isEnabled()) {
                    if (v instanceof EmptyShadeView) {
                        emptyShadeVisible = true;
                    }
                    if (v instanceof FooterView) {
                        if (emptyShadeVisible || notGoneIndex == 0) {
                            // if the empty shade is visible or the footer is the first visible
                            // view, we're in a transitory state so let's leave the footer alone.
                            continue;
                        }
                    }
                }

                notGoneIndex = updateNotGoneIndex(state, notGoneIndex, v);
                if (v instanceof ExpandableNotificationRow row) {

+4 −1
Original line number Diff line number Diff line
@@ -8,6 +8,7 @@ import android.widget.FrameLayout
import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
import com.android.keyguard.BouncerPanelExpansionCalculator.aboutToShowBouncerProgress
import com.android.systemui.Flags
import com.android.systemui.SysuiTestCase
import com.android.systemui.animation.ShadeInterpolation.getContentAlpha
import com.android.systemui.dump.DumpManager
@@ -523,6 +524,7 @@ class StackScrollAlgorithmTest : SysuiTestCase() {
        assertThat((footerView.viewState as FooterViewState).hideContent).isTrue()
    }

    @DisableFlags(Flags.FLAG_NOTIFICATIONS_FOOTER_VIEW_REFACTOR)
    @Test
    fun resetViewStates_clearAllInProgress_allRowsRemoved_emptyShade_footerHidden() {
        ambientState.isClearAllInProgress = true
@@ -1157,6 +1159,7 @@ class StackScrollAlgorithmTest : SysuiTestCase() {

        assertFalse(stackScrollAlgorithm.shouldHunAppearFromBottom(ambientState, viewState))
    }

    // endregion

    private fun createHunViewMock(