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

Commit 9d7da630 authored by András Kurucz's avatar András Kurucz Committed by Android (Google) Code Review
Browse files

Merge "[Flexiglass] Determine the z position of HUNs with the SceneContainer stack" into main

parents 266e4cf2 9fd5292c
Loading
Loading
Loading
Loading
+103 −45
Original line number Diff line number Diff line
@@ -16,6 +16,8 @@

package com.android.systemui.statusbar.notification.stack;

import static androidx.core.math.MathUtils.clamp;

import android.annotation.NonNull;
import android.annotation.Nullable;
import android.content.Context;
@@ -899,11 +901,33 @@ public class StackScrollAlgorithm {
                    if (shouldHunBeVisibleWhenScrolled(row.mustStayOnScreen(),
                            childState.headsUpIsVisible, row.showingPulsing(),
                            ambientState.isOnKeyguard(), row.getEntry().isStickyAndNotDemoted())) {
                        // the height of this child before clamping it to the top
                        float unmodifiedChildHeight = childState.height;
                        clampHunToTop(
                                /* headsUpTop = */ headsUpTranslation,
                                /* collapsedHeight = */ row.getCollapsedHeight(),
                                /* viewState = */ childState
                        );
                        float baseZ = ambientState.getBaseZHeight();
                        if (headsUpTranslation < ambientState.getStackTop()) {
                            // HUN displayed above the stack top, it needs a fix shadow
                            childState.setZTranslation(baseZ + mPinnedZTranslationExtra);
                        } else {
                            // HUN displayed within the stack, add a shadow if it overlaps with
                            // other elements.
                            //
                            // Views stack vertically from the top. Add the HUN's original height
                            // (before clamping) to the stack top, to determine the starting
                            // point for the remaining content.
                            float scrollingContentTop =
                                    ambientState.getStackTop() + unmodifiedChildHeight;
                            updateZTranslationForHunInStack(
                                    /* scrollingContentTop = */ scrollingContentTop,
                                    /* scrollingContentTopPadding = */ mGapHeight,
                                    /* baseZ = */ baseZ,
                                    /* viewState = */ childState
                            );
                        }
                        if (isTopEntry && row.isAboveShelf()) {
                            clampHunToMaxTranslation(
                                    /* headsUpTop =  */ headsUpTranslation,
@@ -1040,8 +1064,30 @@ public class StackScrollAlgorithm {
        // Transition from collapsed pinned state to fully expanded state
        // when the pinned HUN approaches its actual location (when scrolling back to top).
        final float distToRealY = newTranslation - viewState.getYTranslation();
        viewState.height = (int) Math.max(viewState.height - distToRealY, collapsedHeight);
        final float availableHeight = viewState.height - distToRealY;

        viewState.setYTranslation(newTranslation);
        viewState.height = (int) Math.max(availableHeight, collapsedHeight);
    }

    @VisibleForTesting
    void updateZTranslationForHunInStack(float scrollingContentTop,
            float scrollingContentTopPadding, float baseZ, ExpandableViewState viewState) {
        if (SceneContainerFlag.isUnexpectedlyInLegacyMode()) return;
        float hunBottom = viewState.getYTranslation() + viewState.height;
        float overlap = Math.max(0f, hunBottom - scrollingContentTop);

        float shadowFraction = 1f;
        if (scrollingContentTopPadding > 0f) {
            // scrollingContentTopPadding makes a gap between the bottom of the HUN and the top
            // of the scrolling content. Use this to animate to the full shadow.
            shadowFraction = clamp(overlap / scrollingContentTopPadding, 0f, 1f);
        }

        if (overlap > 0.0f) {
            // add a shadow to this HUN, because it overlaps with the scrolling stack
            viewState.setZTranslation(baseZ + shadowFraction * mPinnedZTranslationExtra);
        }
    }

    // Pin HUN to bottom of expanded QS
@@ -1151,6 +1197,15 @@ public class StackScrollAlgorithm {
        ExpandableViewState childViewState = child.getViewState();
        float baseZ = ambientState.getBaseZHeight();

        if (SceneContainerFlag.isEnabled()) {
            // SceneContainer flags off this logic, and just sets the baseZ because:
            // - there are no overlapping HUNs anymore, no need for multiplying their shadows
            // - shadows for HUNs overlapping with the stack are now set from updateHeadsUpStates
            // - shadows for HUNs overlapping with the shelf are NOT set anymore, because it only
            // happens on AOD/Pulsing, where they're displayed on a black background so a shadow
            // wouldn't be visible.
            childViewState.setZTranslation(baseZ);
        } else {
            if (child.mustStayOnScreen() && !childViewState.headsUpIsVisible
                    && !ambientState.isDozingAndNotPulsing(child)
                    && childViewState.getYTranslation() < ambientState.getTopPadding()
@@ -1162,7 +1217,7 @@ public class StackScrollAlgorithm {
                } else {
                    // Handles HUN shadow when Shade is opened, and AmbientState.mScrollY > 0
                    // Calculate the HUN's z-value based on its overlapping fraction with QQS Panel.
                // When scrolling down shade to make HUN back to in-position in Notification Panel,
                    // When scrolling down shade to make HUN back to in-position in Notif Panel,
                    // The overlapping fraction goes to 0, and shadows hides gradually.
                    float overlap = ambientState.getTopPadding()
                            + ambientState.getStackTranslation() - childViewState.getYTranslation();
@@ -1182,10 +1237,12 @@ public class StackScrollAlgorithm {
                float shelfStart = ambientState.getInnerHeight()
                        - shelfHeight + ambientState.getTopPadding()
                        + ambientState.getStackTranslation();
            float notificationEnd = childViewState.getYTranslation() + child.getIntrinsicHeight()
                float notificationEnd =
                        childViewState.getYTranslation() + child.getIntrinsicHeight()
                                + mPaddingBetweenElements;
                if (shelfStart > notificationEnd) {
                // When the notification doesn't overlap with Notification Shelf, there's no shadow
                    // When the notification doesn't overlap with Notification Shelf,
                    // there's no shadow
                    childViewState.setZTranslation(baseZ);
                } else {
                    // Give shadow to the notification if it overlaps with Notification Shelf
@@ -1199,6 +1256,7 @@ public class StackScrollAlgorithm {
            } else {
                childViewState.setZTranslation(baseZ);
            }
        }

        // While HUN is showing and Shade is closed: headerVisibleAmount stays 0, shadow stays.
        // During HUN-to-Shade (eg. dragging down HUN to open Shade): headerVisibleAmount goes
+91 −2
Original line number Diff line number Diff line
@@ -92,9 +92,12 @@ class StackScrollAlgorithmTest : SysuiTestCase() {
    private fun px(@DimenRes id: Int): Float =
        testableResources.resources.getDimensionPixelSize(id).toFloat()

    private val bigGap = px(R.dimen.notification_section_divider_height)
    private val smallGap = px(R.dimen.notification_section_divider_height_lockscreen)
    private val notifSectionDividerGap = px(R.dimen.notification_section_divider_height)
    private val scrimPadding = px(R.dimen.notification_side_paddings)
    private val baseZ by lazy { ambientState.baseZHeight }
    private val headsUpZ = px(R.dimen.heads_up_pinned_elevation)
    private val bigGap = notifSectionDividerGap
    private val smallGap = px(R.dimen.notification_section_divider_height_lockscreen)

    @Before
    fun setUp() {
@@ -219,6 +222,8 @@ class StackScrollAlgorithmTest : SysuiTestCase() {

        // Then: HUN is at the headsUpTop
        assertThat(notificationRow.viewState.yTranslation).isEqualTo(headsUpTop)
        // And: HUN is not elevated
        assertThat(notificationRow.viewState.zTranslation).isEqualTo(baseZ)
        // And: HUN has its full height
        assertThat(notificationRow.viewState.height).isEqualTo(intrinsicHeight)
    }
@@ -243,6 +248,8 @@ class StackScrollAlgorithmTest : SysuiTestCase() {

        // Then: HUN is translated to the headsUpTop
        assertThat(notificationRow.viewState.yTranslation).isEqualTo(headsUpTop)
        // And: HUN is not elevated
        assertThat(notificationRow.viewState.zTranslation).isEqualTo(baseZ)
        // And: HUN is clipped to the available space
        // newTranslation = max(150, -25)
        // distToReal = 150 - (-25)
@@ -270,6 +277,8 @@ class StackScrollAlgorithmTest : SysuiTestCase() {

        // Then: HUN is translated to the headsUpTop
        assertThat(notificationRow.viewState.yTranslation).isEqualTo(headsUpTop)
        // And: HUN fully elevated to baseZ + headsUpZ
        assertThat(notificationRow.viewState.zTranslation).isEqualTo(baseZ + headsUpZ)
        // And: HUN is clipped to its collapsed height
        assertThat(notificationRow.viewState.height).isEqualTo(collapsedHeight)
    }
@@ -294,10 +303,87 @@ class StackScrollAlgorithmTest : SysuiTestCase() {

        // Then: HUN is translated to the headsUpTop
        assertThat(notificationRow.viewState.yTranslation).isEqualTo(headsUpTop)
        // And: HUN is elevated to baseZ + headsUpZ
        assertThat(notificationRow.viewState.zTranslation).isEqualTo(baseZ + headsUpZ)
        // And: HUN maintained its full height
        assertThat(notificationRow.viewState.height).isEqualTo(intrinsicHunHeight)
    }

    @Test
    @EnableSceneContainer
    fun updateZTranslationForHunInStack_fullOverlap_hunHasFullElevation() {
        // Given: the overlap equals to the top content padding
        val contentTop = 280f
        val contentTopPadding = 20f
        val viewState =
            ExpandableViewState().apply {
                height = 100
                yTranslation = 200f
            }

        // When
        stackScrollAlgorithm.updateZTranslationForHunInStack(
            /* scrollingContentTop = */ contentTop,
            /* scrollingContentTopPadding */ contentTopPadding,
            /* baseZ = */ 0f,
            /* viewState = */ viewState,
        )

        // Then: HUN is fully elevated to baseZ + headsUpZ
        assertThat(viewState.zTranslation).isEqualTo(headsUpZ)
    }

    @Test
    @EnableSceneContainer
    fun updateZTranslationForHunInStack_someOverlap_hunIsPartlyElevated() {
        // Given: the overlap is bigger than zero, but less than the top content padding
        val contentTop = 290f
        val contentTopPadding = 20f
        val viewState =
            ExpandableViewState().apply {
                height = 100
                yTranslation = 200f
            }

        // When
        stackScrollAlgorithm.updateZTranslationForHunInStack(
            /* scrollingContentTop = */ contentTop,
            /* scrollingContentTopPadding */ contentTopPadding,
            /* baseZ = */ 0f,
            /* viewState = */ viewState,
        )

        // Then: HUN is partly elevated
        assertThat(viewState.zTranslation).apply {
            isGreaterThan(0f)
            isLessThan(headsUpZ)
        }
    }

    @Test
    @EnableSceneContainer
    fun updateZTranslationForHunInStack_noOverlap_hunIsNotElevated() {
        // Given: no overlap between the content and the HUN
        val contentTop = 300f
        val contentTopPadding = 20f
        val viewState =
            ExpandableViewState().apply {
                height = 100
                yTranslation = 200f
            }

        // When
        stackScrollAlgorithm.updateZTranslationForHunInStack(
            /* scrollingContentTop = */ contentTop,
            /* scrollingContentTopPadding */ contentTopPadding,
            /* baseZ = */ 0f,
            /* viewState = */ viewState,
        )

        // Then: HUN is not elevated
        assertThat(viewState.zTranslation).isEqualTo(0f)
    }

    @Test
    @DisableSceneContainer
    @EnableFlags(NotificationsImprovedHunAnimation.FLAG_NAME)
@@ -971,6 +1057,7 @@ class StackScrollAlgorithmTest : SysuiTestCase() {
    }

    @Test
    @DisableSceneContainer
    fun shadeOpened_hunFullyOverlapsQqsPanel_hunShouldHaveFullShadow() {
        // Given: shade is opened, yTranslation of HUN is 0,
        // the height of HUN equals to the height of QQS Panel,
@@ -996,6 +1083,7 @@ class StackScrollAlgorithmTest : SysuiTestCase() {
    }

    @Test
    @DisableSceneContainer
    fun shadeOpened_hunPartiallyOverlapsQQS_hunShouldHavePartialShadow() {
        // Given: shade is opened, yTranslation of HUN is greater than 0,
        // the height of HUN is equal to the height of QQS Panel,
@@ -1447,6 +1535,7 @@ class StackScrollAlgorithmTest : SysuiTestCase() {
        }
        stackScrollAlgorithm.setIsExpanded(true)

        whenever(notificationRow.headerVisibleAmount).thenReturn(1.0f)
        whenever(notificationRow.mustStayOnScreen()).thenReturn(true)
        whenever(notificationRow.isHeadsUp).thenReturn(true)
        whenever(notificationRow.collapsedHeight).thenReturn(collapsedHeight)