From a1ac6a09252a8a615230f08767ab6153bd23a165 Mon Sep 17 00:00:00 2001 From: Svetoslav Date: Fri, 23 Jan 2015 16:06:43 -0800 Subject: [PATCH] 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 --- core/java/android/view/View.java | 17 ----- core/java/android/view/ViewGroup.java | 74 ++----------------- .../android/widget/HorizontalScrollView.java | 12 --- core/java/android/widget/Toolbar.java | 12 --- .../internal/widget/ActionBarContainer.java | 12 --- 5 files changed, 5 insertions(+), 122 deletions(-) diff --git a/core/java/android/view/View.java b/core/java/android/view/View.java index 6a36c269df9ad..cc8a44091fa53 100644 --- a/core/java/android/view/View.java +++ b/core/java/android/view/View.java @@ -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 outRects) { - if (isClickable() || isLongClickable()) { - RectF bounds = new RectF(); - bounds.set(0, 0, getWidth(), getHeight()); - outRects.add(bounds); - } - } - static void offsetRects(List rects, float offsetX, float offsetY) { final int rectCount = rects.size(); for (int i = 0; i < rectCount; i++) { diff --git a/core/java/android/view/ViewGroup.java b/core/java/android/view/ViewGroup.java index a09837d42ea87..0b1a2d4da9261 100644 --- a/core/java/android/view/ViewGroup.java +++ b/core/java/android/view/ViewGroup.java @@ -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 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 outRects) { - int sizeBefore = outRects.size(); - - super.addClickableRectsForAccessibility(outRects); - - // If we added ourselves, then no need to visit children. - if (outRects.size() > sizeBefore) { - return; - } - - Iterator 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); diff --git a/core/java/android/widget/HorizontalScrollView.java b/core/java/android/widget/HorizontalScrollView.java index 371b4804a1de3..1b93b9705135b 100644 --- a/core/java/android/widget/HorizontalScrollView.java +++ b/core/java/android/widget/HorizontalScrollView.java @@ -762,18 +762,6 @@ public class HorizontalScrollView extends FrameLayout { awakenScrollBars(); } - /** - * @hide - */ - @Override - public void addClickableRectsForAccessibility(List 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)) { diff --git a/core/java/android/widget/Toolbar.java b/core/java/android/widget/Toolbar.java index 0f35e0dc09c83..c5325c4d3b534 100644 --- a/core/java/android/widget/Toolbar.java +++ b/core/java/android/widget/Toolbar.java @@ -1097,18 +1097,6 @@ public class Toolbar extends ViewGroup { return true; } - /** - * @hide - */ - @Override - public void addClickableRectsForAccessibility(List 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 */ diff --git a/core/java/com/android/internal/widget/ActionBarContainer.java b/core/java/com/android/internal/widget/ActionBarContainer.java index e8e2c8d7493f8..7937a95b88c84 100644 --- a/core/java/com/android/internal/widget/ActionBarContainer.java +++ b/core/java/com/android/internal/widget/ActionBarContainer.java @@ -227,18 +227,6 @@ public class ActionBarContainer extends FrameLayout { return true; } - /** - * @hide - */ - @Override - public void addClickableRectsForAccessibility(List 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);