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

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

Merge "[Flexiglass] Handle remoteInput scrolling for nested notifications" into main

parents 44a60618 6a74879f
Loading
Loading
Loading
Loading
+65 −0
Original line number Diff line number Diff line
@@ -2056,6 +2056,69 @@ public class NotificationStackScrollLayoutTest extends SysuiTestCase {
        assertThat(mStackScroller.getOwnScrollY()).isEqualTo(100 - childHeight);
    }

    @Test
    @EnableSceneContainer
    public void testRequestScrollToRemoteInput_singleRow() {
        final Consumer<Float> scroller = mock(FloatConsumer.class);
        mStackScroller.setRemoteInputRowBottomBoundConsumer(scroller);

        // GIVEN a non-grouped notification row with specific dimensions and offsets
        ExpandableNotificationRow row = mock(ExpandableNotificationRow.class);
        when(row.getTranslationY()).thenReturn(100f);
        when(row.getActualHeight()).thenReturn(200);
        when(row.getRemoteInputActionsContainerExpandedOffset()).thenReturn(50f);
        mTestableResources.addOverride(
                com.android.internal.R.dimen.notification_content_margin, 10);

        // WHEN requestScrollToRemoteInput is called
        mStackScroller.requestScrollToRemoteInput(row);

        // THEN the correct bottom bound is sent
        // expected = translationY + height + remoteInputOffset + contentMargin
        // expected = 100 + 200 + 50 + 10 = 360
        verify(scroller).accept(360f);

        // WHEN requestScrollToRemoteInput is called with null
        mStackScroller.requestScrollToRemoteInput(null);

        // THEN null is sent
        verify(scroller).accept(null);
    }

    @Test
    @EnableSceneContainer
    public void testRequestScrollToRemoteInput_childInGroup() {
        final Consumer<Float> scroller = mock(FloatConsumer.class);
        mStackScroller.setRemoteInputRowBottomBoundConsumer(scroller);

        // GIVEN a grouped notification row
        ExpandableNotificationRow parent = mock(ExpandableNotificationRow.class);
        ExpandableNotificationRow row = mock(ExpandableNotificationRow.class);
        when(row.getEntryAdapter()).thenReturn(mock(EntryAdapter.class));
        when(mGroupMembershipManger.isChildInGroup(row.getEntryAdapter())).thenReturn(true);
        when(row.getTranslationY()).thenReturn(100f);
        when(row.getActualHeight()).thenReturn(200);
        when(row.getRemoteInputActionsContainerExpandedOffset()).thenReturn(50f);
        when(parent.getTranslationY()).thenReturn(50f);
        when(row.getNotificationParent()).thenReturn(parent);
        mTestableResources.addOverride(
                com.android.internal.R.dimen.notification_content_margin, 10);

        // WHEN requestScrollToRemoteInput is called for the grouped row
        mStackScroller.requestScrollToRemoteInput(row);

        // THEN the correct bottom bound is sent, including the parent's translation
        // expected = parentTranslationY + translationY + height + remoteInputOffset + contentMargin
        // expected = 50 + 100 + 200 + 50 + 10 = 410
        verify(scroller).accept(410f);

        // WHEN requestScrollToRemoteInput is called with null
        mStackScroller.requestScrollToRemoteInput(null);

        // THEN null is sent
        verify(scroller).accept(null);
    }

    private MotionEvent captureTouchSentToSceneFramework() {
        ArgumentCaptor<MotionEvent> captor = ArgumentCaptor.forClass(MotionEvent.class);
        verify(mStackScrollLayoutController).sendTouchToSceneFramework(captor.capture());
@@ -2150,6 +2213,8 @@ public class NotificationStackScrollLayoutTest extends SysuiTestCase {

    private abstract static class BooleanConsumer implements Consumer<Boolean> { }

    private abstract static class FloatConsumer implements Consumer<Float> { }

    private ShadeScrimShape createScrimShape(int left, int top, int right, int bottom) {
        ShadeScrimBounds bounds = new ShadeScrimBounds(left, top, right, bottom);
        return new ShadeScrimShape(bounds, 0, 0);
+31 −5
Original line number Diff line number Diff line
@@ -766,15 +766,41 @@ public class NotificationStackScrollLayout
        mSectionsManager.reinflateViews();
    }

    void sendRemoteInputRowBottomBound(Float bottom) {
        if (SceneContainerFlag.isUnexpectedlyInLegacyMode()) return;
        if (bottom != null) {
            bottom += getResources().getDimensionPixelSize(
                    com.android.internal.R.dimen.notification_content_margin);
    /**
     * Requests to scroll to the RemoteInput view of the given row.
     * @param row currently holding the active RemoteInput, or null if the RemoteInput is inactive.
     */
    void requestScrollToRemoteInput(@Nullable ExpandableNotificationRow row) {
        if (SceneContainerFlag.isUnexpectedlyInLegacyMode()) {
            return;
        }
        // When SceneContainer is enabled, the NSSL doesn't handle its own scrolling.
        // Instead, it calculates the required scroll amount and emits it as a "synthetic scroll"
        // event. The Compose-based UI (`NotificationScrollingStack.kt`) consumes this event
        // and performs the actual scroll, synchronizing it with the IME animation.
        Float bottom = (row != null) ? getRemoteInputViewBottom(row) : null;
        mScrollViewFields.sendRemoteInputRowBottomBound(bottom);
    }

    /**
     * Calculates the Y position of the bottom of the remoteInput view, relative to the NSSL.
     *
     * @param row with an active remoteInput
     * @return The bottom Y position of the view relative to this layout.
     */
    private float getRemoteInputViewBottom(ExpandableNotificationRow row) {
        if (SceneContainerFlag.isUnexpectedlyInLegacyMode()) return 0f;
        // Calculate the base top position
        float topPosition = row.getTranslationY()
                // For nested notifications, add the parent's translation.
                + (isChildInGroup(row) ? row.getNotificationParent().getTranslationY() : 0f);
        int height = row.getActualHeight();
        float remoteInputOffset = row.getRemoteInputActionsContainerExpandedOffset();
        float contentMargin = getResources().getDimensionPixelSize(
                com.android.internal.R.dimen.notification_content_margin);
        return topPosition + height + remoteInputOffset + contentMargin;
    }

    void updateBgColor() {
        for (int i = 0; i < getChildCount(); i++) {
            View child = getChildAt(i);
+6 −7
Original line number Diff line number Diff line
@@ -1670,28 +1670,27 @@ public class NotificationStackScrollLayoutController implements Dumpable {
            public void setRemoteInputActive(RemoteInputEntryAdapter entry,
                    boolean remoteInputActive) {
                if (SceneContainerFlag.isEnabled()) {
                    sendRemoteInputRowBottomBound(entry, remoteInputActive);
                    setRemoteInputActiveRow(entry, remoteInputActive);
                }
                entry.setRemoteInputActive(mHeadsUpManager, remoteInputActive);
                entry.notifyHeightChanged(true /* needsAnimation */);
            }

            public void lockScrollTo(ExpandableNotificationRow row) {
                if (!SceneContainerFlag.isEnabled()) {
                    mView.lockScrollTo(row);
                }
            }

            public void requestDisallowLongPressAndDismiss() {
                mView.requestDisallowLongPress();
                mView.requestDisallowDismiss();
            }

            private void sendRemoteInputRowBottomBound(RemoteInputEntryAdapter entry,
            private void setRemoteInputActiveRow(RemoteInputEntryAdapter entry,
                    boolean remoteInputActive) {
                ExpandableNotificationRow row = entry.getRow();
                float top = row.getTranslationY();
                int height = row.getActualHeight();
                float bottom = top + height + row.getRemoteInputActionsContainerExpandedOffset();
                mView.sendRemoteInputRowBottomBound(remoteInputActive ? bottom : null);
                mView.requestScrollToRemoteInput(remoteInputActive ? row : null);
            }
        };
    }