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

Commit 0adfbd33 authored by Phil Weaver's avatar Phil Weaver
Browse files

Use accessibility action for touch exploration

Explore-By-Touch has been dispatching touch events to the screen
rather than using the accessibility API. This was intended as a
workaround for apps that did not properly handle accessibility,
but the workaround itself has been causing bugs in corner cases
where properly accessible Views are partially covered by windows.

This CL first tries to dispatch a click action, and falls back on
the touch dispatch only if the click action fails.

Bug: 35200501
Bug: 26216304
Bug: 20665958
Bug: 34949365
Bug: 34844480
Bug: 29535082

Test: Poking around with first party apps and TalkBack works fine.
This behavior isn't covered by automated testing.

Change-Id: I9cc18399d8f40f7381dfcbef91b5991b711bb7f1
parent d666953f
Loading
Loading
Loading
Loading
+40 −11
Original line number Diff line number Diff line
@@ -912,8 +912,21 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub {
     */
    // TODO: (multi-display) Make sure this works for multiple displays.
    boolean getAccessibilityFocusClickPointInScreen(Point outPoint) {
        return getInteractionBridgeLocked()
                .getAccessibilityFocusClickPointInScreenNotLocked(outPoint);
        return getInteractionBridge().getAccessibilityFocusClickPointInScreenNotLocked(outPoint);
    }

    /**
     * Perform an accessibility action on the view that currently has accessibility focus.
     * Has no effect if no item has accessibility focus, if the item with accessibility
     * focus does not expose the specified action, or if the action fails.
     *
     * @param actionId The id of the action to perform.
     *
     * @return {@code true} if the action was performed. {@code false} if it was not.
     */
    public boolean performActionOnAccessibilityFocusedItem(
            AccessibilityNodeInfo.AccessibilityAction action) {
        return getInteractionBridge().performActionOnAccessibilityFocusedItemNotLocked(action);
    }

    /**
@@ -1029,12 +1042,14 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub {
        onUserStateChangedLocked(userState);
    }

    private InteractionBridge getInteractionBridgeLocked() {
    private InteractionBridge getInteractionBridge() {
        synchronized (mLock) {
            if (mInteractionBridge == null) {
                mInteractionBridge = new InteractionBridge();
            }
            return mInteractionBridge;
        }
    }

    private boolean notifyGestureLocked(int gestureId, boolean isDefault) {
        // TODO: Now we are giving the gestures to the last enabled
@@ -2274,11 +2289,7 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub {

                case MSG_CLEAR_ACCESSIBILITY_FOCUS: {
                    final int windowId = msg.arg1;
                    InteractionBridge bridge;
                    synchronized (mLock) {
                        bridge = getInteractionBridgeLocked();
                    }
                    bridge.clearAccessibilityFocusNotLocked(windowId);
                    getInteractionBridge().clearAccessibilityFocusNotLocked(windowId);
                } break;

                case MSG_SEND_SERVICES_STATE_CHANGED_TO_CLIENTS: {
@@ -4051,6 +4062,24 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub {
            }
        }

        /**
         * Perform an accessibility action on the view that currently has accessibility focus.
         * Has no effect if no item has accessibility focus, if the item with accessibility
         * focus does not expose the specified action, or if the action fails.
         *
         * @param actionId The id of the action to perform.
         *
         * @return {@code true} if the action was performed. {@code false} if it was not.
         */
        public boolean performActionOnAccessibilityFocusedItemNotLocked(
                AccessibilityNodeInfo.AccessibilityAction action) {
            AccessibilityNodeInfo focus = getAccessibilityFocusNotLocked();
            if ((focus == null) || !focus.getActionList().contains(action)) {
                return false;
            }
            return focus.performAction(action.getId());
        }

        public boolean getAccessibilityFocusClickPointInScreenNotLocked(Point outPoint) {
            AccessibilityNodeInfo focus = getAccessibilityFocusNotLocked();
            if (focus == null) {
+8 −0
Original line number Diff line number Diff line
@@ -29,6 +29,7 @@ import android.view.ViewConfiguration;
import android.view.WindowManagerPolicy;
import android.view.accessibility.AccessibilityEvent;
import android.view.accessibility.AccessibilityManager;
import android.view.accessibility.AccessibilityNodeInfo;

import java.util.ArrayList;
import java.util.Arrays;
@@ -407,6 +408,13 @@ class TouchExplorer implements EventStreamTransformation, AccessibilityGestureDe
            mSendTouchInteractionEndDelayed.forceSendAndRemove();
        }

        // Try to use the standard accessibility API to click
        if (mAms.performActionOnAccessibilityFocusedItem(
                AccessibilityNodeInfo.AccessibilityAction.ACTION_CLICK)) {
            return true;
        }
        Slog.e(LOG_TAG, "ACTION_CLICK failed. Dispatching motion events to simulate click.");

        final int pointerIndex = event.getActionIndex();
        final int pointerId = event.getPointerId(pointerIndex);