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

Commit a1ac6a09 authored by Svetoslav's avatar Svetoslav
Browse files

Accessibility: Ignore overlapping siblings when computing a click location

To click a view we were computing a click location by ignoring overlapping
views that are actionable. However, detection whether a view is actionable
is not always possible as the view may handle touch events directly. This
leads to unhandled edge cases. We are taking a conservative approach and
ignore all overlapping siblings regardless if clickable. This is also has
limitations but hopefully less frequent edge cases.

bug:18889611

Change-Id: Icea0b7b3e2d4ed53e50e01cb6a99b880be560b14
parent 10a053e4
Loading
Loading
Loading
Loading
+0 −17
Original line number Diff line number Diff line
@@ -5932,23 +5932,6 @@ public class View implements Drawable.Callback, KeyEvent.Callback,
        return true;
    }
    /**
     * Adds the clickable rectangles withing the bounds of this view. They
     * may overlap. This method is intended for use only by the accessibility
     * layer.
     *
     * @param outRects List to which to add clickable areas.
     *
     * @hide
     */
    public void addClickableRectsForAccessibility(List<RectF> outRects) {
        if (isClickable() || isLongClickable()) {
            RectF bounds = new RectF();
            bounds.set(0, 0, getWidth(), getHeight());
            outRects.add(bounds);
        }
    }
    static void offsetRects(List<RectF> rects, float offsetX, float offsetY) {
        final int rectCount = rects.size();
        for (int i = 0; i < rectCount; i++) {
+5 −69
Original line number Diff line number Diff line
@@ -855,27 +855,11 @@ public abstract class ViewGroup extends View implements ViewParent, ViewManager

            // Compute the intersection between the child and the sibling.
            if (siblingBounds.intersect(bounds)) {
                List<RectF> clickableRects = new ArrayList<>();
                sibling.addClickableRectsForAccessibility(clickableRects);

                final int clickableRectCount = clickableRects.size();
                for (int j = 0; j < clickableRectCount; j++) {
                    RectF clickableRect = clickableRects.get(j);

                    // Translate the clickable rect to our coordinates.
                    offsetChildRectToMyCoords(clickableRect, sibling);

                    // Compute the intersection between the child and the clickable rects.
                    if (clickableRect.intersect(bounds)) {
                        // If a clickable rect completely covers the child, done.
                        if (clickableRect.equals(bounds)) {
                            releaseOrderedChildIterator();
                            return false;
                        }
                        // Keep track of the intersection rectangle.
                        intersections.add(clickableRect);
                    }
                }
                // Conservatively we consider an overlapping sibling to be
                // interactive and ignore it. This is not ideal as if the
                // sibling completely covers the view despite handling no
                // touch events we will not be able to click on the view.
                intersections.add(siblingBounds);
            }
        }

@@ -890,54 +874,6 @@ public abstract class ViewGroup extends View implements ViewParent, ViewManager
        return true;
    }

    /**
     * @hide
     */
    @Override
    public void addClickableRectsForAccessibility(List<RectF> outRects) {
        int sizeBefore = outRects.size();

        super.addClickableRectsForAccessibility(outRects);

        // If we added ourselves, then no need to visit children.
        if (outRects.size() > sizeBefore) {
            return;
        }

        Iterator<View> iterator = obtainOrderedChildIterator();
        while (iterator.hasNext()) {
            View child = iterator.next();

            // Cannot click on an invisible view.
            if (!isVisible(child)) {
                continue;
            }

            sizeBefore = outRects.size();

            // Add clickable rects in the child bounds.
            child.addClickableRectsForAccessibility(outRects);

            // Offset the clickable rects for out children to our coordinates.
            final int sizeAfter = outRects.size();
            for (int j = sizeBefore; j < sizeAfter; j++) {
                RectF rect = outRects.get(j);

                // Translate the clickable rect to our coordinates.
                offsetChildRectToMyCoords(rect, child);

                // If a clickable rect fills the parent, done.
                if ((int) rect.left == 0 && (int) rect.top == 0
                        && (int) rect.right == mRight && (int) rect.bottom == mBottom) {
                    releaseOrderedChildIterator();
                    return;
                }
            }
        }

        releaseOrderedChildIterator();
    }

    private void offsetChildRectToMyCoords(RectF rect, View child) {
        if (!child.hasIdentityMatrix()) {
            child.getMatrix().mapRect(rect);
+0 −12
Original line number Diff line number Diff line
@@ -762,18 +762,6 @@ public class HorizontalScrollView extends FrameLayout {
        awakenScrollBars();
    }

    /**
     * @hide
     */
    @Override
    public void addClickableRectsForAccessibility(List<RectF> outRects) {
        // This class always consumes touch events, therefore if it
        // covers a view we do not want to send a click over it.
        RectF bounds = new RectF();
        bounds.set(0, 0, getWidth(), getHeight());
        outRects.add(bounds);
    }

    @Override
    public boolean performAccessibilityAction(int action, Bundle arguments) {
        if (super.performAccessibilityAction(action, arguments)) {
+0 −12
Original line number Diff line number Diff line
@@ -1097,18 +1097,6 @@ public class Toolbar extends ViewGroup {
        return true;
    }

    /**
     * @hide
     */
    @Override
    public void addClickableRectsForAccessibility(List<RectF> outRects) {
        // This class always consumes touch events, therefore if it
        // covers a view we do not want to send a click over it.
        RectF bounds = new RectF();
        bounds.set(0, 0, getWidth(), getHeight());
        outRects.add(bounds);
    }

    /**
     * @hide
     */
+0 −12
Original line number Diff line number Diff line
@@ -227,18 +227,6 @@ public class ActionBarContainer extends FrameLayout {
        return true;
    }

    /**
     * @hide
     */
    @Override
    public void addClickableRectsForAccessibility(List<RectF> outRects) {
        // This class always consumes touch events, therefore if it
        // covers a view we do not want to send a click over it.
        RectF bounds = new RectF();
        bounds.set(0, 0, getWidth(), getHeight());
        outRects.add(bounds);
    }

    @Override
    public boolean onHoverEvent(MotionEvent ev) {
        super.onHoverEvent(ev);