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

Commit c14734d8 authored by Mark Renouf's avatar Mark Renouf
Browse files

Account for padding in dispatchScrollCaptureSearch

When dispatching to self, ViewGroup is  not applying padding
to localVisibleRect, only when dispatching to child views.

Also when deducting padding from the visible bounds,
it must be intersected with bounds, not subtracted.

Test: atest ViewGroupScrollCaptureTest
Bug: 185153400
Change-Id: Ib92e4c552fbe02fd863123f166878e19b8b1c952
parent 6304a503
Loading
Loading
Loading
Loading
+1 −1
Original line number Diff line number Diff line
@@ -1494,7 +1494,7 @@ public class View implements Drawable.Callback, KeyEvent.Callback,
    public static final int SCROLL_CAPTURE_HINT_AUTO = 0;
    /**
     * Explicitly exclcude this view as a potential scroll capture target. The system will not
     * Explicitly exclude this view as a potential scroll capture target. The system will not
     * consider it. Mutually exclusive with {@link #SCROLL_CAPTURE_HINT_INCLUDE}, which this flag
     * takes precedence over.
     *
+8 −13
Original line number Diff line number Diff line
@@ -7479,12 +7479,9 @@ public abstract class ViewGroup extends View implements ViewParent, ViewManager
            @NonNull Rect localVisibleRect, @NonNull Point windowOffset,
            @NonNull Consumer<ScrollCaptureTarget> targets) {

        // copy local visible rect for modification and dispatch
        final Rect rect = getTempRect();
        rect.set(localVisibleRect);

        if (getClipToPadding()) {
            rect.inset(mPaddingLeft, mPaddingTop, mPaddingRight, mPaddingBottom);
        if (getClipToPadding() && !localVisibleRect.intersect(mPaddingLeft, mPaddingTop,
                    (mRight - mLeft)  - mPaddingRight, (mBottom - mTop) - mPaddingBottom)) {
            return;
        }

        // Dispatch to self first.
@@ -7495,6 +7492,7 @@ public abstract class ViewGroup extends View implements ViewParent, ViewManager
            return;
        }

        final Rect tmpRect = getTempRect();
        final int childCount = getChildCount();
        for (int i = 0; i < childCount; i++) {
            View child = getChildAt(i);
@@ -7507,8 +7505,7 @@ public abstract class ViewGroup extends View implements ViewParent, ViewManager
            // If the resulting rectangle is not empty, the request is forwarded to the child.

            // copy local visible rect for modification and dispatch
            final Rect childVisibleRect = getTempRect();
            childVisibleRect.set(localVisibleRect);
            tmpRect.set(localVisibleRect);

            // transform to child coords
            final Point childWindowOffset = getTempPoint();
@@ -7517,20 +7514,18 @@ public abstract class ViewGroup extends View implements ViewParent, ViewManager
            final int dx = child.mLeft - mScrollX;
            final int dy = child.mTop - mScrollY;

            childVisibleRect.offset(-dx, -dy);
            tmpRect.offset(-dx, -dy);
            childWindowOffset.offset(dx, dy);

            boolean rectIsVisible = true;

            // Clip to child bounds
            if (getClipChildren()) {
                rectIsVisible = childVisibleRect.intersect(0, 0, child.getWidth(),
                        child.getHeight());
                rectIsVisible = tmpRect.intersect(0, 0, child.getWidth(), child.getHeight());
            }

            // Clip to child padding.
            if (rectIsVisible) {
                child.dispatchScrollCaptureSearch(childVisibleRect, childWindowOffset, targets);
                child.dispatchScrollCaptureSearch(tmpRect, childWindowOffset, targets);
            }
        }
    }
+82 −1
Original line number Diff line number Diff line
@@ -33,7 +33,6 @@ import android.os.CancellationSignal;
import android.platform.test.annotations.Presubmit;

import androidx.annotation.NonNull;
import androidx.test.filters.FlakyTest;
import androidx.test.filters.MediumTest;
import androidx.test.filters.SmallTest;

@@ -343,6 +342,54 @@ public class ViewGroupScrollCaptureTest {
                target.getContainingView().getScrollCaptureHint());
    }

    /**
     * Tests the effect of padding on scroll capture search dispatch.
     * <p>
     * Verifies computation of child visible bounds with padding.
     */
    @MediumTest
    @Test
    public void testOnScrollCaptureSearch_withPadding() {
        final Context context = getInstrumentation().getContext();

        Rect windowBounds = new Rect(0, 0, 200, 200);
        Point windowOffset = new Point(0, 0);

        final MockViewGroup parent = new MockViewGroup(context, 0, 0, 200, 200);
        parent.setPadding(25, 50, 25, 50);
        parent.setClipToPadding(true); // (default)

        final MockView view1 = new MockView(context, 0, -100, 200, 100);
        parent.addView(view1);

        final MockView view2 = new MockView(context, 0, 0, 200, 200);
        parent.addView(view2);

        final MockViewGroup view3 = new MockViewGroup(context, 0, 100, 200, 300);
        parent.addView(view3);
        view3.setPadding(25, 25, 25, 25);
        view3.setClipToPadding(true);

        // Where targets are added
        final ScrollCaptureSearchResults results = new ScrollCaptureSearchResults(DIRECT_EXECUTOR);

        // Dispatch to the ViewGroup
        parent.dispatchScrollCaptureSearch(windowBounds, windowOffset, results::addTarget);

        // Verify padding (with clipToPadding) is subtracted from visibleBounds
        parent.assertOnScrollCaptureSearchLastArgs(new Rect(25, 50, 175, 150), new Point(0, 0));

        view1.assertOnScrollCaptureSearchLastArgs(
                new Rect(25, 150, 175, 200), new Point(0, -100));

        view2.assertOnScrollCaptureSearchLastArgs(
                new Rect(25, 50, 175, 150), new Point(0, 0));

        // Account for padding on view3 as well (top == 25px)
        view3.assertOnScrollCaptureSearchLastArgs(
                new Rect(25, 25, 175, 50), new Point(0, 100));
    }

    public static final class MockView extends View {
        private ScrollCaptureCallback mInternalCallback;

@@ -350,6 +397,8 @@ public class ViewGroupScrollCaptureTest {
        private Rect mDispatchScrollCaptureSearchLastLocalVisibleRect;
        private Point mDispatchScrollCaptureSearchLastWindowOffset;
        private int mCreateScrollCaptureCallbackInternalCount;
        private Rect mOnScrollCaptureSearchLastLocalVisibleRect;
        private Point mOnScrollCaptureSearchLastWindowOffset;

        MockView(Context context) {
            this(context, /* left */ 0, /* top */0, /* right */ 0, /* bottom */0);
@@ -394,6 +443,21 @@ public class ViewGroupScrollCaptureTest {

        }

        @Override
        public void onScrollCaptureSearch(Rect localVisibleRect, Point windowOffset,
                Consumer<ScrollCaptureTarget> targets) {
            super.onScrollCaptureSearch(localVisibleRect, windowOffset, targets);
            mOnScrollCaptureSearchLastLocalVisibleRect = new Rect(localVisibleRect);
            mOnScrollCaptureSearchLastWindowOffset = new Point(windowOffset);
        }

        void assertOnScrollCaptureSearchLastArgs(Rect localVisibleRect, Point windowOffset) {
            assertEquals("arg localVisibleRect was incorrect.",
                    localVisibleRect, mOnScrollCaptureSearchLastLocalVisibleRect);
            assertEquals("arg windowOffset was incorrect.",
                    windowOffset, mOnScrollCaptureSearchLastWindowOffset);
        }

        @Override
        public void dispatchScrollCaptureSearch(Rect localVisibleRect, Point windowOffset,
                Consumer<ScrollCaptureTarget> results) {
@@ -437,6 +501,8 @@ public class ViewGroupScrollCaptureTest {

    public static final class MockViewGroup extends ViewGroup {
        private ScrollCaptureCallback mInternalCallback;
        private Rect mOnScrollCaptureSearchLastLocalVisibleRect;
        private Point mOnScrollCaptureSearchLastWindowOffset;

        MockViewGroup(Context context) {
            this(context, /* left */ 0, /* top */0, /* right */ 0, /* bottom */0);
@@ -464,6 +530,21 @@ public class ViewGroupScrollCaptureTest {
            return mInternalCallback;
        }

        @Override
        public void onScrollCaptureSearch(Rect localVisibleRect, Point windowOffset,
                Consumer<ScrollCaptureTarget> targets) {
            super.onScrollCaptureSearch(localVisibleRect, windowOffset, targets);
            mOnScrollCaptureSearchLastLocalVisibleRect = new Rect(localVisibleRect);
            mOnScrollCaptureSearchLastWindowOffset = new Point(windowOffset);
        }

        void assertOnScrollCaptureSearchLastArgs(Rect localVisibleRect, Point windowOffset) {
            assertEquals("arg localVisibleRect was incorrect.",
                    localVisibleRect, mOnScrollCaptureSearchLastLocalVisibleRect);
            assertEquals("arg windowOffset was incorrect.",
                    windowOffset, mOnScrollCaptureSearchLastWindowOffset);
        }

        @Override
        protected void onLayout(boolean changed, int l, int t, int r, int b) {
            // We don't layout this view.