Extract event sending code into EventDispatcher.
Also combine event-sending code with what used to be the InjectedPointerTracker class because that class was used for sending events anyway.
Bug: 136131815:
Test: atest CtsAccessibilityTestCases CtsAccessibilityServiceTestCases FrameworksServicesTests:TouchExplorerTest
Change-Id: I7fae3dc958642d8fae61b9120c732d27af16fca9
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 0000000..dc7a9aa
--- /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 7044c4d..f4ac821 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.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 @@
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 @@
// 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 @@
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 @@
} 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 @@
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 @@
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 @@
}
// 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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
// 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 @@
} 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 @@
// 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 @@
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 @@
}
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 @@
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 @@
* @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 @@
* @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 @@
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 @@
@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 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 @@
// 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 @@
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 @@
@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 17e969a..49938fa 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 @@
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 @@
* 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 @@
// 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 @@
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 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";