diff options
3 files changed, 395 insertions, 325 deletions
diff --git a/services/accessibility/java/com/android/server/accessibility/gestures/EventDispatcher.java b/services/accessibility/java/com/android/server/accessibility/gestures/EventDispatcher.java new file mode 100644 index 000000000000..dc7a9aaf966d --- /dev/null +++ b/services/accessibility/java/com/android/server/accessibility/gestures/EventDispatcher.java @@ -0,0 +1,309 @@ +/* + * Copyright (C) 2019 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.server.accessibility.gestures; + +import static com.android.server.accessibility.gestures.TouchExplorer.DEBUG; +import static com.android.server.accessibility.gestures.TouchState.ALL_POINTER_ID_BITS; +import static com.android.server.accessibility.gestures.TouchState.MAX_POINTER_COUNT; + +import android.content.Context; +import android.util.Slog; +import android.view.MotionEvent; +import android.view.accessibility.AccessibilityEvent; +import android.view.accessibility.AccessibilityManager; + +import com.android.server.accessibility.AccessibilityManagerService; +import com.android.server.accessibility.EventStreamTransformation; +import com.android.server.policy.WindowManagerPolicy; + +/** + * This class dispatches motion events and accessibility events relating to touch exploration and + * gesture dispatch. TouchExplorer is responsible for insuring that the receiver of motion events is + * set correctly so that events go to the right place. + */ +class EventDispatcher { + private static final String LOG_TAG = "EventDispatcher"; + + private final AccessibilityManagerService mAms; + private Context mContext; + // The receiver of motion events. + private EventStreamTransformation mReceiver; + // Keep track of which pointers sent to the system are down. + private int mInjectedPointersDown; + + // The time of the last injected down. + private long mLastInjectedDownEventTime; + + // The last injected hover event. + private MotionEvent mLastInjectedHoverEvent; + private TouchState mState; + + EventDispatcher( + Context context, + AccessibilityManagerService ams, + EventStreamTransformation receiver, + TouchState state) { + mContext = context; + mAms = ams; + mReceiver = receiver; + mState = state; + } + + public void setReceiver(EventStreamTransformation receiver) { + mReceiver = receiver; + } + + /** + * Sends an event. + * + * @param prototype The prototype from which to create the injected events. + * @param action The action of the event. + * @param pointerIdBits The bits of the pointers to send. + * @param policyFlags The policy flags associated with the event. + */ + void sendMotionEvent(MotionEvent prototype, int action, int pointerIdBits, int policyFlags) { + prototype.setAction(action); + + MotionEvent event = null; + if (pointerIdBits == ALL_POINTER_ID_BITS) { + event = prototype; + } else { + try { + event = prototype.split(pointerIdBits); + } catch (IllegalArgumentException e) { + Slog.e(LOG_TAG, "sendMotionEvent: Failed to split motion event: " + e); + return; + } + } + if (action == MotionEvent.ACTION_DOWN) { + event.setDownTime(event.getEventTime()); + } else { + event.setDownTime(getLastInjectedDownEventTime()); + } + if (DEBUG) { + Slog.d( + LOG_TAG, + "Injecting event: " + + event + + ", policyFlags=0x" + + Integer.toHexString(policyFlags)); + } + + // Make sure that the user will see the event. + policyFlags |= WindowManagerPolicy.FLAG_PASS_TO_USER; + // TODO: For now pass null for the raw event since the touch + // explorer is the last event transformation and it does + // not care about the raw event. + if (mReceiver != null) { + mReceiver.onMotionEvent(event, null, policyFlags); + } else { + Slog.e(LOG_TAG, "Error sending event: no receiver specified."); + } + updateState(event); + + if (event != prototype) { + event.recycle(); + } + } + + /** + * Sends an accessibility event of the given type. + * + * @param type The event type. + */ + void sendAccessibilityEvent(int type) { + AccessibilityManager accessibilityManager = AccessibilityManager.getInstance(mContext); + if (accessibilityManager.isEnabled()) { + AccessibilityEvent event = AccessibilityEvent.obtain(type); + event.setWindowId(mAms.getActiveWindowId()); + accessibilityManager.sendAccessibilityEvent(event); + if (DEBUG) { + Slog.d( + LOG_TAG, + "Sending accessibility event" + AccessibilityEvent.eventTypeToString(type)); + } + } + // Todo: get rid of this and have TouchState control the sending of events rather than react + // to it. + mState.onInjectedAccessibilityEvent(type); + } + + /** + * Processes an injected {@link MotionEvent} event. + * + * @param event The event to process. + */ + void updateState(MotionEvent event) { + final int action = event.getActionMasked(); + final int pointerId = event.getPointerId(event.getActionIndex()); + final int pointerFlag = (1 << pointerId); + switch (action) { + case MotionEvent.ACTION_DOWN: + case MotionEvent.ACTION_POINTER_DOWN: + mInjectedPointersDown |= pointerFlag; + mLastInjectedDownEventTime = event.getDownTime(); + break; + case MotionEvent.ACTION_UP: + case MotionEvent.ACTION_POINTER_UP: + mInjectedPointersDown &= ~pointerFlag; + if (mInjectedPointersDown == 0) { + mLastInjectedDownEventTime = 0; + } + break; + case MotionEvent.ACTION_HOVER_ENTER: + case MotionEvent.ACTION_HOVER_MOVE: + case MotionEvent.ACTION_HOVER_EXIT: + if (mLastInjectedHoverEvent != null) { + mLastInjectedHoverEvent.recycle(); + } + mLastInjectedHoverEvent = MotionEvent.obtain(event); + break; + } + if (DEBUG) { + Slog.i(LOG_TAG, "Injected pointer:\n" + toString()); + } + } + + /** Clears the internals state. */ + public void clear() { + mInjectedPointersDown = 0; + } + + /** @return The time of the last injected down event. */ + public long getLastInjectedDownEventTime() { + return mLastInjectedDownEventTime; + } + + /** @return The number of down pointers injected to the view hierarchy. */ + public int getInjectedPointerDownCount() { + return Integer.bitCount(mInjectedPointersDown); + } + + /** @return The bits of the injected pointers that are down. */ + public int getInjectedPointersDown() { + return mInjectedPointersDown; + } + + /** + * Whether an injected pointer is down. + * + * @param pointerId The unique pointer id. + * @return True if the pointer is down. + */ + public boolean isInjectedPointerDown(int pointerId) { + final int pointerFlag = (1 << pointerId); + return (mInjectedPointersDown & pointerFlag) != 0; + } + + /** @return The the last injected hover event. */ + public MotionEvent getLastInjectedHoverEvent() { + return mLastInjectedHoverEvent; + } + + @Override + public String toString() { + StringBuilder builder = new StringBuilder(); + builder.append("========================="); + builder.append("\nDown pointers #"); + builder.append(Integer.bitCount(mInjectedPointersDown)); + builder.append(" [ "); + for (int i = 0; i < MAX_POINTER_COUNT; i++) { + if ((mInjectedPointersDown & i) != 0) { + builder.append(i); + builder.append(" "); + } + } + builder.append("]"); + builder.append("\n========================="); + return builder.toString(); + } + + /** + * Computes the action for an injected event based on a masked action and a pointer index. + * + * @param actionMasked The masked action. + * @param pointerIndex The index of the pointer which has changed. + * @return The action to be used for injection. + */ + private int computeInjectionAction(int actionMasked, int pointerIndex) { + switch (actionMasked) { + case MotionEvent.ACTION_DOWN: + case MotionEvent.ACTION_POINTER_DOWN: + // Compute the action based on how many down pointers are injected. + if (getInjectedPointerDownCount() == 0) { + return MotionEvent.ACTION_DOWN; + } else { + return (pointerIndex << MotionEvent.ACTION_POINTER_INDEX_SHIFT) + | MotionEvent.ACTION_POINTER_DOWN; + } + case MotionEvent.ACTION_POINTER_UP: + // Compute the action based on how many down pointers are injected. + if (getInjectedPointerDownCount() == 1) { + return MotionEvent.ACTION_UP; + } else { + return (pointerIndex << MotionEvent.ACTION_POINTER_INDEX_SHIFT) + | MotionEvent.ACTION_POINTER_UP; + } + default: + return actionMasked; + } + } + /** + * Sends down events to the view hierarchy for all pointers which are not already being + * delivered i.e. pointers that are not yet injected. + * + * @param prototype The prototype from which to create the injected events. + * @param policyFlags The policy flags associated with the event. + */ + void sendDownForAllNotInjectedPointers(MotionEvent prototype, int policyFlags) { + + // Inject the injected pointers. + int pointerIdBits = 0; + final int pointerCount = prototype.getPointerCount(); + for (int i = 0; i < pointerCount; i++) { + final int pointerId = prototype.getPointerId(i); + // Do not send event for already delivered pointers. + if (!isInjectedPointerDown(pointerId)) { + pointerIdBits |= (1 << pointerId); + final int action = computeInjectionAction(MotionEvent.ACTION_DOWN, i); + sendMotionEvent(prototype, action, pointerIdBits, policyFlags); + } + } + } + + /** + * Sends up events to the view hierarchy for all pointers which are already being delivered i.e. + * pointers that are injected. + * + * @param prototype The prototype from which to create the injected events. + * @param policyFlags The policy flags associated with the event. + */ + void sendUpForInjectedDownPointers(MotionEvent prototype, int policyFlags) { + int pointerIdBits = 0; + final int pointerCount = prototype.getPointerCount(); + for (int i = 0; i < pointerCount; i++) { + final int pointerId = prototype.getPointerId(i); + // Skip non injected down pointers. + if (!isInjectedPointerDown(pointerId)) { + continue; + } + pointerIdBits |= (1 << pointerId); + final int action = computeInjectionAction(MotionEvent.ACTION_UP, i); + sendMotionEvent(prototype, action, pointerIdBits, policyFlags); + } + } +} diff --git a/services/accessibility/java/com/android/server/accessibility/gestures/TouchExplorer.java b/services/accessibility/java/com/android/server/accessibility/gestures/TouchExplorer.java index 7044c4d28bdf..f4ac82157d04 100644 --- a/services/accessibility/java/com/android/server/accessibility/gestures/TouchExplorer.java +++ b/services/accessibility/java/com/android/server/accessibility/gestures/TouchExplorer.java @@ -29,11 +29,11 @@ import android.view.InputDevice; import android.view.MotionEvent; import android.view.ViewConfiguration; import android.view.accessibility.AccessibilityEvent; -import android.view.accessibility.AccessibilityManager; import android.view.accessibility.AccessibilityNodeInfo; import com.android.server.accessibility.AccessibilityManagerService; import com.android.server.accessibility.BaseEventStreamTransformation; +import com.android.server.accessibility.EventStreamTransformation; import com.android.server.policy.WindowManagerPolicy; import java.util.ArrayList; @@ -61,7 +61,7 @@ import java.util.List; public class TouchExplorer extends BaseEventStreamTransformation implements AccessibilityGestureDetector.Listener { - private static final boolean DEBUG = false; + static final boolean DEBUG = false; // Tag for logging received events. private static final String LOG_TAG = "TouchExplorer"; @@ -109,8 +109,7 @@ public class TouchExplorer extends BaseEventStreamTransformation // Helper class to track received pointers. private final TouchState.ReceivedPointerTracker mReceivedPointerTracker; - // Helper class to track injected pointers. - private final TouchState.InjectedPointerTracker mInjectedPointerTracker; + private final EventDispatcher mDispatcher; // Handle to the accessibility manager service. private final AccessibilityManagerService mAms; @@ -148,7 +147,7 @@ public class TouchExplorer extends BaseEventStreamTransformation mAms = service; mState = new TouchState(); mReceivedPointerTracker = mState.getReceivedPointerTracker(); - mInjectedPointerTracker = mState.getInjectedPointerTracker(); + mDispatcher = new EventDispatcher(context, mAms, super.getNext(), mState); mDetermineUserIntentTimeout = ViewConfiguration.getDoubleTapTimeout(); mDoubleTapSlop = ViewConfiguration.get(context).getScaledDoubleTapSlop(); mHandler = new Handler(context.getMainLooper()); @@ -197,10 +196,10 @@ public class TouchExplorer extends BaseEventStreamTransformation } else if (mState.isDragging()) { mDraggingPointerId = INVALID_POINTER_ID; // Send exit to all pointers that we have delivered. - sendUpForInjectedDownPointers(event, policyFlags); + mDispatcher.sendUpForInjectedDownPointers(event, policyFlags); } else if (mState.isDelegating()) { // Send exit to all pointers that we have delivered. - sendUpForInjectedDownPointers(event, policyFlags); + mDispatcher.sendUpForInjectedDownPointers(event, policyFlags); } else if (mState.isGestureDetecting()) { // No state specific cleanup required. } @@ -271,7 +270,8 @@ public class TouchExplorer extends BaseEventStreamTransformation if (mSendTouchExplorationEndDelayed.isPending() && eventType == AccessibilityEvent.TYPE_VIEW_HOVER_EXIT) { mSendTouchExplorationEndDelayed.cancel(); - sendAccessibilityEvent(AccessibilityEvent.TYPE_TOUCH_EXPLORATION_GESTURE_END); + mDispatcher.sendAccessibilityEvent( + AccessibilityEvent.TYPE_TOUCH_EXPLORATION_GESTURE_END); } // The event for touch interaction end should be strictly after the @@ -279,7 +279,7 @@ public class TouchExplorer extends BaseEventStreamTransformation if (mSendTouchInteractionEndDelayed.isPending() && eventType == AccessibilityEvent.TYPE_VIEW_HOVER_EXIT) { mSendTouchInteractionEndDelayed.cancel(); - sendAccessibilityEvent(AccessibilityEvent.TYPE_TOUCH_INTERACTION_END); + mDispatcher.sendAccessibilityEvent(AccessibilityEvent.TYPE_TOUCH_INTERACTION_END); } super.onAccessibilityEvent(event); } @@ -318,7 +318,7 @@ public class TouchExplorer extends BaseEventStreamTransformation } // Announce the end of a new touch interaction. - sendAccessibilityEvent(AccessibilityEvent.TYPE_TOUCH_INTERACTION_END); + mDispatcher.sendAccessibilityEvent(AccessibilityEvent.TYPE_TOUCH_INTERACTION_END); // Try to use the standard accessibility API to click if (!mAms.performActionOnAccessibilityFocusedItem( @@ -338,7 +338,7 @@ public class TouchExplorer extends BaseEventStreamTransformation mExitGestureDetectionModeDelayed.post(); // Send accessibility event to announce the start // of gesture recognition. - sendAccessibilityEvent(AccessibilityEvent.TYPE_GESTURE_DETECTION_START); + mDispatcher.sendAccessibilityEvent(AccessibilityEvent.TYPE_GESTURE_DETECTION_START); return false; } @@ -371,7 +371,8 @@ public class TouchExplorer extends BaseEventStreamTransformation mSendHoverEnterAndMoveDelayed.addEvent(event); mSendHoverEnterAndMoveDelayed.forceSendAndRemove(); mSendHoverExitDelayed.cancel(); - sendMotionEvent(event, MotionEvent.ACTION_HOVER_MOVE, pointerIdBits, policyFlags); + mDispatcher.sendMotionEvent( + event, MotionEvent.ACTION_HOVER_MOVE, pointerIdBits, policyFlags); return true; } } @@ -417,7 +418,7 @@ public class TouchExplorer extends BaseEventStreamTransformation if (!mGestureDetector.firstTapDetected() && mState.isClear()) { mSendTouchExplorationEndDelayed.forceSendAndRemove(); mSendTouchInteractionEndDelayed.forceSendAndRemove(); - sendAccessibilityEvent(AccessibilityEvent.TYPE_TOUCH_INTERACTION_START); + mDispatcher.sendAccessibilityEvent(AccessibilityEvent.TYPE_TOUCH_INTERACTION_START); } else { // Let gesture to handle to avoid duplicated TYPE_TOUCH_INTERACTION_END event. mSendTouchInteractionEndDelayed.cancel(); @@ -536,18 +537,19 @@ public class TouchExplorer extends BaseEventStreamTransformation mState.startDragging(); mDraggingPointerId = pointerId; event.setEdgeFlags(mReceivedPointerTracker.getLastReceivedDownEdgeFlags()); - sendMotionEvent(event, MotionEvent.ACTION_DOWN, pointerIdBits, policyFlags); + mDispatcher.sendMotionEvent( + event, MotionEvent.ACTION_DOWN, pointerIdBits, policyFlags); } else { // Two pointers moving arbitrary are delegated to the view hierarchy. mState.startDelegating(); - sendDownForAllNotInjectedPointers(event, policyFlags); + mDispatcher.sendDownForAllNotInjectedPointers(event, policyFlags); } break; default: // More than two pointers are delegated to the view hierarchy. mState.startDelegating(); event = MotionEvent.obtainNoHistory(event); - sendDownForAllNotInjectedPointers(event, policyFlags); + mDispatcher.sendDownForAllNotInjectedPointers(event, policyFlags); break; } } @@ -585,7 +587,8 @@ public class TouchExplorer extends BaseEventStreamTransformation case 1: // Touch exploration. sendTouchExplorationGestureStartAndHoverEnterIfNeeded(policyFlags); - sendMotionEvent(event, MotionEvent.ACTION_HOVER_MOVE, pointerIdBits, policyFlags); + mDispatcher.sendMotionEvent( + event, MotionEvent.ACTION_HOVER_MOVE, pointerIdBits, policyFlags); break; case 2: if (mSendHoverEnterAndMoveDelayed.isPending()) { @@ -658,9 +661,10 @@ public class TouchExplorer extends BaseEventStreamTransformation // goes down => delegate the three pointers to the view hierarchy mState.startDelegating(); if (mDraggingPointerId != INVALID_POINTER_ID) { - sendMotionEvent(event, MotionEvent.ACTION_UP, pointerIdBits, policyFlags); + mDispatcher.sendMotionEvent( + event, MotionEvent.ACTION_UP, pointerIdBits, policyFlags); } - sendDownForAllNotInjectedPointers(event, policyFlags); + mDispatcher.sendDownForAllNotInjectedPointers(event, policyFlags); } break; case MotionEvent.ACTION_MOVE: { if (mDraggingPointerId == INVALID_POINTER_ID) { @@ -672,21 +676,12 @@ public class TouchExplorer extends BaseEventStreamTransformation } break; case 2: { if (isDraggingGesture(event)) { - // Adjust event location to the middle location of the two pointers. - final float firstPtrX = event.getX(0); - final float firstPtrY = event.getY(0); - final float secondPtrX = event.getX(1); - final float secondPtrY = event.getY(1); - final int pointerIndex = event.findPointerIndex(mDraggingPointerId); - final float deltaX = - (pointerIndex == 0) ? (secondPtrX - firstPtrX) - : (firstPtrX - secondPtrX); - final float deltaY = - (pointerIndex == 0) ? (secondPtrY - firstPtrY) - : (firstPtrY - secondPtrY); - event.offsetLocation(deltaX / 2, deltaY / 2); // If still dragging send a drag event. - sendMotionEvent(event, MotionEvent.ACTION_MOVE, pointerIdBits, + adjustEventLocationForDrag(event); + mDispatcher.sendMotionEvent( + event, + MotionEvent.ACTION_MOVE, + pointerIdBits, policyFlags); } else { // The two pointers are moving either in different directions or @@ -695,20 +690,20 @@ public class TouchExplorer extends BaseEventStreamTransformation // Remove move history before send injected non-move events event = MotionEvent.obtainNoHistory(event); // Send an event to the end of the drag gesture. - sendMotionEvent(event, MotionEvent.ACTION_UP, pointerIdBits, + mDispatcher.sendMotionEvent(event, MotionEvent.ACTION_UP, pointerIdBits, policyFlags); // Deliver all pointers to the view hierarchy. - sendDownForAllNotInjectedPointers(event, policyFlags); + mDispatcher.sendDownForAllNotInjectedPointers(event, policyFlags); } } break; default: { mState.startDelegating(); event = MotionEvent.obtainNoHistory(event); // Send an event to the end of the drag gesture. - sendMotionEvent(event, MotionEvent.ACTION_UP, pointerIdBits, + mDispatcher.sendMotionEvent(event, MotionEvent.ACTION_UP, pointerIdBits, policyFlags); // Deliver all pointers to the view hierarchy. - sendDownForAllNotInjectedPointers(event, policyFlags); + mDispatcher.sendDownForAllNotInjectedPointers(event, policyFlags); } } } break; @@ -716,20 +711,22 @@ public class TouchExplorer extends BaseEventStreamTransformation final int pointerId = event.getPointerId(event.getActionIndex()); if (pointerId == mDraggingPointerId) { mDraggingPointerId = INVALID_POINTER_ID; - // Send an event to the end of the drag gesture. - sendMotionEvent(event, MotionEvent.ACTION_UP, pointerIdBits, policyFlags); + // Send an event to the end of the drag gesture. + mDispatcher.sendMotionEvent( + event, MotionEvent.ACTION_UP, pointerIdBits, policyFlags); } } break; case MotionEvent.ACTION_UP: { mAms.onTouchInteractionEnd(); // Announce the end of a new touch interaction. - sendAccessibilityEvent( + mDispatcher.sendAccessibilityEvent( AccessibilityEvent.TYPE_TOUCH_INTERACTION_END); final int pointerId = event.getPointerId(event.getActionIndex()); if (pointerId == mDraggingPointerId) { mDraggingPointerId = INVALID_POINTER_ID; // Send an event to the end of the drag gesture. - sendMotionEvent(event, MotionEvent.ACTION_UP, pointerIdBits, policyFlags); + mDispatcher.sendMotionEvent( + event, MotionEvent.ACTION_UP, pointerIdBits, policyFlags); } } break; } @@ -751,16 +748,18 @@ public class TouchExplorer extends BaseEventStreamTransformation } case MotionEvent.ACTION_UP: { // Deliver the event. - sendMotionEvent(event, event.getAction(), ALL_POINTER_ID_BITS, policyFlags); + mDispatcher.sendMotionEvent( + event, event.getAction(), ALL_POINTER_ID_BITS, policyFlags); // Announce the end of a the touch interaction. mAms.onTouchInteractionEnd(); - sendAccessibilityEvent(AccessibilityEvent.TYPE_TOUCH_INTERACTION_END); + mDispatcher.sendAccessibilityEvent(AccessibilityEvent.TYPE_TOUCH_INTERACTION_END); } break; default: { - // Deliver the event. - sendMotionEvent(event, event.getAction(), ALL_POINTER_ID_BITS, policyFlags); + // Deliver the event. + mDispatcher.sendMotionEvent( + event, event.getAction(), ALL_POINTER_ID_BITS, policyFlags); } } } @@ -769,57 +768,15 @@ public class TouchExplorer extends BaseEventStreamTransformation mAms.onTouchInteractionEnd(); // Announce the end of the gesture recognition. - sendAccessibilityEvent(AccessibilityEvent.TYPE_GESTURE_DETECTION_END); + mDispatcher.sendAccessibilityEvent(AccessibilityEvent.TYPE_GESTURE_DETECTION_END); // Don't announce the end of a the touch interaction if users didn't lift their fingers. if (interactionEnd) { - sendAccessibilityEvent(AccessibilityEvent.TYPE_TOUCH_INTERACTION_END); + mDispatcher.sendAccessibilityEvent(AccessibilityEvent.TYPE_TOUCH_INTERACTION_END); } mExitGestureDetectionModeDelayed.cancel(); } - /** - * Sends an accessibility event of the given type. - * - * @param type The event type. - */ - private void sendAccessibilityEvent(int type) { - AccessibilityManager accessibilityManager = AccessibilityManager.getInstance(mContext); - if (accessibilityManager.isEnabled()) { - AccessibilityEvent event = AccessibilityEvent.obtain(type); - event.setWindowId(mAms.getActiveWindowId()); - accessibilityManager.sendAccessibilityEvent(event); - if (DEBUG) { - Slog.d( - LOG_TAG, - "Sending accessibility event" + AccessibilityEvent.eventTypeToString(type)); - } - } - mState.onInjectedAccessibilityEvent(type); - } - - /** - * Sends down events to the view hierarchy for all pointers which are - * not already being delivered i.e. pointers that are not yet injected. - * - * @param prototype The prototype from which to create the injected events. - * @param policyFlags The policy flags associated with the event. - */ - private void sendDownForAllNotInjectedPointers(MotionEvent prototype, int policyFlags) { - - // Inject the injected pointers. - int pointerIdBits = 0; - final int pointerCount = prototype.getPointerCount(); - for (int i = 0; i < pointerCount; i++) { - final int pointerId = prototype.getPointerId(i); - // Do not send event for already delivered pointers. - if (!mInjectedPointerTracker.isInjectedPointerDown(pointerId)) { - pointerIdBits |= (1 << pointerId); - final int action = computeInjectionAction(MotionEvent.ACTION_DOWN, i); - sendMotionEvent(prototype, action, pointerIdBits, policyFlags); - } - } - } /** * Sends the exit events if needed. Such events are hover exit and touch explore @@ -828,13 +785,14 @@ public class TouchExplorer extends BaseEventStreamTransformation * @param policyFlags The policy flags associated with the event. */ private void sendHoverExitAndTouchExplorationGestureEndIfNeeded(int policyFlags) { - MotionEvent event = mInjectedPointerTracker.getLastInjectedHoverEvent(); + MotionEvent event = mDispatcher.getLastInjectedHoverEvent(); if (event != null && event.getActionMasked() != MotionEvent.ACTION_HOVER_EXIT) { final int pointerIdBits = event.getPointerIdBits(); if (!mSendTouchExplorationEndDelayed.isPending()) { mSendTouchExplorationEndDelayed.post(); } - sendMotionEvent(event, MotionEvent.ACTION_HOVER_EXIT, pointerIdBits, policyFlags); + mDispatcher.sendMotionEvent( + event, MotionEvent.ACTION_HOVER_EXIT, pointerIdBits, policyFlags); } } @@ -845,115 +803,14 @@ public class TouchExplorer extends BaseEventStreamTransformation * @param policyFlags The policy flags associated with the event. */ private void sendTouchExplorationGestureStartAndHoverEnterIfNeeded(int policyFlags) { - MotionEvent event = mInjectedPointerTracker.getLastInjectedHoverEvent(); + MotionEvent event = mDispatcher.getLastInjectedHoverEvent(); if (event != null && event.getActionMasked() == MotionEvent.ACTION_HOVER_EXIT) { final int pointerIdBits = event.getPointerIdBits(); - sendMotionEvent(event, MotionEvent.ACTION_HOVER_ENTER, pointerIdBits, policyFlags); + mDispatcher.sendMotionEvent( + event, MotionEvent.ACTION_HOVER_ENTER, pointerIdBits, policyFlags); } } - /** - * Sends up events to the view hierarchy for all pointers which are - * already being delivered i.e. pointers that are injected. - * - * @param prototype The prototype from which to create the injected events. - * @param policyFlags The policy flags associated with the event. - */ - private void sendUpForInjectedDownPointers(MotionEvent prototype, int policyFlags) { - int pointerIdBits = 0; - final int pointerCount = prototype.getPointerCount(); - for (int i = 0; i < pointerCount; i++) { - final int pointerId = prototype.getPointerId(i); - // Skip non injected down pointers. - if (!mInjectedPointerTracker.isInjectedPointerDown(pointerId)) { - continue; - } - pointerIdBits |= (1 << pointerId); - final int action = computeInjectionAction(MotionEvent.ACTION_UP, i); - sendMotionEvent(prototype, action, pointerIdBits, policyFlags); - } - } - - /** - * Sends an event. - * - * @param prototype The prototype from which to create the injected events. - * @param action The action of the event. - * @param pointerIdBits The bits of the pointers to send. - * @param policyFlags The policy flags associated with the event. - */ - private void sendMotionEvent(MotionEvent prototype, int action, int pointerIdBits, - int policyFlags) { - prototype.setAction(action); - - MotionEvent event = null; - if (pointerIdBits == ALL_POINTER_ID_BITS) { - event = prototype; - } else { - try { - event = prototype.split(pointerIdBits); - } catch (IllegalArgumentException e) { - Slog.e(LOG_TAG, "sendMotionEvent: Failed to split motion event: " + e); - return; - } - } - if (action == MotionEvent.ACTION_DOWN) { - event.setDownTime(event.getEventTime()); - } else { - event.setDownTime(mInjectedPointerTracker.getLastInjectedDownEventTime()); - } - if (DEBUG) { - Slog.d(LOG_TAG, "Injecting event: " + event + ", policyFlags=0x" - + Integer.toHexString(policyFlags)); - } - - // Make sure that the user will see the event. - policyFlags |= WindowManagerPolicy.FLAG_PASS_TO_USER; - // TODO: For now pass null for the raw event since the touch - // explorer is the last event transformation and it does - // not care about the raw event. - super.onMotionEvent(event, null, policyFlags); - - mInjectedPointerTracker.onMotionEvent(event); - - if (event != prototype) { - event.recycle(); - } - } - - /** - * Computes the action for an injected event based on a masked action - * and a pointer index. - * - * @param actionMasked The masked action. - * @param pointerIndex The index of the pointer which has changed. - * @return The action to be used for injection. - */ - private int computeInjectionAction(int actionMasked, int pointerIndex) { - switch (actionMasked) { - case MotionEvent.ACTION_DOWN: - case MotionEvent.ACTION_POINTER_DOWN: { - // Compute the action based on how many down pointers are injected. - if (mInjectedPointerTracker.getInjectedPointerDownCount() == 0) { - return MotionEvent.ACTION_DOWN; - } else { - return (pointerIndex << MotionEvent.ACTION_POINTER_INDEX_SHIFT) - | MotionEvent.ACTION_POINTER_DOWN; - } - } - case MotionEvent.ACTION_POINTER_UP: { - // Compute the action based on how many down pointers are injected. - if (mInjectedPointerTracker.getInjectedPointerDownCount() == 1) { - return MotionEvent.ACTION_UP; - } else { - return (pointerIndex << MotionEvent.ACTION_POINTER_INDEX_SHIFT) - | MotionEvent.ACTION_POINTER_UP; - } - } - default: - return actionMasked; - } - } /** * Determines whether a two pointer gesture is a dragging one. @@ -978,10 +835,34 @@ public class TouchExplorer extends BaseEventStreamTransformation MAX_DRAGGING_ANGLE_COS); } + /** + * Adjust the location of an injected event when performing a drag The new location will be in + * between the two fingers touching the screen. + */ + private void adjustEventLocationForDrag(MotionEvent event) { + + final float firstPtrX = event.getX(0); + final float firstPtrY = event.getY(0); + final float secondPtrX = event.getX(1); + final float secondPtrY = event.getY(1); + final int pointerIndex = event.findPointerIndex(mDraggingPointerId); + final float deltaX = + (pointerIndex == 0) ? (secondPtrX - firstPtrX) : (firstPtrX - secondPtrX); + final float deltaY = + (pointerIndex == 0) ? (secondPtrY - firstPtrY) : (firstPtrY - secondPtrY); + event.offsetLocation(deltaX / 2, deltaY / 2); + } + public TouchState getState() { return mState; } + @Override + public void setNext(EventStreamTransformation next) { + mDispatcher.setReceiver(next); + super.setNext(next); + } + /** * Class for delayed exiting from gesture detecting mode. */ @@ -998,7 +879,7 @@ public class TouchExplorer extends BaseEventStreamTransformation @Override public void run() { // Announce the end of gesture recognition. - sendAccessibilityEvent(AccessibilityEvent.TYPE_GESTURE_DETECTION_END); + mDispatcher.sendAccessibilityEvent(AccessibilityEvent.TYPE_GESTURE_DETECTION_END); clear(); } } @@ -1055,11 +936,12 @@ public class TouchExplorer extends BaseEventStreamTransformation public void run() { // Send an accessibility event to announce the touch exploration start. - sendAccessibilityEvent(AccessibilityEvent.TYPE_TOUCH_EXPLORATION_GESTURE_START); + mDispatcher.sendAccessibilityEvent( + AccessibilityEvent.TYPE_TOUCH_EXPLORATION_GESTURE_START); if (!mEvents.isEmpty()) { // Deliver a down event. - sendMotionEvent(mEvents.get(0), MotionEvent.ACTION_HOVER_ENTER, + mDispatcher.sendMotionEvent(mEvents.get(0), MotionEvent.ACTION_HOVER_ENTER, mPointerIdBits, mPolicyFlags); if (DEBUG) { Slog.d(LOG_TAG_SEND_HOVER_DELAYED, @@ -1069,7 +951,7 @@ public class TouchExplorer extends BaseEventStreamTransformation // Deliver move events. final int eventCount = mEvents.size(); for (int i = 1; i < eventCount; i++) { - sendMotionEvent(mEvents.get(i), MotionEvent.ACTION_HOVER_MOVE, + mDispatcher.sendMotionEvent(mEvents.get(i), MotionEvent.ACTION_HOVER_MOVE, mPointerIdBits, mPolicyFlags); if (DEBUG) { Slog.d(LOG_TAG_SEND_HOVER_DELAYED, @@ -1129,7 +1011,7 @@ public class TouchExplorer extends BaseEventStreamTransformation Slog.d(LOG_TAG_SEND_HOVER_DELAYED, "Injecting motion event:" + " ACTION_HOVER_EXIT"); } - sendMotionEvent(mPrototype, MotionEvent.ACTION_HOVER_EXIT, + mDispatcher.sendMotionEvent(mPrototype, MotionEvent.ACTION_HOVER_EXIT, mPointerIdBits, mPolicyFlags); if (!mSendTouchExplorationEndDelayed.isPending()) { mSendTouchExplorationEndDelayed.cancel(); @@ -1173,7 +1055,7 @@ public class TouchExplorer extends BaseEventStreamTransformation @Override public void run() { - sendAccessibilityEvent(mEventType); + mDispatcher.sendAccessibilityEvent(mEventType); } } diff --git a/services/accessibility/java/com/android/server/accessibility/gestures/TouchState.java b/services/accessibility/java/com/android/server/accessibility/gestures/TouchState.java index 17e969a1aa49..49938fa4c6b9 100644 --- a/services/accessibility/java/com/android/server/accessibility/gestures/TouchState.java +++ b/services/accessibility/java/com/android/server/accessibility/gestures/TouchState.java @@ -18,6 +18,8 @@ package com.android.server.accessibility.gestures; import static android.view.MotionEvent.INVALID_POINTER_ID; +import static com.android.server.accessibility.gestures.TouchExplorer.DEBUG; + import android.annotation.IntDef; import android.util.Slog; import android.view.MotionEvent; @@ -29,14 +31,12 @@ import android.view.accessibility.AccessibilityEvent; * dispatch. */ public class TouchState { - - private static final boolean DEBUG = false; private static final String LOG_TAG = "TouchState"; // Pointer-related constants // This constant captures the current implementation detail that // pointer IDs are between 0 and 31 inclusive (subject to change). // (See MAX_POINTER_ID in frameworks/base/include/ui/Input.h) - private static final int MAX_POINTER_COUNT = 32; + static final int MAX_POINTER_COUNT = 32; // Constant referring to the ids bits of all pointers. public static final int ALL_POINTER_ID_BITS = 0xFFFFFFFF; @@ -71,13 +71,9 @@ public class TouchState { // Helper class to track received pointers. // Todo: collapse or hide this class so multiple classes don't modify it. private final ReceivedPointerTracker mReceivedPointerTracker; - // Helper class to track injected pointers. - // Todo: collapse or hide this class so multiple classes don't modify it. - private final InjectedPointerTracker mInjectedPointerTracker; public TouchState() { mReceivedPointerTracker = new ReceivedPointerTracker(); - mInjectedPointerTracker = new InjectedPointerTracker(); } /** Clears the internal shared state. */ @@ -85,16 +81,6 @@ public class TouchState { setState(STATE_CLEAR); // Reset the pointer trackers. mReceivedPointerTracker.clear(); - mInjectedPointerTracker.clear(); - } - - /** - * Updates the state in response to a hover event dispatched by TouchExplorer. - * - * @param event The event sent from TouchExplorer. - */ - public void onInjectedMotionEvent(MotionEvent event) { - mInjectedPointerTracker.onMotionEvent(event); } /** @@ -226,117 +212,10 @@ public class TouchState { } } - public InjectedPointerTracker getInjectedPointerTracker() { - return mInjectedPointerTracker; - } - public ReceivedPointerTracker getReceivedPointerTracker() { return mReceivedPointerTracker; } - /** This class tracks the up/down state of each pointer. It does not track movement. */ - class InjectedPointerTracker { - private static final String LOG_TAG_INJECTED_POINTER_TRACKER = "InjectedPointerTracker"; - - // Keep track of which pointers sent to the system are down. - private int mInjectedPointersDown; - - // The time of the last injected down. - private long mLastInjectedDownEventTime; - - // The last injected hover event. - private MotionEvent mLastInjectedHoverEvent; - - /** - * Processes an injected {@link MotionEvent} event. - * - * @param event The event to process. - */ - public void onMotionEvent(MotionEvent event) { - final int action = event.getActionMasked(); - final int pointerId = event.getPointerId(event.getActionIndex()); - final int pointerFlag = (1 << pointerId); - switch (action) { - case MotionEvent.ACTION_DOWN: - case MotionEvent.ACTION_POINTER_DOWN: - mInjectedPointersDown |= pointerFlag; - mLastInjectedDownEventTime = event.getDownTime(); - break; - case MotionEvent.ACTION_UP: - case MotionEvent.ACTION_POINTER_UP: - mInjectedPointersDown &= ~pointerFlag; - if (mInjectedPointersDown == 0) { - mLastInjectedDownEventTime = 0; - } - break; - case MotionEvent.ACTION_HOVER_ENTER: - case MotionEvent.ACTION_HOVER_MOVE: - case MotionEvent.ACTION_HOVER_EXIT: - if (mLastInjectedHoverEvent != null) { - mLastInjectedHoverEvent.recycle(); - } - mLastInjectedHoverEvent = MotionEvent.obtain(event); - break; - } - if (DEBUG) { - Slog.i(LOG_TAG_INJECTED_POINTER_TRACKER, "Injected pointer:\n" + toString()); - } - } - - /** Clears the internals state. */ - public void clear() { - mInjectedPointersDown = 0; - } - - /** @return The time of the last injected down event. */ - public long getLastInjectedDownEventTime() { - return mLastInjectedDownEventTime; - } - - /** @return The number of down pointers injected to the view hierarchy. */ - public int getInjectedPointerDownCount() { - return Integer.bitCount(mInjectedPointersDown); - } - - /** @return The bits of the injected pointers that are down. */ - public int getInjectedPointersDown() { - return mInjectedPointersDown; - } - - /** - * Whether an injected pointer is down. - * - * @param pointerId The unique pointer id. - * @return True if the pointer is down. - */ - public boolean isInjectedPointerDown(int pointerId) { - final int pointerFlag = (1 << pointerId); - return (mInjectedPointersDown & pointerFlag) != 0; - } - - /** @return The the last injected hover event. */ - public MotionEvent getLastInjectedHoverEvent() { - return mLastInjectedHoverEvent; - } - - @Override - public String toString() { - StringBuilder builder = new StringBuilder(); - builder.append("========================="); - builder.append("\nDown pointers #"); - builder.append(Integer.bitCount(mInjectedPointersDown)); - builder.append(" [ "); - for (int i = 0; i < MAX_POINTER_COUNT; i++) { - if ((mInjectedPointersDown & i) != 0) { - builder.append(i); - builder.append(" "); - } - } - builder.append("]"); - builder.append("\n========================="); - return builder.toString(); - } - } /** This class tracks where and when a pointer went down. It does not track its movement. */ class ReceivedPointerTracker { private static final String LOG_TAG_RECEIVED_POINTER_TRACKER = "ReceivedPointerTracker"; |