diff options
| author | Ameer Armaly <aarmaly@google.com> | 2020-06-01 18:12:54 -0700 |
|---|---|---|
| committer | Ameer Armaly <aarmaly@google.com> | 2020-06-22 13:58:51 -0700 |
| commit | 9e83d078248c8a6a7aa0cefb4457234a244a986c (patch) | |
| tree | 17549f52bbe89de319ed69702b8a82b3a173e02e /core/java | |
| parent | 3d4bab487086fb389ee4130f3894393c8d27de7a (diff) | |
[DO NOT MERGE] Bring back touch events for double tap and double tap and hold.
Bug: 159168795
Test: atest TouchExplorerTest
Change-Id: I427b98c71ce8a2ac5b9285b2f34c1864f48c4a32
Diffstat (limited to 'core/java')
| -rw-r--r-- | core/java/android/view/MotionEvent.java | 29 | ||||
| -rw-r--r-- | core/java/android/view/View.java | 8 | ||||
| -rw-r--r-- | core/java/android/view/ViewGroup.java | 81 |
3 files changed, 114 insertions, 4 deletions
diff --git a/core/java/android/view/MotionEvent.java b/core/java/android/view/MotionEvent.java index 19eff72ca814..51b0c6b59f3c 100644 --- a/core/java/android/view/MotionEvent.java +++ b/core/java/android/view/MotionEvent.java @@ -487,6 +487,21 @@ public final class MotionEvent extends InputEvent implements Parcelable { public static final int FLAG_TAINTED = 0x80000000; /** + * Private flag indicating that this event was synthesized by the system and should be delivered + * to the accessibility focused view first. When being dispatched such an event is not handled + * by predecessors of the accessibility focused view and after the event reaches that view the + * flag is cleared and normal event dispatch is performed. This ensures that the platform can + * click on any view that has accessibility focus which is semantically equivalent to asking the + * view to perform a click accessibility action but more generic as views not implementing click + * action correctly can still be activated. + * + * @hide + * @see #isTargetAccessibilityFocus() + * @see #setTargetAccessibilityFocus(boolean) + */ + public static final int FLAG_TARGET_ACCESSIBILITY_FOCUS = 0x40000000; + + /** * Flag indicating the motion event intersected the top edge of the screen. */ public static final int EDGE_TOP = 0x00000001; @@ -2140,6 +2155,20 @@ public final class MotionEvent extends InputEvent implements Parcelable { } /** @hide */ + public boolean isTargetAccessibilityFocus() { + final int flags = getFlags(); + return (flags & FLAG_TARGET_ACCESSIBILITY_FOCUS) != 0; + } + + /** @hide */ + public void setTargetAccessibilityFocus(boolean targetsFocus) { + final int flags = getFlags(); + nativeSetFlags(mNativePtr, targetsFocus + ? flags | FLAG_TARGET_ACCESSIBILITY_FOCUS + : flags & ~FLAG_TARGET_ACCESSIBILITY_FOCUS); + } + + /** @hide */ public final boolean isHoverExitPending() { final int flags = getFlags(); return (flags & FLAG_HOVER_EXIT_PENDING) != 0; diff --git a/core/java/android/view/View.java b/core/java/android/view/View.java index 1226202dfdf9..df1c672eb9eb 100644 --- a/core/java/android/view/View.java +++ b/core/java/android/view/View.java @@ -14274,6 +14274,14 @@ public class View implements Drawable.Callback, KeyEvent.Callback, */ public boolean dispatchTouchEvent(MotionEvent event) { // If the event should be handled by accessibility focus first. + if (event.isTargetAccessibilityFocus()) { + // We don't have focus or no virtual descendant has it, do not handle the event. + if (!isAccessibilityFocusedViewOrHost()) { + return false; + } + // We have focus and got the event, then use normal event dispatch. + event.setTargetAccessibilityFocus(false); + } boolean result = false; if (mInputEventConsistencyVerifier != null) { diff --git a/core/java/android/view/ViewGroup.java b/core/java/android/view/ViewGroup.java index e3362aafbcd4..77fedd7c30d4 100644 --- a/core/java/android/view/ViewGroup.java +++ b/core/java/android/view/ViewGroup.java @@ -2048,8 +2048,26 @@ public abstract class ViewGroup extends View implements ViewParent, ViewManager for (int i = childrenCount - 1; i >= 0; i--) { final int childIndex = getAndVerifyPreorderedIndex(childrenCount, i, customOrder); final View child = getAndVerifyPreorderedView(preorderedList, children, childIndex); + View childWithAccessibilityFocus = + event.isTargetAccessibilityFocus() + ? findChildWithAccessibilityFocus() + : null; + if (!child.canReceivePointerEvents() || !isTransformedTouchPointInView(x, y, child, null)) { + + // If there is a view that has accessibility focus we want it + // to get the event first and if not handled we will perform a + // normal dispatch. We may do a double iteration but this is + // safer given the timeframe. + if (childWithAccessibilityFocus != null) { + if (childWithAccessibilityFocus != child) { + continue; + } + childWithAccessibilityFocus = null; + i = childrenCount - 1; + } + event.setTargetAccessibilityFocus(false); continue; } final PointerIcon pointerIcon = @@ -2617,6 +2635,12 @@ public abstract class ViewGroup extends View implements ViewParent, ViewManager mInputEventConsistencyVerifier.onTouchEvent(ev, 1); } + // If the event targets the accessibility focused view and this is it, start + // normal event dispatch. Maybe a descendant is what will handle the click. + if (ev.isTargetAccessibilityFocus() && isAccessibilityFocusedViewOrHost()) { + ev.setTargetAccessibilityFocus(false); + } + boolean handled = false; if (onFilterTouchEventForSecurity(ev)) { final int action = ev.getAction(); @@ -2647,6 +2671,13 @@ public abstract class ViewGroup extends View implements ViewParent, ViewManager // so this view group continues to intercept touches. intercepted = true; } + + // If intercepted, start normal event dispatch. Also if there is already + // a view that is handling the gesture, do normal event dispatch. + if (intercepted || mFirstTouchTarget != null) { + ev.setTargetAccessibilityFocus(false); + } + // Check for cancelation. final boolean canceled = resetCancelNextUpFlag(this) || actionMasked == MotionEvent.ACTION_CANCEL; @@ -2658,6 +2689,14 @@ public abstract class ViewGroup extends View implements ViewParent, ViewManager TouchTarget newTouchTarget = null; boolean alreadyDispatchedToNewTouchTarget = false; if (!canceled && !intercepted) { + // If the event is targeting accessibility focus we give it to the + // view that has accessibility focus and if it does not handle it + // we clear the flag and dispatch the event to all children as usual. + // We are looking up the accessibility focused host to avoid keeping + // state since these events are very rare. + View childWithAccessibilityFocus = ev.isTargetAccessibilityFocus() + ? findChildWithAccessibilityFocus() : null; + if (actionMasked == MotionEvent.ACTION_DOWN || (split && actionMasked == MotionEvent.ACTION_POINTER_DOWN) || actionMasked == MotionEvent.ACTION_HOVER_MOVE) { @@ -2720,6 +2759,10 @@ public abstract class ViewGroup extends View implements ViewParent, ViewManager alreadyDispatchedToNewTouchTarget = true; break; } + + // The accessibility focus didn't handle the event, so clear + // the flag and do a normal dispatch to all children. + ev.setTargetAccessibilityFocus(false); } if (preorderedList != null) preorderedList.clear(); } @@ -2803,6 +2846,34 @@ public abstract class ViewGroup extends View implements ViewParent, ViewManager return buildOrderedChildList(); } + /** + * Finds the child which has accessibility focus. + * + * @return The child that has focus. + */ + private View findChildWithAccessibilityFocus() { + ViewRootImpl viewRoot = getViewRootImpl(); + if (viewRoot == null) { + return null; + } + + View current = viewRoot.getAccessibilityFocusedHost(); + if (current == null) { + return null; + } + + ViewParent parent = current.getParent(); + while (parent instanceof View) { + if (parent == this) { + return current; + } + current = (View) parent; + parent = current.getParent(); + } + + return null; + } + /** * Resets all touch state in preparation for a new cycle. */ @@ -3257,9 +3328,10 @@ public abstract class ViewGroup extends View implements ViewParent, ViewManager break; } default: - throw new IllegalStateException("descendant focusability must be " - + "one of FOCUS_BEFORE_DESCENDANTS, FOCUS_AFTER_DESCENDANTS, FOCUS_BLOCK_DESCENDANTS " - + "but is " + descendantFocusability); + throw new IllegalStateException( + "descendant focusability must be one of FOCUS_BEFORE_DESCENDANTS," + + " FOCUS_AFTER_DESCENDANTS, FOCUS_BLOCK_DESCENDANTS but is " + + descendantFocusability); } if (result && !isLayoutValid() && ((mPrivateFlags & PFLAG_WANTS_FOCUS) == 0)) { mPrivateFlags |= PFLAG_WANTS_FOCUS; @@ -4925,7 +4997,8 @@ public abstract class ViewGroup extends View implements ViewParent, ViewManager if (params == null) { params = generateDefaultLayoutParams(); if (params == null) { - throw new IllegalArgumentException("generateDefaultLayoutParams() cannot return null"); + throw new IllegalArgumentException( + "generateDefaultLayoutParams() cannot return null"); } } addView(child, index, params); |
