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

Commit fe7f4f6c authored by Automerger Merge Worker's avatar Automerger Merge Worker Committed by Android (Google) Code Review
Browse files

Merge "Merge "Fix Notification clipping flicker during AOD=>LS" into udc-dev...

Merge "Merge "Fix Notification clipping flicker during AOD=>LS" into udc-dev am: 51a5c2fd am: 44aa5601"
parents 9fb68530 5f6cff85
Loading
Loading
Loading
Loading
+19 −0
Original line number Diff line number Diff line
@@ -93,6 +93,12 @@ public class AmbientState implements Dumpable {
    private boolean mAppearing;
    private float mPulseHeight = MAX_PULSE_HEIGHT;

    /**
     * The ExpandableNotificationRow that is pulsing, or the one that was pulsing
     * when the device started to transition from AOD to LockScreen.
     */
    private ExpandableNotificationRow mPulsingRow;

    /** Fraction of lockscreen to shade animation (on lockscreen swipe down). */
    private float mFractionToShade;

@@ -564,6 +570,19 @@ public class AmbientState implements Dumpable {
        return mPulsing && entry.isAlerting();
    }

    public void setPulsingRow(ExpandableNotificationRow row) {
        mPulsingRow = row;
    }

    /**
     * @param row The row to check
     * @return true if row is the pulsing row when the device started to transition from AOD to lock
     * screen
     */
    public boolean isPulsingRow(ExpandableView row) {
        return mPulsingRow == row;
    }

    public boolean isPanelTracking() {
        return mPanelTracking;
    }
+22 −2
Original line number Diff line number Diff line
@@ -548,7 +548,7 @@ public class StackScrollAlgorithm {
        ExpandableViewState viewState = view.getViewState();
        viewState.location = ExpandableViewState.LOCATION_UNKNOWN;

        final float expansionFraction = getExpansionFractionWithoutShelf(
        float expansionFraction = getExpansionFractionWithoutShelf(
                algorithmState, ambientState);

        // Add gap between sections.
@@ -619,6 +619,11 @@ public class StackScrollAlgorithm {
                    updateViewWithShelf(view, viewState, shelfStart);
                }
            }
            // Avoid pulsing notification flicker during AOD to LS
            // A pulsing notification is already expanded, no need to expand it again with animation
            if (ambientState.isPulsingRow(view)) {
                expansionFraction = 1.0f;
            }
            // Clip height of view right before shelf.
            viewState.height = (int) (getMaxAllowedChildHeight(view) * expansionFraction);
        }
@@ -700,9 +705,11 @@ public class StackScrollAlgorithm {
                && !(child instanceof FooterView);
    }

    private void updatePulsingStates(StackScrollAlgorithmState algorithmState,
    @VisibleForTesting
    void updatePulsingStates(StackScrollAlgorithmState algorithmState,
                                     AmbientState ambientState) {
        int childCount = algorithmState.visibleChildren.size();
        ExpandableNotificationRow pulsingRow = null;
        for (int i = 0; i < childCount; i++) {
            View child = algorithmState.visibleChildren.get(i);
            if (!(child instanceof ExpandableNotificationRow)) {
@@ -714,6 +721,19 @@ public class StackScrollAlgorithm {
            }
            ExpandableViewState viewState = row.getViewState();
            viewState.hidden = false;
            pulsingRow = row;
        }

        // Set AmbientState#pulsingRow to the current pulsing row when on AOD.
        // Set AmbientState#pulsingRow=null when on lockscreen, since AmbientState#pulsingRow
        // is only used for skipping the unfurl animation for (the notification that was already
        // showing at full height on AOD) during the AOD=>lockscreen transition, where
        // dozeAmount=[1f, 0f). We also need to reset the pulsingRow once it is no longer used
        // because it will interfere with future unfurling animations - for example, during the
        // LS=>AOD animation, the pulsingRow may stay at full height when it should squish with the
        // rest of the stack.
        if (ambientState.getDozeAmount() == 0.0f || ambientState.getDozeAmount() == 1.0f) {
            ambientState.setPulsingRow(pulsingRow);
        }
    }

+111 −0
Original line number Diff line number Diff line
@@ -716,6 +716,94 @@ class StackScrollAlgorithmTest : SysuiTestCase() {
                .isLessThan(px(R.dimen.heads_up_pinned_elevation))
    }

    @Test
    fun aodToLockScreen_hasPulsingNotification_pulsingNotificationRowDoesNotChange() {
        // Given: Before AOD to LockScreen, there was a pulsing notification
        val pulsingNotificationView = createPulsingViewMock()
        val algorithmState = StackScrollAlgorithm.StackScrollAlgorithmState()
        algorithmState.visibleChildren.add(pulsingNotificationView)
        ambientState.setPulsingRow(pulsingNotificationView)

        // When: during AOD to LockScreen, any dozeAmount between (0, 1.0) is equivalent as a middle
        // stage; here we use 0.5 for testing.
        // stackScrollAlgorithm.updatePulsingStates is called
        ambientState.dozeAmount = 0.5f
        stackScrollAlgorithm.updatePulsingStates(algorithmState, ambientState)

        // Then: ambientState.pulsingRow should still be pulsingNotificationView
        assertTrue(ambientState.isPulsingRow(pulsingNotificationView))
    }

    @Test
    fun deviceOnAod_hasPulsingNotification_recordPulsingNotificationRow() {
        // Given: Device is on AOD, there is a pulsing notification
        // ambientState.pulsingRow is null before stackScrollAlgorithm.updatePulsingStates
        ambientState.dozeAmount = 1.0f
        val pulsingNotificationView = createPulsingViewMock()
        val algorithmState = StackScrollAlgorithm.StackScrollAlgorithmState()
        algorithmState.visibleChildren.add(pulsingNotificationView)
        ambientState.setPulsingRow(null)

        // When: stackScrollAlgorithm.updatePulsingStates is called
        stackScrollAlgorithm.updatePulsingStates(algorithmState, ambientState)

        // Then: ambientState.pulsingRow should record the pulsingNotificationView
        assertTrue(ambientState.isPulsingRow(pulsingNotificationView))
    }

    @Test
    fun deviceOnLockScreen_hasPulsingNotificationBefore_clearPulsingNotificationRowRecord() {
        // Given: Device finished AOD to LockScreen, there was a pulsing notification, and
        // ambientState.pulsingRow was not null before AOD to LockScreen
        // pulsingNotificationView.showingPulsing() returns false since the device is on LockScreen
        ambientState.dozeAmount = 0.0f
        val pulsingNotificationView = createPulsingViewMock()
        whenever(pulsingNotificationView.showingPulsing()).thenReturn(false)
        val algorithmState = StackScrollAlgorithm.StackScrollAlgorithmState()
        algorithmState.visibleChildren.add(pulsingNotificationView)
        ambientState.setPulsingRow(pulsingNotificationView)

        // When: stackScrollAlgorithm.updatePulsingStates is called
        stackScrollAlgorithm.updatePulsingStates(algorithmState, ambientState)

        // Then: ambientState.pulsingRow should be null
        assertTrue(ambientState.isPulsingRow(null))
    }

    @Test
    fun aodToLockScreen_hasPulsingNotification_pulsingNotificationRowShowAtFullHeight() {
        // Given: Before AOD to LockScreen, there was a pulsing notification
        val pulsingNotificationView = createPulsingViewMock()
        val algorithmState = StackScrollAlgorithm.StackScrollAlgorithmState()
        algorithmState.visibleChildren.add(pulsingNotificationView)
        ambientState.setPulsingRow(pulsingNotificationView)

        // When: during AOD to LockScreen, any dozeAmount between (0, 1.0) is equivalent as a middle
        // stage; here we use 0.5 for testing. The expansionFraction is also 0.5.
        // stackScrollAlgorithm.resetViewStates is called.
        ambientState.dozeAmount = 0.5f
        setExpansionFractionWithoutShelfDuringAodToLockScreen(
                ambientState,
                algorithmState,
                fraction = 0.5f
        )
        stackScrollAlgorithm.resetViewStates(ambientState, 0)

        // Then: pulsingNotificationView should show at full height
        assertEquals(
                stackScrollAlgorithm.getMaxAllowedChildHeight(pulsingNotificationView),
                pulsingNotificationView.viewState.height
        )

        // After: reset dozeAmount and expansionFraction
        ambientState.dozeAmount = 0f
        setExpansionFractionWithoutShelfDuringAodToLockScreen(
                ambientState,
                algorithmState,
                fraction = 1f
        )
    }

    private fun createHunViewMock(
            isShadeOpen: Boolean,
            fullyVisible: Boolean,
@@ -744,6 +832,29 @@ class StackScrollAlgorithmTest : SysuiTestCase() {
                headsUpIsVisible = fullyVisible
            }

    private fun createPulsingViewMock(
    ) =
            mock<ExpandableNotificationRow>().apply {
                whenever(this.viewState).thenReturn(ExpandableViewState())
                whenever(this.showingPulsing()).thenReturn(true)
            }

    private fun setExpansionFractionWithoutShelfDuringAodToLockScreen(
            ambientState: AmbientState,
            algorithmState: StackScrollAlgorithm.StackScrollAlgorithmState,
            fraction: Float
    ) {
        // showingShelf: false
        algorithmState.firstViewInShelf = null
        // scrimPadding: 0, because device is on lock screen
        ambientState.setStatusBarState(StatusBarState.KEYGUARD)
        ambientState.dozeAmount = 0.0f
        // set stackEndHeight and stackHeight
        // ExpansionFractionWithoutShelf == stackHeight / stackEndHeight
        ambientState.stackEndHeight = 100f
        ambientState.stackHeight = ambientState.stackEndHeight * fraction
    }

    private fun resetViewStates_expansionChanging_notificationAlphaUpdated(
            expansionFraction: Float,
            expectedAlpha: Float,