Merge "Extract event sending code into EventDispatcher."
This commit is contained in:
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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";
|
||||
|
||||
Reference in New Issue
Block a user