Skip to content

Commit d367b70

Browse files
sganovAndroid (Google) Code Review
authored andcommitted
Merge "Accessibility HOVER_ENTER / EXIT without enclosing EXPLORATION_GESTURE_START / END" into jb-mr1-dev
2 parents 6ebbe1b + f068fed commit d367b70

File tree

1 file changed

+103
-78
lines changed

1 file changed

+103
-78
lines changed

services/java/com/android/server/accessibility/TouchExplorer.java

Lines changed: 103 additions & 78 deletions
Original file line numberDiff line numberDiff line change
@@ -102,10 +102,6 @@ class TouchExplorer implements EventStreamTransformation {
102102
// The timeout after which we are no longer trying to detect a gesture.
103103
private static final int EXIT_GESTURE_DETECTION_TIMEOUT = 2000;
104104

105-
// The timeout to send interaction end events in case we did not
106-
// receive the expected hover exit event due to a misbehaving app.
107-
private static final int SEND_INTERACTION_END_EVENTS_TIMEOUT = 200;
108-
109105
// Temporary array for storing pointer IDs.
110106
private final int[] mTempPointerIds = new int[MAX_POINTER_COUNT];
111107

@@ -139,8 +135,11 @@ class TouchExplorer implements EventStreamTransformation {
139135
// Command for delayed sending of a hover exit event.
140136
private final SendHoverDelayed mSendHoverExitDelayed;
141137

142-
// Command for delayed sending of interaction ending events.
143-
private final SendInteractionEndEventsDelayed mSendInteractionEndEventsDelayed;
138+
// Command for delayed sending of touch exploration end events.
139+
private final SendAccessibilityEventDelayed mSendTouchExplorationEndDelayed;
140+
141+
// Command for delayed sending of touch interaction end events.
142+
private final SendAccessibilityEventDelayed mSendTouchInteractionEndDelayed;
144143

145144
// Command for delayed sending of a long press.
146145
private final PerformLongPressDelayed mPerformLongPressDelayed;
@@ -209,11 +208,8 @@ class TouchExplorer implements EventStreamTransformation {
209208
// The id of the last touch explored window.
210209
private int mLastTouchedWindowId;
211210

212-
// Whether touch exploration gesture has ended.
213-
private boolean mTouchExplorationGestureEnded;
214-
215-
// Whether touch interaction has ended.
216-
private boolean mTouchInteractionEnded;
211+
// Whether touch exploration is in progress.
212+
private boolean mTouchExplorationInProgress;
217213

218214
/**
219215
* Creates a new instance.
@@ -240,7 +236,12 @@ public TouchExplorer(Context context, AccessibilityManagerService service) {
240236
mGestureLibrary.load();
241237
mSendHoverEnterDelayed = new SendHoverDelayed(MotionEvent.ACTION_HOVER_ENTER, true);
242238
mSendHoverExitDelayed = new SendHoverDelayed(MotionEvent.ACTION_HOVER_EXIT, false);
243-
mSendInteractionEndEventsDelayed = new SendInteractionEndEventsDelayed();
239+
mSendTouchExplorationEndDelayed = new SendAccessibilityEventDelayed(
240+
AccessibilityEvent.TYPE_TOUCH_EXPLORATION_GESTURE_END,
241+
mDetermineUserIntentTimeout);
242+
mSendTouchInteractionEndDelayed = new SendAccessibilityEventDelayed(
243+
AccessibilityEvent.TYPE_TOUCH_INTERACTION_END,
244+
mDetermineUserIntentTimeout);
244245
mDoubleTapDetector = new DoubleTapDetector();
245246
final float density = context.getResources().getDisplayMetrics().density;
246247
mScaledMinPointerDistanceToUseMiddleLocation =
@@ -265,7 +266,7 @@ private void clear(MotionEvent event, int policyFlags) {
265266
switch (mCurrentState) {
266267
case STATE_TOUCH_EXPLORING: {
267268
// If a touch exploration gesture is in progress send events for its end.
268-
sendExitEventsIfNeeded(policyFlags);
269+
sendHoverExitAndTouchExplorationGestureEndIfNeeded(policyFlags);
269270
} break;
270271
case STATE_DRAGGING: {
271272
mDraggingPointerId = INVALID_POINTER_ID;
@@ -286,7 +287,8 @@ private void clear(MotionEvent event, int policyFlags) {
286287
mSendHoverExitDelayed.remove();
287288
mPerformLongPressDelayed.remove();
288289
mExitGestureDetectionModeDelayed.remove();
289-
mSendInteractionEndEventsDelayed.remove();
290+
mSendTouchExplorationEndDelayed.remove();
291+
mSendTouchInteractionEndDelayed.remove();
290292
// Reset the pointer trackers.
291293
mReceivedPointerTracker.clear();
292294
mInjectedPointerTracker.clear();
@@ -301,6 +303,7 @@ private void clear(MotionEvent event, int policyFlags) {
301303
if (mNext != null) {
302304
mNext.clear();
303305
}
306+
mTouchExplorationInProgress = false;
304307
}
305308

306309
@Override
@@ -341,19 +344,17 @@ public void onAccessibilityEvent(AccessibilityEvent event) {
341344

342345
// The event for gesture end should be strictly after the
343346
// last hover exit event.
344-
if (mTouchExplorationGestureEnded
347+
if (mSendTouchExplorationEndDelayed.isPending()
345348
&& eventType == AccessibilityEvent.TYPE_VIEW_HOVER_EXIT) {
346-
mSendInteractionEndEventsDelayed.remove();
347-
mTouchExplorationGestureEnded = false;
349+
mSendTouchExplorationEndDelayed.remove();
348350
sendAccessibilityEvent(AccessibilityEvent.TYPE_TOUCH_EXPLORATION_GESTURE_END);
349351
}
350352

351353
// The event for touch interaction end should be strictly after the
352354
// last hover exit and the touch exploration gesture end events.
353-
if (mTouchInteractionEnded
355+
if (mSendTouchInteractionEndDelayed.isPending()
354356
&& eventType == AccessibilityEvent.TYPE_VIEW_HOVER_EXIT) {
355-
mSendInteractionEndEventsDelayed.remove();
356-
mTouchInteractionEnded = false;
357+
mSendTouchInteractionEndDelayed.remove();
357358
sendAccessibilityEvent(AccessibilityEvent.TYPE_TOUCH_INTERACTION_END);
358359
}
359360

@@ -396,15 +397,6 @@ private void handleMotionEventStateTouchExploring(MotionEvent event, MotionEvent
396397

397398
switch (event.getActionMasked()) {
398399
case MotionEvent.ACTION_DOWN:
399-
// The delayed enter not delivered implies that we have delivered
400-
// TYPE_TOUCH_INTERACTION_START and not TYPE_TOUCH_INTERACTION_END,
401-
// therefore we need to deliver the interaction end event here.
402-
if (mSendHoverEnterDelayed.isPending()) {
403-
sendAccessibilityEvent(AccessibilityEvent.TYPE_TOUCH_INTERACTION_END);
404-
}
405-
// Announce the start of a new touch interaction.
406-
sendAccessibilityEvent(
407-
AccessibilityEvent.TYPE_TOUCH_INTERACTION_START);
408400
// Pre-feed the motion events to the gesture detector since we
409401
// have a distance slop before getting into gesture detection
410402
// mode and not using the points within this slop significantly
@@ -426,8 +418,20 @@ private void handleMotionEventStateTouchExploring(MotionEvent event, MotionEvent
426418
mSendHoverExitDelayed.remove();
427419
}
428420

429-
if (mSendInteractionEndEventsDelayed.isPending()) {
430-
mSendInteractionEndEventsDelayed.forceSendAndRemove();
421+
if (mSendTouchExplorationEndDelayed.isPending()) {
422+
mSendTouchExplorationEndDelayed.forceSendAndRemove();
423+
}
424+
425+
if (mSendTouchInteractionEndDelayed.isPending()) {
426+
mSendTouchInteractionEndDelayed.forceSendAndRemove();
427+
}
428+
429+
// Every pointer that goes down is active until it moves or
430+
// another one goes down. Hence, having more than one pointer
431+
// down we have already send the interaction start event.
432+
if (event.getPointerCount() == 1) {
433+
sendAccessibilityEvent(
434+
AccessibilityEvent.TYPE_TOUCH_INTERACTION_START);
431435
}
432436

433437
mPerformLongPressDelayed.remove();
@@ -443,11 +447,13 @@ private void handleMotionEventStateTouchExploring(MotionEvent event, MotionEvent
443447
mPerformLongPressDelayed.post(event, policyFlags);
444448
break;
445449
}
446-
// Deliver hover enter with a delay to have a chance
447-
// to detect what the user is trying to do.
448-
final int pointerId = receivedTracker.getPrimaryActivePointerId();
449-
final int pointerIdBits = (1 << pointerId);
450-
mSendHoverEnterDelayed.post(event, true, pointerIdBits, policyFlags);
450+
if (!mTouchExplorationInProgress) {
451+
// Deliver hover enter with a delay to have a chance
452+
// to detect what the user is trying to do.
453+
final int pointerId = receivedTracker.getPrimaryActivePointerId();
454+
final int pointerIdBits = (1 << pointerId);
455+
mSendHoverEnterDelayed.post(event, true, pointerIdBits, policyFlags);
456+
}
451457
} break;
452458
default: {
453459
/* do nothing - let the code for ACTION_MOVE decide what to do */
@@ -512,12 +518,27 @@ private void handleMotionEventStateTouchExploring(MotionEvent event, MotionEvent
512518
break;
513519
}
514520
} else {
521+
// Cancel the long press if pending and the user
522+
// moved more than the slop.
523+
if (mPerformLongPressDelayed.isPending()) {
524+
final float deltaX =
525+
receivedTracker.getReceivedPointerDownX(pointerId)
526+
- rawEvent.getX(pointerIndex);
527+
final float deltaY =
528+
receivedTracker.getReceivedPointerDownY(pointerId)
529+
- rawEvent.getY(pointerIndex);
530+
final double moveDelta = Math.hypot(deltaX, deltaY);
531+
// The user has moved enough for us to decide.
532+
if (moveDelta > mTouchSlop) {
533+
mPerformLongPressDelayed.remove();
534+
}
535+
}
515536
// The user is wither double tapping or performing long
516537
// press so do not send move events yet.
517538
if (mDoubleTapDetector.firstTapDetected()) {
518539
break;
519540
}
520-
sendEnterEventsIfNeeded(policyFlags);
541+
sendTouchExplorationGestureStartAndHoverEnterIfNeeded(policyFlags);
521542
sendMotionEvent(event, MotionEvent.ACTION_HOVER_MOVE, pointerIdBits,
522543
policyFlags);
523544
}
@@ -548,7 +569,7 @@ private void handleMotionEventStateTouchExploring(MotionEvent event, MotionEvent
548569
}
549570
// We are sending events so send exit and gesture
550571
// end since we transition to another state.
551-
sendExitEventsIfNeeded(policyFlags);
572+
sendHoverExitAndTouchExplorationGestureEndIfNeeded(policyFlags);
552573
}
553574

554575
// We know that a new state transition is to happen and the
@@ -583,7 +604,7 @@ private void handleMotionEventStateTouchExploring(MotionEvent event, MotionEvent
583604
mPerformLongPressDelayed.remove();
584605
// We are sending events so send exit and gesture
585606
// end since we transition to another state.
586-
sendExitEventsIfNeeded(policyFlags);
607+
sendHoverExitAndTouchExplorationGestureEndIfNeeded(policyFlags);
587608
}
588609

589610
// More than two pointers are delegated to the view hierarchy.
@@ -612,11 +633,14 @@ private void handleMotionEventStateTouchExploring(MotionEvent event, MotionEvent
612633

613634
// If we have not delivered the enter schedule exit.
614635
if (mSendHoverEnterDelayed.isPending()) {
615-
mSendHoverEnterDelayed.mTouchExplorationInProgress = false;
616636
mSendHoverExitDelayed.post(event, false, pointerIdBits, policyFlags);
617637
} else {
618638
// The user is touch exploring so we send events for end.
619-
sendExitEventsIfNeeded(policyFlags);
639+
sendHoverExitAndTouchExplorationGestureEndIfNeeded(policyFlags);
640+
}
641+
642+
if (!mSendTouchInteractionEndDelayed.isPending()) {
643+
mSendTouchInteractionEndDelayed.post();
620644
}
621645
} break;
622646
}
@@ -846,6 +870,14 @@ private void sendAccessibilityEvent(int type) {
846870
if (accessibilityManager.isEnabled()) {
847871
AccessibilityEvent event = AccessibilityEvent.obtain(type);
848872
accessibilityManager.sendAccessibilityEvent(event);
873+
switch (type) {
874+
case AccessibilityEvent.TYPE_TOUCH_EXPLORATION_GESTURE_START: {
875+
mTouchExplorationInProgress = true;
876+
} break;
877+
case AccessibilityEvent.TYPE_TOUCH_EXPLORATION_GESTURE_END: {
878+
mTouchExplorationInProgress = false;
879+
} break;
880+
}
849881
}
850882
}
851883

@@ -893,14 +925,12 @@ private void sendDownForAllActiveNotInjectedPointers(MotionEvent prototype, int
893925
*
894926
* @param policyFlags The policy flags associated with the event.
895927
*/
896-
private void sendExitEventsIfNeeded(int policyFlags) {
928+
private void sendHoverExitAndTouchExplorationGestureEndIfNeeded(int policyFlags) {
897929
MotionEvent event = mInjectedPointerTracker.getLastInjectedHoverEvent();
898930
if (event != null && event.getActionMasked() != MotionEvent.ACTION_HOVER_EXIT) {
899931
final int pointerIdBits = event.getPointerIdBits();
900-
mTouchExplorationGestureEnded = true;
901-
mTouchInteractionEnded = true;
902-
if (!mSendInteractionEndEventsDelayed.isPending()) {
903-
mSendInteractionEndEventsDelayed.post();
932+
if (!mSendTouchExplorationEndDelayed.isPending()) {
933+
mSendTouchExplorationEndDelayed.post();
904934
}
905935
sendMotionEvent(event, MotionEvent.ACTION_HOVER_EXIT, pointerIdBits, policyFlags);
906936
}
@@ -912,10 +942,11 @@ private void sendExitEventsIfNeeded(int policyFlags) {
912942
*
913943
* @param policyFlags The policy flags associated with the event.
914944
*/
915-
private void sendEnterEventsIfNeeded(int policyFlags) {
945+
private void sendTouchExplorationGestureStartAndHoverEnterIfNeeded(int policyFlags) {
916946
MotionEvent event = mInjectedPointerTracker.getLastInjectedHoverEvent();
917947
if (event != null && event.getActionMasked() == MotionEvent.ACTION_HOVER_EXIT) {
918948
final int pointerIdBits = event.getPointerIdBits();
949+
sendAccessibilityEvent(AccessibilityEvent.TYPE_TOUCH_EXPLORATION_GESTURE_START);
919950
sendMotionEvent(event, MotionEvent.ACTION_HOVER_ENTER, pointerIdBits, policyFlags);
920951
}
921952
}
@@ -1181,8 +1212,12 @@ public void onDoubleTap(MotionEvent secondTapUp, int policyFlags) {
11811212
mSendHoverExitDelayed.remove();
11821213
mPerformLongPressDelayed.remove();
11831214

1184-
// The touch interaction has ended since we will send a click.
1185-
sendAccessibilityEvent(AccessibilityEvent.TYPE_TOUCH_INTERACTION_END);
1215+
if (mSendTouchExplorationEndDelayed.isPending()) {
1216+
mSendTouchExplorationEndDelayed.forceSendAndRemove();
1217+
}
1218+
if (mSendTouchInteractionEndDelayed.isPending()) {
1219+
mSendTouchInteractionEndDelayed.forceSendAndRemove();
1220+
}
11861221

11871222
int clickLocationX;
11881223
int clickLocationY;
@@ -1416,7 +1451,7 @@ public void run() {
14161451
mLongPressingPointerDeltaX = (int) mEvent.getX(pointerIndex) - clickLocationX;
14171452
mLongPressingPointerDeltaY = (int) mEvent.getY(pointerIndex) - clickLocationY;
14181453

1419-
sendExitEventsIfNeeded(mPolicyFlags);
1454+
sendHoverExitAndTouchExplorationGestureEndIfNeeded(mPolicyFlags);
14201455

14211456
mCurrentState = STATE_DELEGATING;
14221457
sendDownForAllActiveNotInjectedPointers(mEvent, mPolicyFlags);
@@ -1445,7 +1480,6 @@ class SendHoverDelayed implements Runnable {
14451480
private MotionEvent mPrototype;
14461481
private int mPointerIdBits;
14471482
private int mPolicyFlags;
1448-
private boolean mTouchExplorationInProgress;
14491483

14501484
public SendHoverDelayed(int hoverAction, boolean gestureStarted) {
14511485
mHoverAction = hoverAction;
@@ -1456,7 +1490,6 @@ public void post(MotionEvent prototype, boolean touchExplorationInProgress,
14561490
int pointerIdBits, int policyFlags) {
14571491
remove();
14581492
mPrototype = MotionEvent.obtain(prototype);
1459-
mTouchExplorationInProgress = touchExplorationInProgress;
14601493
mPointerIdBits = pointerIdBits;
14611494
mPolicyFlags = policyFlags;
14621495
mHandler.postDelayed(this, mDetermineUserIntentTimeout);
@@ -1493,7 +1526,6 @@ private void clear() {
14931526
mPrototype = null;
14941527
mPointerIdBits = -1;
14951528
mPolicyFlags = 0;
1496-
mTouchExplorationInProgress = false;
14971529
}
14981530

14991531
public void forceSendAndRemove() {
@@ -1510,37 +1542,37 @@ public void run() {
15101542
Slog.d(LOG_TAG_SEND_HOVER_DELAYED, mGestureStarted ?
15111543
"touchExplorationGestureStarted" : "touchExplorationGestureEnded");
15121544
}
1513-
if (mTouchExplorationInProgress) {
1514-
if (mGestureStarted) {
1515-
sendAccessibilityEvent(AccessibilityEvent.TYPE_TOUCH_EXPLORATION_GESTURE_START);
1516-
} else {
1517-
mTouchExplorationGestureEnded = true;
1518-
mTouchInteractionEnded = true;
1519-
if (!mSendInteractionEndEventsDelayed.isPending()) {
1520-
mSendInteractionEndEventsDelayed.post();
1521-
}
1522-
}
1545+
if (mGestureStarted) {
1546+
sendAccessibilityEvent(AccessibilityEvent.TYPE_TOUCH_EXPLORATION_GESTURE_START);
15231547
} else {
1524-
if (!mGestureStarted) {
1525-
mTouchInteractionEnded = true;
1526-
if (!mSendInteractionEndEventsDelayed.isPending()) {
1527-
mSendInteractionEndEventsDelayed.post();
1528-
}
1548+
if (!mSendTouchExplorationEndDelayed.isPending()) {
1549+
mSendTouchExplorationEndDelayed.post();
1550+
}
1551+
if (mSendTouchInteractionEndDelayed.isPending()) {
1552+
mSendTouchInteractionEndDelayed.remove();
1553+
mSendTouchInteractionEndDelayed.post();
15291554
}
15301555
}
15311556
sendMotionEvent(mPrototype, mHoverAction, mPointerIdBits, mPolicyFlags);
15321557
clear();
15331558
}
15341559
}
15351560

1536-
private class SendInteractionEndEventsDelayed implements Runnable {
1561+
private class SendAccessibilityEventDelayed implements Runnable {
1562+
private final int mEventType;
1563+
private final int mDelay;
1564+
1565+
public SendAccessibilityEventDelayed(int eventType, int delay) {
1566+
mEventType = eventType;
1567+
mDelay = delay;
1568+
}
15371569

15381570
public void remove() {
15391571
mHandler.removeCallbacks(this);
15401572
}
15411573

15421574
public void post() {
1543-
mHandler.postDelayed(this, SEND_INTERACTION_END_EVENTS_TIMEOUT);
1575+
mHandler.postDelayed(this, mDelay);
15441576
}
15451577

15461578
public boolean isPending() {
@@ -1556,14 +1588,7 @@ public void forceSendAndRemove() {
15561588

15571589
@Override
15581590
public void run() {
1559-
if (mTouchExplorationGestureEnded) {
1560-
mTouchExplorationGestureEnded = false;
1561-
sendAccessibilityEvent(AccessibilityEvent.TYPE_TOUCH_EXPLORATION_GESTURE_END);
1562-
}
1563-
if (mTouchInteractionEnded) {
1564-
mTouchInteractionEnded = false;
1565-
sendAccessibilityEvent(AccessibilityEvent.TYPE_TOUCH_INTERACTION_END);
1566-
}
1591+
sendAccessibilityEvent(mEventType);
15671592
}
15681593
}
15691594

0 commit comments

Comments
 (0)