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

Commit 3ee2bd7d authored by Dieter Hsu's avatar Dieter Hsu Committed by Android (Google) Code Review
Browse files

Merge "Improve TouchDelegate Accessibility: Explore by Touch handle hover events"

parents e4d87cb7 fa1b8dec
Loading
Loading
Loading
Loading
+1 −0
Original line number Diff line number Diff line
@@ -48377,6 +48377,7 @@ package android.view {
    ctor public TouchDelegate(android.graphics.Rect, android.view.View);
    method public android.view.accessibility.AccessibilityNodeInfo.TouchDelegateInfo getTouchDelegateInfo();
    method public boolean onTouchEvent(android.view.MotionEvent);
    method public boolean onTouchExplorationHoverEvent(android.view.MotionEvent);
    field public static final int ABOVE = 1; // 0x1
    field public static final int BELOW = 2; // 0x2
    field public static final int TO_LEFT = 4; // 0x4
+54 −7
Original line number Diff line number Diff line
@@ -103,13 +103,13 @@ public class TouchDelegate {
    }

    /**
     * Will forward touch events to the delegate view if the event is within the bounds
     * Forward touch events to the delegate view if the event is within the bounds
     * specified in the constructor.
     *
     * @param event The touch event to forward
     * @return True if the event was forwarded to the delegate, false otherwise.
     * @return True if the event was consumed by the delegate, false otherwise.
     */
    public boolean onTouchEvent(MotionEvent event) {
    public boolean onTouchEvent(@NonNull MotionEvent event) {
        int x = (int)event.getX();
        int y = (int)event.getY();
        boolean sendToDelegate = false;
@@ -139,18 +139,65 @@ public class TouchDelegate {
                break;
        }
        if (sendToDelegate) {
            final View delegateView = mDelegateView;

            if (hit) {
                // Offset event coordinates to be inside the target view
                event.setLocation(delegateView.getWidth() / 2, delegateView.getHeight() / 2);
                event.setLocation(mDelegateView.getWidth() / 2, mDelegateView.getHeight() / 2);
            } else {
                // Offset event coordinates to be outside the target view (in case it does
                // something like tracking pressed state)
                int slop = mSlop;
                event.setLocation(-(slop * 2), -(slop * 2));
            }
            handled = delegateView.dispatchTouchEvent(event);
            handled = mDelegateView.dispatchTouchEvent(event);
        }
        return handled;
    }

    /**
     * Forward hover events to the delegate view if the event is within the bounds
     * specified in the constructor and touch exploration is enabled.
     *
     * @param event The hover event to forward
     * @return True if the event was consumed by the delegate, false otherwise.
     *
     * @see android.view.accessibility.AccessibilityManager#isTouchExplorationEnabled
     */
    public boolean onTouchExplorationHoverEvent(@NonNull MotionEvent event) {
        if (mBounds == null) {
            return false;
        }

        final int x = (int) event.getX();
        final int y = (int) event.getY();
        boolean hit = true;
        boolean handled = false;

        final boolean isInbound = mBounds.contains(x, y);
        switch (event.getActionMasked()) {
            case MotionEvent.ACTION_HOVER_ENTER:
                mDelegateTargeted = isInbound;
                break;
            case MotionEvent.ACTION_HOVER_MOVE:
                if (isInbound) {
                    mDelegateTargeted = true;
                } else {
                    // delegated previously
                    if (mDelegateTargeted && !mSlopBounds.contains(x, y)) {
                        hit = false;
                    }
                }
                break;
            case MotionEvent.ACTION_HOVER_EXIT:
                mDelegateTargeted = true;
                break;
        }
        if (mDelegateTargeted) {
            if (hit) {
                event.setLocation(mDelegateView.getWidth() / 2, mDelegateView.getHeight() / 2);
            } else {
                mDelegateTargeted = false;
            }
            handled = mDelegateView.dispatchHoverEvent(event);
        }
        return handled;
    }
+21 −1
Original line number Diff line number Diff line
@@ -12829,6 +12829,15 @@ public class View implements Drawable.Callback, KeyEvent.Callback,
        return false;
    }
    /**
     * Returns true if the given point, in local coordinates, is inside the hovered child.
     *
     * @hide
     */
    protected boolean pointInHoveredChild(MotionEvent event) {
        return false;
    }
    /**
     * Dispatch a generic motion event to the view under the first pointer.
     * <p>
@@ -13584,6 +13593,17 @@ public class View implements Drawable.Callback, KeyEvent.Callback,
     * @see #onHoverChanged
     */
    public boolean onHoverEvent(MotionEvent event) {
        // Explore by touch should dispatch events to children under pointer first if any before
        // dispatching to TouchDelegate. For children non-hoverable that will not consume events,
        // it should also not delegate when they got the pointer hovered.
        if (mTouchDelegate != null && !pointInHoveredChild(event)) {
            final AccessibilityManager manager = AccessibilityManager.getInstance(mContext);
            if (manager.isEnabled() && manager.isTouchExplorationEnabled()
                    && mTouchDelegate.onTouchExplorationHoverEvent(event)) {
                return true;
            }
        }
        // The root view may receive hover (or touch) events that are outside the bounds of
        // the window.  This code ensures that we only send accessibility events for
        // hovers that are actually within the bounds of the root view.
@@ -13598,7 +13618,7 @@ public class View implements Drawable.Callback, KeyEvent.Callback,
            }
        } else {
            if (action == MotionEvent.ACTION_HOVER_EXIT
                    || (action == MotionEvent.ACTION_MOVE
                    || (action == MotionEvent.ACTION_HOVER_MOVE
                            && !pointInView(event.getX(), event.getY()))) {
                mSendingHoverAccessibilityEvents = false;
                sendAccessibilityHoverEvent(AccessibilityEvent.TYPE_VIEW_HOVER_EXIT);
+10 −0
Original line number Diff line number Diff line
@@ -2383,6 +2383,16 @@ public abstract class ViewGroup extends View implements ViewParent, ViewManager
        return mFirstHoverTarget != null;
    }

    /** @hide */
    @Override
    protected boolean pointInHoveredChild(MotionEvent event) {
        if (mFirstHoverTarget != null) {
            return isTransformedTouchPointInView(event.getX(), event.getY(),
                mFirstHoverTarget.child, null);
        }
        return false;
    }

    @Override
    public void addChildrenForAccessibility(ArrayList<View> outChildren) {
        if (getAccessibilityNodeProvider() != null) {