Home | History | Annotate | Download | only in accessibility
      1 /*
      2  ** Copyright 2011, The Android Open Source Project
      3  **
      4  ** Licensed under the Apache License, Version 2.0 (the "License");
      5  ** you may not use this file except in compliance with the License.
      6  ** You may obtain a copy of the License at
      7  **
      8  **     http://www.apache.org/licenses/LICENSE-2.0
      9  **
     10  ** Unless required by applicable law or agreed to in writing, software
     11  ** distributed under the License is distributed on an "AS IS" BASIS,
     12  ** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     13  ** See the License for the specific language governing permissions and
     14  ** limitations under the License.
     15  */
     16 
     17 package com.android.server.accessibility;
     18 
     19 import android.content.Context;
     20 import android.graphics.Point;
     21 import android.os.Handler;
     22 import android.util.Slog;
     23 import android.view.InputDevice;
     24 import android.view.KeyEvent;
     25 import android.view.MotionEvent;
     26 import android.view.MotionEvent.PointerCoords;
     27 import android.view.MotionEvent.PointerProperties;
     28 import android.view.ViewConfiguration;
     29 import android.view.WindowManagerPolicy;
     30 import android.view.accessibility.AccessibilityEvent;
     31 import android.view.accessibility.AccessibilityManager;
     32 
     33 import java.util.ArrayList;
     34 import java.util.Arrays;
     35 import java.util.List;
     36 
     37 /**
     38  * This class is a strategy for performing touch exploration. It
     39  * transforms the motion event stream by modifying, adding, replacing,
     40  * and consuming certain events. The interaction model is:
     41  *
     42  * <ol>
     43  *   <li>1. One finger moving slow around performs touch exploration.</li>
     44  *   <li>2. One finger moving fast around performs gestures.</li>
     45  *   <li>3. Two close fingers moving in the same direction perform a drag.</li>
     46  *   <li>4. Multi-finger gestures are delivered to view hierarchy.</li>
     47  *   <li>5. Two fingers moving in different directions are considered a multi-finger gesture.</li>
     48  *   <li>7. Double tapping clicks on the on the last touch explored location if it was in
     49  *          a window that does not take focus, otherwise the click is within the accessibility
     50  *          focused rectangle.</li>
     51  *   <li>7. Tapping and holding for a while performs a long press in a similar fashion
     52  *          as the click above.</li>
     53  * <ol>
     54  *
     55  * @hide
     56  */
     57 class TouchExplorer implements EventStreamTransformation, AccessibilityGestureDetector.Listener {
     58 
     59     private static final boolean DEBUG = false;
     60 
     61     // Tag for logging received events.
     62     private static final String LOG_TAG = "TouchExplorer";
     63 
     64     // States this explorer can be in.
     65     private static final int STATE_TOUCH_EXPLORING = 0x00000001;
     66     private static final int STATE_DRAGGING = 0x00000002;
     67     private static final int STATE_DELEGATING = 0x00000004;
     68     private static final int STATE_GESTURE_DETECTING = 0x00000005;
     69 
     70     private static final int CLICK_LOCATION_NONE = 0;
     71     private static final int CLICK_LOCATION_ACCESSIBILITY_FOCUS = 1;
     72     private static final int CLICK_LOCATION_LAST_TOUCH_EXPLORED = 2;
     73 
     74     // The maximum of the cosine between the vectors of two moving
     75     // pointers so they can be considered moving in the same direction.
     76     private static final float MAX_DRAGGING_ANGLE_COS = 0.525321989f; // cos(pi/4)
     77 
     78     // Constant referring to the ids bits of all pointers.
     79     private static final int ALL_POINTER_ID_BITS = 0xFFFFFFFF;
     80 
     81     // This constant captures the current implementation detail that
     82     // pointer IDs are between 0 and 31 inclusive (subject to change).
     83     // (See MAX_POINTER_ID in frameworks/base/include/ui/Input.h)
     84     private static final int MAX_POINTER_COUNT = 32;
     85 
     86     // Invalid pointer ID.
     87     private static final int INVALID_POINTER_ID = -1;
     88 
     89     // The minimal distance before we take the middle of the distance between
     90     // the two dragging pointers as opposed to use the location of the primary one.
     91     private static final int MIN_POINTER_DISTANCE_TO_USE_MIDDLE_LOCATION_DIP = 200;
     92 
     93     // The timeout after which we are no longer trying to detect a gesture.
     94     private static final int EXIT_GESTURE_DETECTION_TIMEOUT = 2000;
     95 
     96     // Timeout before trying to decide what the user is trying to do.
     97     private final int mDetermineUserIntentTimeout;
     98 
     99     // Slop between the first and second tap to be a double tap.
    100     private final int mDoubleTapSlop;
    101 
    102     // The current state of the touch explorer.
    103     private int mCurrentState = STATE_TOUCH_EXPLORING;
    104 
    105     // The ID of the pointer used for dragging.
    106     private int mDraggingPointerId;
    107 
    108     // Handler for performing asynchronous operations.
    109     private final Handler mHandler;
    110 
    111     // Command for delayed sending of a hover enter and move event.
    112     private final SendHoverEnterAndMoveDelayed mSendHoverEnterAndMoveDelayed;
    113 
    114     // Command for delayed sending of a hover exit event.
    115     private final SendHoverExitDelayed mSendHoverExitDelayed;
    116 
    117     // Command for delayed sending of touch exploration end events.
    118     private final SendAccessibilityEventDelayed mSendTouchExplorationEndDelayed;
    119 
    120     // Command for delayed sending of touch interaction end events.
    121     private final SendAccessibilityEventDelayed mSendTouchInteractionEndDelayed;
    122 
    123     // Command for exiting gesture detection mode after a timeout.
    124     private final ExitGestureDetectionModeDelayed mExitGestureDetectionModeDelayed;
    125 
    126     // Helper to detect gestures.
    127     private final AccessibilityGestureDetector mGestureDetector;
    128 
    129     // The scaled minimal distance before we take the middle of the distance between
    130     // the two dragging pointers as opposed to use the location of the primary one.
    131     private final int mScaledMinPointerDistanceToUseMiddleLocation;
    132 
    133     // The handler to which to delegate events.
    134     private EventStreamTransformation mNext;
    135 
    136     // Helper class to track received pointers.
    137     private final ReceivedPointerTracker mReceivedPointerTracker;
    138 
    139     // Helper class to track injected pointers.
    140     private final InjectedPointerTracker mInjectedPointerTracker;
    141 
    142     // Handle to the accessibility manager service.
    143     private final AccessibilityManagerService mAms;
    144 
    145     // Temporary point to avoid instantiation.
    146     private final Point mTempPoint = new Point();
    147 
    148     // Context in which this explorer operates.
    149     private final Context mContext;
    150 
    151     // The long pressing pointer id if coordinate remapping is needed.
    152     private int mLongPressingPointerId = -1;
    153 
    154     // The long pressing pointer X if coordinate remapping is needed.
    155     private int mLongPressingPointerDeltaX;
    156 
    157     // The long pressing pointer Y if coordinate remapping is needed.
    158     private int mLongPressingPointerDeltaY;
    159 
    160     // The id of the last touch explored window.
    161     private int mLastTouchedWindowId;
    162 
    163     // Whether touch exploration is in progress.
    164     private boolean mTouchExplorationInProgress;
    165 
    166     /**
    167      * Creates a new instance.
    168      *
    169      * @param inputFilter The input filter associated with this explorer.
    170      * @param context A context handle for accessing resources.
    171      */
    172     public TouchExplorer(Context context, AccessibilityManagerService service) {
    173         mContext = context;
    174         mAms = service;
    175         mReceivedPointerTracker = new ReceivedPointerTracker();
    176         mInjectedPointerTracker = new InjectedPointerTracker();
    177         mDetermineUserIntentTimeout = ViewConfiguration.getDoubleTapTimeout();
    178         mDoubleTapSlop = ViewConfiguration.get(context).getScaledDoubleTapSlop();
    179         mHandler = new Handler(context.getMainLooper());
    180         mExitGestureDetectionModeDelayed = new ExitGestureDetectionModeDelayed();
    181         mSendHoverEnterAndMoveDelayed = new SendHoverEnterAndMoveDelayed();
    182         mSendHoverExitDelayed = new SendHoverExitDelayed();
    183         mSendTouchExplorationEndDelayed = new SendAccessibilityEventDelayed(
    184                 AccessibilityEvent.TYPE_TOUCH_EXPLORATION_GESTURE_END,
    185                 mDetermineUserIntentTimeout);
    186         mSendTouchInteractionEndDelayed = new SendAccessibilityEventDelayed(
    187                 AccessibilityEvent.TYPE_TOUCH_INTERACTION_END,
    188                 mDetermineUserIntentTimeout);
    189         mGestureDetector = new AccessibilityGestureDetector(context, this);
    190         final float density = context.getResources().getDisplayMetrics().density;
    191         mScaledMinPointerDistanceToUseMiddleLocation =
    192             (int) (MIN_POINTER_DISTANCE_TO_USE_MIDDLE_LOCATION_DIP * density);
    193     }
    194 
    195     @Override
    196     public void clearEvents(int inputSource) {
    197         if (inputSource == InputDevice.SOURCE_TOUCHSCREEN) {
    198             clear();
    199         }
    200         if (mNext != null) {
    201             mNext.clearEvents(inputSource);
    202         }
    203     }
    204 
    205     @Override
    206     public void onDestroy() {
    207         clear();
    208     }
    209 
    210     private void clear() {
    211         // If we have not received an event then we are in initial
    212         // state. Therefore, there is not need to clean anything.
    213         MotionEvent event = mReceivedPointerTracker.getLastReceivedEvent();
    214         if (event != null) {
    215             clear(mReceivedPointerTracker.getLastReceivedEvent(), WindowManagerPolicy.FLAG_TRUSTED);
    216         }
    217     }
    218 
    219     private void clear(MotionEvent event, int policyFlags) {
    220         switch (mCurrentState) {
    221             case STATE_TOUCH_EXPLORING: {
    222                 // If a touch exploration gesture is in progress send events for its end.
    223                 sendHoverExitAndTouchExplorationGestureEndIfNeeded(policyFlags);
    224             } break;
    225             case STATE_DRAGGING: {
    226                 mDraggingPointerId = INVALID_POINTER_ID;
    227                 // Send exit to all pointers that we have delivered.
    228                 sendUpForInjectedDownPointers(event, policyFlags);
    229             } break;
    230             case STATE_DELEGATING: {
    231                 // Send exit to all pointers that we have delivered.
    232                 sendUpForInjectedDownPointers(event, policyFlags);
    233             } break;
    234             case STATE_GESTURE_DETECTING: {
    235                 // No state specific cleanup required.
    236             } break;
    237         }
    238         // Remove all pending callbacks.
    239         mSendHoverEnterAndMoveDelayed.cancel();
    240         mSendHoverExitDelayed.cancel();
    241         mExitGestureDetectionModeDelayed.cancel();
    242         mSendTouchExplorationEndDelayed.cancel();
    243         mSendTouchInteractionEndDelayed.cancel();
    244         // Reset the pointer trackers.
    245         mReceivedPointerTracker.clear();
    246         mInjectedPointerTracker.clear();
    247         // Clear the gesture detector
    248         mGestureDetector.clear();
    249         // Go to initial state.
    250         // Clear the long pressing pointer remap data.
    251         mLongPressingPointerId = -1;
    252         mLongPressingPointerDeltaX = 0;
    253         mLongPressingPointerDeltaY = 0;
    254         mCurrentState = STATE_TOUCH_EXPLORING;
    255         mTouchExplorationInProgress = false;
    256         mAms.onTouchInteractionEnd();
    257     }
    258 
    259     @Override
    260     public void setNext(EventStreamTransformation next) {
    261         mNext = next;
    262     }
    263 
    264     @Override
    265     public void onMotionEvent(MotionEvent event, MotionEvent rawEvent, int policyFlags) {
    266         if (!event.isFromSource(InputDevice.SOURCE_TOUCHSCREEN)) {
    267             if (mNext != null) {
    268                 mNext.onMotionEvent(event, rawEvent, policyFlags);
    269             }
    270             return;
    271         }
    272 
    273         if (DEBUG) {
    274             Slog.d(LOG_TAG, "Received event: " + event + ", policyFlags=0x"
    275                     + Integer.toHexString(policyFlags));
    276             Slog.d(LOG_TAG, getStateSymbolicName(mCurrentState));
    277         }
    278 
    279         mReceivedPointerTracker.onMotionEvent(rawEvent);
    280 
    281         // The motion detector is interested in the movements in physical space,
    282         // so it uses the rawEvent to ignore magnification and other
    283         // transformations.
    284         if (mGestureDetector.onMotionEvent(rawEvent, policyFlags)) {
    285             // Event was handled by the gesture detector.
    286             return;
    287         }
    288 
    289         if (event.getActionMasked() == MotionEvent.ACTION_CANCEL) {
    290             clear(event, policyFlags);
    291             return;
    292         }
    293 
    294         switch(mCurrentState) {
    295             case STATE_TOUCH_EXPLORING: {
    296                 handleMotionEventStateTouchExploring(event, rawEvent, policyFlags);
    297             } break;
    298             case STATE_DRAGGING: {
    299                 handleMotionEventStateDragging(event, policyFlags);
    300             } break;
    301             case STATE_DELEGATING: {
    302                 handleMotionEventStateDelegating(event, policyFlags);
    303             } break;
    304             case STATE_GESTURE_DETECTING: {
    305                 // Already handled.
    306             } break;
    307             default:
    308                 throw new IllegalStateException("Illegal state: " + mCurrentState);
    309         }
    310     }
    311 
    312     @Override
    313     public void onKeyEvent(KeyEvent event, int policyFlags) {
    314         if (mNext != null) {
    315             mNext.onKeyEvent(event, policyFlags);
    316         }
    317     }
    318 
    319     @Override
    320     public void onAccessibilityEvent(AccessibilityEvent event) {
    321         final int eventType = event.getEventType();
    322 
    323         // The event for gesture end should be strictly after the
    324         // last hover exit event.
    325         if (mSendTouchExplorationEndDelayed.isPending()
    326                 && eventType == AccessibilityEvent.TYPE_VIEW_HOVER_EXIT) {
    327                     mSendTouchExplorationEndDelayed.cancel();
    328             sendAccessibilityEvent(AccessibilityEvent.TYPE_TOUCH_EXPLORATION_GESTURE_END);
    329         }
    330 
    331         // The event for touch interaction end should be strictly after the
    332         // last hover exit and the touch exploration gesture end events.
    333         if (mSendTouchInteractionEndDelayed.isPending()
    334                 && eventType == AccessibilityEvent.TYPE_VIEW_HOVER_EXIT) {
    335             mSendTouchInteractionEndDelayed.cancel();
    336             sendAccessibilityEvent(AccessibilityEvent.TYPE_TOUCH_INTERACTION_END);
    337         }
    338 
    339         // If a new window opens or the accessibility focus moves we no longer
    340         // want to click/long press on the last touch explored location.
    341         switch (eventType) {
    342             case AccessibilityEvent.TYPE_WINDOW_STATE_CHANGED:
    343             case AccessibilityEvent.TYPE_VIEW_ACCESSIBILITY_FOCUSED: {
    344                 if (mInjectedPointerTracker.mLastInjectedHoverEventForClick != null) {
    345                     mInjectedPointerTracker.mLastInjectedHoverEventForClick.recycle();
    346                     mInjectedPointerTracker.mLastInjectedHoverEventForClick = null;
    347                 }
    348                 mLastTouchedWindowId = -1;
    349             } break;
    350             case AccessibilityEvent.TYPE_VIEW_HOVER_ENTER:
    351             case AccessibilityEvent.TYPE_VIEW_HOVER_EXIT: {
    352                 mLastTouchedWindowId = event.getWindowId();
    353             } break;
    354         }
    355         if (mNext != null) {
    356             mNext.onAccessibilityEvent(event);
    357         }
    358     }
    359 
    360     @Override
    361     public void onDoubleTapAndHold(MotionEvent event, int policyFlags) {
    362         // Ignore the event if we aren't touch exploring.
    363         if (mCurrentState != STATE_TOUCH_EXPLORING) {
    364             return;
    365         }
    366 
    367         // Pointers should not be zero when running this command.
    368         if (mReceivedPointerTracker.getLastReceivedEvent().getPointerCount() == 0) {
    369             return;
    370         }
    371 
    372         final int pointerIndex = event.getActionIndex();
    373         final int pointerId = event.getPointerId(pointerIndex);
    374 
    375         Point clickLocation = mTempPoint;
    376         final int result = computeClickLocation(clickLocation);
    377 
    378         if (result == CLICK_LOCATION_NONE) {
    379             return;
    380         }
    381 
    382         mLongPressingPointerId = pointerId;
    383         mLongPressingPointerDeltaX = (int) event.getX(pointerIndex) - clickLocation.x;
    384         mLongPressingPointerDeltaY = (int) event.getY(pointerIndex) - clickLocation.y;
    385 
    386         sendHoverExitAndTouchExplorationGestureEndIfNeeded(policyFlags);
    387 
    388         mCurrentState = STATE_DELEGATING;
    389         sendDownForAllNotInjectedPointers(event, policyFlags);
    390     }
    391 
    392     @Override
    393     public boolean onDoubleTap(MotionEvent event, int policyFlags) {
    394         // Ignore the event if we aren't touch exploring.
    395         if (mCurrentState != STATE_TOUCH_EXPLORING) {
    396             return false;
    397         }
    398 
    399         // Remove pending event deliveries.
    400         mSendHoverEnterAndMoveDelayed.cancel();
    401         mSendHoverExitDelayed.cancel();
    402 
    403         if (mSendTouchExplorationEndDelayed.isPending()) {
    404             mSendTouchExplorationEndDelayed.forceSendAndRemove();
    405         }
    406         if (mSendTouchInteractionEndDelayed.isPending()) {
    407             mSendTouchInteractionEndDelayed.forceSendAndRemove();
    408         }
    409 
    410         final int pointerIndex = event.getActionIndex();
    411         final int pointerId = event.getPointerId(pointerIndex);
    412 
    413         Point clickLocation = mTempPoint;
    414         final int result = computeClickLocation(clickLocation);
    415         if (result == CLICK_LOCATION_NONE) {
    416             // We can't send a click to no location, but the gesture was still
    417             // consumed.
    418             return true;
    419         }
    420 
    421         // Do the click.
    422         PointerProperties[] properties = new PointerProperties[1];
    423         properties[0] = new PointerProperties();
    424         event.getPointerProperties(pointerIndex, properties[0]);
    425         PointerCoords[] coords = new PointerCoords[1];
    426         coords[0] = new PointerCoords();
    427         coords[0].x = clickLocation.x;
    428         coords[0].y = clickLocation.y;
    429         MotionEvent click_event = MotionEvent.obtain(event.getDownTime(),
    430                 event.getEventTime(), MotionEvent.ACTION_DOWN, 1, properties,
    431                 coords, 0, 0, 1.0f, 1.0f, event.getDeviceId(), 0,
    432                 event.getSource(), event.getFlags());
    433         final boolean targetAccessibilityFocus = (result == CLICK_LOCATION_ACCESSIBILITY_FOCUS);
    434         sendActionDownAndUp(click_event, policyFlags, targetAccessibilityFocus);
    435         click_event.recycle();
    436         return true;
    437     }
    438 
    439     @Override
    440     public boolean onGestureStarted() {
    441       // We have to perform gesture detection, so
    442       // clear the current state and try to detect.
    443       mCurrentState = STATE_GESTURE_DETECTING;
    444       mSendHoverEnterAndMoveDelayed.cancel();
    445       mSendHoverExitDelayed.cancel();
    446       mExitGestureDetectionModeDelayed.post();
    447       // Send accessibility event to announce the start
    448       // of gesture recognition.
    449       sendAccessibilityEvent(AccessibilityEvent.TYPE_GESTURE_DETECTION_START);
    450       return false;
    451     }
    452 
    453     @Override
    454     public boolean onGestureCompleted(int gestureId) {
    455         if (mCurrentState != STATE_GESTURE_DETECTING) {
    456             return false;
    457         }
    458 
    459         endGestureDetection();
    460 
    461         mAms.onGesture(gestureId);
    462 
    463         return true;
    464     }
    465 
    466     @Override
    467     public boolean onGestureCancelled(MotionEvent event, int policyFlags) {
    468         if (mCurrentState == STATE_GESTURE_DETECTING) {
    469             endGestureDetection();
    470             return true;
    471         } else if (mCurrentState == STATE_TOUCH_EXPLORING) {
    472             // If the finger is still moving, pass the event on.
    473             if (event.getActionMasked() == MotionEvent.ACTION_MOVE) {
    474                 final int pointerId = mReceivedPointerTracker.getPrimaryPointerId();
    475                 final int pointerIdBits = (1 << pointerId);
    476 
    477                 // We have just decided that the user is touch,
    478                 // exploring so start sending events.
    479                 mSendHoverEnterAndMoveDelayed.addEvent(event);
    480                 mSendHoverEnterAndMoveDelayed.forceSendAndRemove();
    481                 mSendHoverExitDelayed.cancel();
    482                 sendMotionEvent(event, MotionEvent.ACTION_HOVER_MOVE, pointerIdBits, policyFlags);
    483                 return true;
    484             }
    485         }
    486         return false;
    487     }
    488 
    489     /**
    490      * Handles a motion event in touch exploring state.
    491      *
    492      * @param event The event to be handled.
    493      * @param rawEvent The raw (unmodified) motion event.
    494      * @param policyFlags The policy flags associated with the event.
    495      */
    496     private void handleMotionEventStateTouchExploring(MotionEvent event, MotionEvent rawEvent,
    497             int policyFlags) {
    498         ReceivedPointerTracker receivedTracker = mReceivedPointerTracker;
    499 
    500         switch (event.getActionMasked()) {
    501             case MotionEvent.ACTION_DOWN: {
    502                 mAms.onTouchInteractionStart();
    503 
    504                 sendAccessibilityEvent(AccessibilityEvent.TYPE_TOUCH_INTERACTION_START);
    505 
    506                 // If we still have not notified the user for the last
    507                 // touch, we figure out what to do. If were waiting
    508                 // we resent the delayed callback and wait again.
    509                 mSendHoverEnterAndMoveDelayed.cancel();
    510                 mSendHoverExitDelayed.cancel();
    511 
    512                 if (mSendTouchExplorationEndDelayed.isPending()) {
    513                     mSendTouchExplorationEndDelayed.forceSendAndRemove();
    514                 }
    515 
    516                 if (mSendTouchInteractionEndDelayed.isPending()) {
    517                     mSendTouchInteractionEndDelayed.forceSendAndRemove();
    518                 }
    519 
    520                 if (!mGestureDetector.firstTapDetected() && !mTouchExplorationInProgress) {
    521                     if (!mSendHoverEnterAndMoveDelayed.isPending()) {
    522                         // Deliver hover enter with a delay to have a chance
    523                         // to detect what the user is trying to do.
    524                         final int pointerId = receivedTracker.getPrimaryPointerId();
    525                         final int pointerIdBits = (1 << pointerId);
    526                         mSendHoverEnterAndMoveDelayed.post(event, true, pointerIdBits,
    527                                 policyFlags);
    528                     } else {
    529                         // Cache the event until we discern exploration from gesturing.
    530                         mSendHoverEnterAndMoveDelayed.addEvent(event);
    531                     }
    532                 }
    533             } break;
    534             case MotionEvent.ACTION_POINTER_DOWN: {
    535                 // Another finger down means that if we have not started to deliver
    536                 // hover events, we will not have to. The code for ACTION_MOVE will
    537                 // decide what we will actually do next.
    538                 mSendHoverEnterAndMoveDelayed.cancel();
    539                 mSendHoverExitDelayed.cancel();
    540             } break;
    541             case MotionEvent.ACTION_MOVE: {
    542                 final int pointerId = receivedTracker.getPrimaryPointerId();
    543                 final int pointerIndex = event.findPointerIndex(pointerId);
    544                 final int pointerIdBits = (1 << pointerId);
    545                 switch (event.getPointerCount()) {
    546                     case 1: {
    547                         // We have not started sending events since we try to
    548                         // figure out what the user is doing.
    549                         if (mSendHoverEnterAndMoveDelayed.isPending()) {
    550                             // Cache the event until we discern exploration from gesturing.
    551                             mSendHoverEnterAndMoveDelayed.addEvent(event);
    552                         } else {
    553                             if (mTouchExplorationInProgress) {
    554                                 sendTouchExplorationGestureStartAndHoverEnterIfNeeded(policyFlags);
    555                                 sendMotionEvent(event, MotionEvent.ACTION_HOVER_MOVE, pointerIdBits,
    556                                         policyFlags);
    557                             }
    558                         }
    559                     } break;
    560                     case 2: {
    561                         // More than one pointer so the user is not touch exploring
    562                         // and now we have to decide whether to delegate or drag.
    563                         if (mSendHoverEnterAndMoveDelayed.isPending()) {
    564                             // We have not started sending events so cancel
    565                             // scheduled sending events.
    566                             mSendHoverEnterAndMoveDelayed.cancel();
    567                             mSendHoverExitDelayed.cancel();
    568                         } else {
    569                             if (mTouchExplorationInProgress) {
    570                                 // If the user is touch exploring the second pointer may be
    571                                 // performing a double tap to activate an item without need
    572                                 // for the user to lift his exploring finger.
    573                                 // It is *important* to use the distance traveled by the pointers
    574                                 // on the screen which may or may not be magnified.
    575                                 final float deltaX = receivedTracker.getReceivedPointerDownX(
    576                                         pointerId) - rawEvent.getX(pointerIndex);
    577                                 final float deltaY = receivedTracker.getReceivedPointerDownY(
    578                                         pointerId) - rawEvent.getY(pointerIndex);
    579                                 final double moveDelta = Math.hypot(deltaX, deltaY);
    580                                 if (moveDelta < mDoubleTapSlop) {
    581                                     break;
    582                                 }
    583                                 // We are sending events so send exit and gesture
    584                                 // end since we transition to another state.
    585                                 sendHoverExitAndTouchExplorationGestureEndIfNeeded(policyFlags);
    586                             }
    587                         }
    588 
    589                         if (isDraggingGesture(event)) {
    590                             // Two pointers moving in the same direction within
    591                             // a given distance perform a drag.
    592                             mCurrentState = STATE_DRAGGING;
    593                             mDraggingPointerId = pointerId;
    594                             event.setEdgeFlags(receivedTracker.getLastReceivedDownEdgeFlags());
    595                             sendMotionEvent(event, MotionEvent.ACTION_DOWN, pointerIdBits,
    596                                     policyFlags);
    597                         } else {
    598                             // Two pointers moving arbitrary are delegated to the view hierarchy.
    599                             mCurrentState = STATE_DELEGATING;
    600                             sendDownForAllNotInjectedPointers(event, policyFlags);
    601                         }
    602                     } break;
    603                     default: {
    604                         // More than one pointer so the user is not touch exploring
    605                         // and now we have to decide whether to delegate or drag.
    606                         if (mSendHoverEnterAndMoveDelayed.isPending()) {
    607                             // We have not started sending events so cancel
    608                             // scheduled sending events.
    609                             mSendHoverEnterAndMoveDelayed.cancel();
    610                             mSendHoverExitDelayed.cancel();
    611                         } else {
    612                             // We are sending events so send exit and gesture
    613                             // end since we transition to another state.
    614                             sendHoverExitAndTouchExplorationGestureEndIfNeeded(policyFlags);
    615                         }
    616 
    617                         // More than two pointers are delegated to the view hierarchy.
    618                         mCurrentState = STATE_DELEGATING;
    619                         sendDownForAllNotInjectedPointers(event, policyFlags);
    620                     }
    621                 }
    622             } break;
    623             case MotionEvent.ACTION_UP: {
    624                 mAms.onTouchInteractionEnd();
    625                 final int pointerId = event.getPointerId(event.getActionIndex());
    626                 final int pointerIdBits = (1 << pointerId);
    627 
    628                 if (mSendHoverEnterAndMoveDelayed.isPending()) {
    629                     // If we have not delivered the enter schedule an exit.
    630                     mSendHoverExitDelayed.post(event, pointerIdBits, policyFlags);
    631                 } else {
    632                     // The user is touch exploring so we send events for end.
    633                     sendHoverExitAndTouchExplorationGestureEndIfNeeded(policyFlags);
    634                 }
    635 
    636                 if (!mSendTouchInteractionEndDelayed.isPending()) {
    637                     mSendTouchInteractionEndDelayed.post();
    638                 }
    639 
    640             } break;
    641         }
    642     }
    643 
    644     /**
    645      * Handles a motion event in dragging state.
    646      *
    647      * @param event The event to be handled.
    648      * @param policyFlags The policy flags associated with the event.
    649      */
    650     private void handleMotionEventStateDragging(MotionEvent event, int policyFlags) {
    651         int pointerIdBits = 0;
    652         // Clear the dragging pointer id if it's no longer valid.
    653         if (event.findPointerIndex(mDraggingPointerId) == -1) {
    654             Slog.e(LOG_TAG, "mDraggingPointerId doesn't match any pointers on current event. " +
    655                     "mDraggingPointerId: " + Integer.toString(mDraggingPointerId) +
    656                     ", Event: " + event);
    657             mDraggingPointerId = INVALID_POINTER_ID;
    658         } else {
    659             pointerIdBits = (1 << mDraggingPointerId);
    660         }
    661         switch (event.getActionMasked()) {
    662             case MotionEvent.ACTION_DOWN: {
    663                 throw new IllegalStateException("Dragging state can be reached only if two "
    664                         + "pointers are already down");
    665             }
    666             case MotionEvent.ACTION_POINTER_DOWN: {
    667                 // We are in dragging state so we have two pointers and another one
    668                 // goes down => delegate the three pointers to the view hierarchy
    669                 mCurrentState = STATE_DELEGATING;
    670                 if (mDraggingPointerId != INVALID_POINTER_ID) {
    671                     sendMotionEvent(event, MotionEvent.ACTION_UP, pointerIdBits, policyFlags);
    672                 }
    673                 sendDownForAllNotInjectedPointers(event, policyFlags);
    674             } break;
    675             case MotionEvent.ACTION_MOVE: {
    676                 if (mDraggingPointerId == INVALID_POINTER_ID) {
    677                     break;
    678                 }
    679                 switch (event.getPointerCount()) {
    680                     case 1: {
    681                         // do nothing
    682                     } break;
    683                     case 2: {
    684                         if (isDraggingGesture(event)) {
    685                             final float firstPtrX = event.getX(0);
    686                             final float firstPtrY = event.getY(0);
    687                             final float secondPtrX = event.getX(1);
    688                             final float secondPtrY = event.getY(1);
    689 
    690                             final float deltaX = firstPtrX - secondPtrX;
    691                             final float deltaY = firstPtrY - secondPtrY;
    692                             final double distance = Math.hypot(deltaX, deltaY);
    693 
    694                             if (distance > mScaledMinPointerDistanceToUseMiddleLocation) {
    695                                 event.setLocation(deltaX / 2, deltaY / 2);
    696                             }
    697 
    698                             // If still dragging send a drag event.
    699                             sendMotionEvent(event, MotionEvent.ACTION_MOVE, pointerIdBits,
    700                                     policyFlags);
    701                         } else {
    702                             // The two pointers are moving either in different directions or
    703                             // no close enough => delegate the gesture to the view hierarchy.
    704                             mCurrentState = STATE_DELEGATING;
    705                             // Send an event to the end of the drag gesture.
    706                             sendMotionEvent(event, MotionEvent.ACTION_UP, pointerIdBits,
    707                                     policyFlags);
    708                             // Deliver all pointers to the view hierarchy.
    709                             sendDownForAllNotInjectedPointers(event, policyFlags);
    710                         }
    711                     } break;
    712                     default: {
    713                         mCurrentState = STATE_DELEGATING;
    714                         // Send an event to the end of the drag gesture.
    715                         sendMotionEvent(event, MotionEvent.ACTION_UP, pointerIdBits,
    716                                 policyFlags);
    717                         // Deliver all pointers to the view hierarchy.
    718                         sendDownForAllNotInjectedPointers(event, policyFlags);
    719                     }
    720                 }
    721             } break;
    722             case MotionEvent.ACTION_POINTER_UP: {
    723                  final int pointerId = event.getPointerId(event.getActionIndex());
    724                  if (pointerId == mDraggingPointerId) {
    725                      mDraggingPointerId = INVALID_POINTER_ID;
    726                      // Send an event to the end of the drag gesture.
    727                      sendMotionEvent(event, MotionEvent.ACTION_UP, pointerIdBits, policyFlags);
    728                  }
    729             } break;
    730             case MotionEvent.ACTION_UP: {
    731                 mAms.onTouchInteractionEnd();
    732                 // Announce the end of a new touch interaction.
    733                 sendAccessibilityEvent(
    734                         AccessibilityEvent.TYPE_TOUCH_INTERACTION_END);
    735                 final int pointerId = event.getPointerId(event.getActionIndex());
    736                 if (pointerId == mDraggingPointerId) {
    737                     mDraggingPointerId = INVALID_POINTER_ID;
    738                     // Send an event to the end of the drag gesture.
    739                     sendMotionEvent(event, MotionEvent.ACTION_UP, pointerIdBits, policyFlags);
    740                 }
    741                 mCurrentState = STATE_TOUCH_EXPLORING;
    742             } break;
    743         }
    744     }
    745 
    746     /**
    747      * Handles a motion event in delegating state.
    748      *
    749      * @param event The event to be handled.
    750      * @param policyFlags The policy flags associated with the event.
    751      */
    752     private void handleMotionEventStateDelegating(MotionEvent event, int policyFlags) {
    753         switch (event.getActionMasked()) {
    754             case MotionEvent.ACTION_DOWN: {
    755                 throw new IllegalStateException("Delegating state can only be reached if "
    756                         + "there is at least one pointer down!");
    757             }
    758             case MotionEvent.ACTION_UP: {
    759                 // Offset the event if we are doing a long press as the
    760                 // target is not necessarily under the user's finger.
    761                 if (mLongPressingPointerId >= 0) {
    762                     event = offsetEvent(event, - mLongPressingPointerDeltaX,
    763                             - mLongPressingPointerDeltaY);
    764                     // Clear the long press state.
    765                     mLongPressingPointerId = -1;
    766                     mLongPressingPointerDeltaX = 0;
    767                     mLongPressingPointerDeltaY = 0;
    768                 }
    769 
    770                 // Deliver the event.
    771                 sendMotionEvent(event, event.getAction(), ALL_POINTER_ID_BITS, policyFlags);
    772 
    773                 // Announce the end of a the touch interaction.
    774                 mAms.onTouchInteractionEnd();
    775                 sendAccessibilityEvent(AccessibilityEvent.TYPE_TOUCH_INTERACTION_END);
    776 
    777                 mCurrentState = STATE_TOUCH_EXPLORING;
    778             } break;
    779             default: {
    780                 // Deliver the event.
    781                 sendMotionEvent(event, event.getAction(), ALL_POINTER_ID_BITS, policyFlags);
    782             }
    783         }
    784     }
    785 
    786     private void endGestureDetection() {
    787         mAms.onTouchInteractionEnd();
    788 
    789         // Announce the end of the gesture recognition.
    790         sendAccessibilityEvent(AccessibilityEvent.TYPE_GESTURE_DETECTION_END);
    791         // Announce the end of a the touch interaction.
    792         sendAccessibilityEvent(AccessibilityEvent.TYPE_TOUCH_INTERACTION_END);
    793 
    794         mExitGestureDetectionModeDelayed.cancel();
    795         mCurrentState = STATE_TOUCH_EXPLORING;
    796     }
    797 
    798     /**
    799      * Sends an accessibility event of the given type.
    800      *
    801      * @param type The event type.
    802      */
    803     private void sendAccessibilityEvent(int type) {
    804         AccessibilityManager accessibilityManager = AccessibilityManager.getInstance(mContext);
    805         if (accessibilityManager.isEnabled()) {
    806             AccessibilityEvent event = AccessibilityEvent.obtain(type);
    807             event.setWindowId(mAms.getActiveWindowId());
    808             accessibilityManager.sendAccessibilityEvent(event);
    809             switch (type) {
    810                 case AccessibilityEvent.TYPE_TOUCH_EXPLORATION_GESTURE_START: {
    811                     mTouchExplorationInProgress = true;
    812                 } break;
    813                 case AccessibilityEvent.TYPE_TOUCH_EXPLORATION_GESTURE_END: {
    814                     mTouchExplorationInProgress = false;
    815                 } break;
    816             }
    817         }
    818     }
    819 
    820     /**
    821      * Sends down events to the view hierarchy for all pointers which are
    822      * not already being delivered i.e. pointers that are not yet injected.
    823      *
    824      * @param prototype The prototype from which to create the injected events.
    825      * @param policyFlags The policy flags associated with the event.
    826      */
    827     private void sendDownForAllNotInjectedPointers(MotionEvent prototype, int policyFlags) {
    828         InjectedPointerTracker injectedPointers = mInjectedPointerTracker;
    829 
    830         // Inject the injected pointers.
    831         int pointerIdBits = 0;
    832         final int pointerCount = prototype.getPointerCount();
    833         for (int i = 0; i < pointerCount; i++) {
    834             final int pointerId = prototype.getPointerId(i);
    835             // Do not send event for already delivered pointers.
    836             if (!injectedPointers.isInjectedPointerDown(pointerId)) {
    837                 pointerIdBits |= (1 << pointerId);
    838                 final int action = computeInjectionAction(MotionEvent.ACTION_DOWN, i);
    839                 sendMotionEvent(prototype, action, pointerIdBits, policyFlags);
    840             }
    841         }
    842     }
    843 
    844     /**
    845      * Sends the exit events if needed. Such events are hover exit and touch explore
    846      * gesture end.
    847      *
    848      * @param policyFlags The policy flags associated with the event.
    849      */
    850     private void sendHoverExitAndTouchExplorationGestureEndIfNeeded(int policyFlags) {
    851         MotionEvent event = mInjectedPointerTracker.getLastInjectedHoverEvent();
    852         if (event != null && event.getActionMasked() != MotionEvent.ACTION_HOVER_EXIT) {
    853             final int pointerIdBits = event.getPointerIdBits();
    854             if (!mSendTouchExplorationEndDelayed.isPending()) {
    855                 mSendTouchExplorationEndDelayed.post();
    856             }
    857             sendMotionEvent(event, MotionEvent.ACTION_HOVER_EXIT, pointerIdBits, policyFlags);
    858         }
    859     }
    860 
    861     /**
    862      * Sends the enter events if needed. Such events are hover enter and touch explore
    863      * gesture start.
    864      *
    865      * @param policyFlags The policy flags associated with the event.
    866      */
    867     private void sendTouchExplorationGestureStartAndHoverEnterIfNeeded(int policyFlags) {
    868         MotionEvent event = mInjectedPointerTracker.getLastInjectedHoverEvent();
    869         if (event != null && event.getActionMasked() == MotionEvent.ACTION_HOVER_EXIT) {
    870             final int pointerIdBits = event.getPointerIdBits();
    871             sendAccessibilityEvent(AccessibilityEvent.TYPE_TOUCH_EXPLORATION_GESTURE_START);
    872             sendMotionEvent(event, MotionEvent.ACTION_HOVER_ENTER, pointerIdBits, policyFlags);
    873         }
    874     }
    875 
    876     /**
    877      * Sends up events to the view hierarchy for all pointers which are
    878      * already being delivered i.e. pointers that are injected.
    879      *
    880      * @param prototype The prototype from which to create the injected events.
    881      * @param policyFlags The policy flags associated with the event.
    882      */
    883     private void sendUpForInjectedDownPointers(MotionEvent prototype, int policyFlags) {
    884         final InjectedPointerTracker injectedTracked = mInjectedPointerTracker;
    885         int pointerIdBits = 0;
    886         final int pointerCount = prototype.getPointerCount();
    887         for (int i = 0; i < pointerCount; i++) {
    888             final int pointerId = prototype.getPointerId(i);
    889             // Skip non injected down pointers.
    890             if (!injectedTracked.isInjectedPointerDown(pointerId)) {
    891                 continue;
    892             }
    893             pointerIdBits |= (1 << pointerId);
    894             final int action = computeInjectionAction(MotionEvent.ACTION_UP, i);
    895             sendMotionEvent(prototype, action, pointerIdBits, policyFlags);
    896         }
    897     }
    898 
    899     /**
    900      * Sends an up and down events.
    901      *
    902      * @param prototype The prototype from which to create the injected events.
    903      * @param policyFlags The policy flags associated with the event.
    904      * @param targetAccessibilityFocus Whether the event targets the accessibility focus.
    905      */
    906     private void sendActionDownAndUp(MotionEvent prototype, int policyFlags,
    907             boolean targetAccessibilityFocus) {
    908         // Tap with the pointer that last explored.
    909         final int pointerId = prototype.getPointerId(prototype.getActionIndex());
    910         final int pointerIdBits = (1 << pointerId);
    911         prototype.setTargetAccessibilityFocus(targetAccessibilityFocus);
    912         sendMotionEvent(prototype, MotionEvent.ACTION_DOWN, pointerIdBits, policyFlags);
    913         prototype.setTargetAccessibilityFocus(targetAccessibilityFocus);
    914         sendMotionEvent(prototype, MotionEvent.ACTION_UP, pointerIdBits, policyFlags);
    915     }
    916 
    917     /**
    918      * Sends an event.
    919      *
    920      * @param prototype The prototype from which to create the injected events.
    921      * @param action The action of the event.
    922      * @param pointerIdBits The bits of the pointers to send.
    923      * @param policyFlags The policy flags associated with the event.
    924      */
    925     private void sendMotionEvent(MotionEvent prototype, int action, int pointerIdBits,
    926             int policyFlags) {
    927         prototype.setAction(action);
    928 
    929         MotionEvent event = null;
    930         if (pointerIdBits == ALL_POINTER_ID_BITS) {
    931             event = prototype;
    932         } else {
    933             event = prototype.split(pointerIdBits);
    934         }
    935         if (action == MotionEvent.ACTION_DOWN) {
    936             event.setDownTime(event.getEventTime());
    937         } else {
    938             event.setDownTime(mInjectedPointerTracker.getLastInjectedDownEventTime());
    939         }
    940 
    941         // If the user is long pressing but the long pressing pointer
    942         // was not exactly over the accessibility focused item we need
    943         // to remap the location of that pointer so the user does not
    944         // have to explicitly touch explore something to be able to
    945         // long press it, or even worse to avoid the user long pressing
    946         // on the wrong item since click and long press behave differently.
    947         if (mLongPressingPointerId >= 0) {
    948             event = offsetEvent(event, - mLongPressingPointerDeltaX,
    949                     - mLongPressingPointerDeltaY);
    950         }
    951 
    952         if (DEBUG) {
    953             Slog.d(LOG_TAG, "Injecting event: " + event + ", policyFlags=0x"
    954                     + Integer.toHexString(policyFlags));
    955         }
    956 
    957         // Make sure that the user will see the event.
    958         policyFlags |= WindowManagerPolicy.FLAG_PASS_TO_USER;
    959         if (mNext != null) {
    960             // TODO: For now pass null for the raw event since the touch
    961             //       explorer is the last event transformation and it does
    962             //       not care about the raw event.
    963             mNext.onMotionEvent(event, null, policyFlags);
    964         }
    965 
    966         mInjectedPointerTracker.onMotionEvent(event);
    967 
    968         if (event != prototype) {
    969             event.recycle();
    970         }
    971     }
    972 
    973     /**
    974      * Offsets all pointers in the given event by adding the specified X and Y
    975      * offsets.
    976      *
    977      * @param event The event to offset.
    978      * @param offsetX The X offset.
    979      * @param offsetY The Y offset.
    980      * @return An event with the offset pointers or the original event if both
    981      *         offsets are zero.
    982      */
    983     private MotionEvent offsetEvent(MotionEvent event, int offsetX, int offsetY) {
    984         if (offsetX == 0 && offsetY == 0) {
    985             return event;
    986         }
    987         final int remappedIndex = event.findPointerIndex(mLongPressingPointerId);
    988         final int pointerCount = event.getPointerCount();
    989         PointerProperties[] props = PointerProperties.createArray(pointerCount);
    990         PointerCoords[] coords = PointerCoords.createArray(pointerCount);
    991         for (int i = 0; i < pointerCount; i++) {
    992             event.getPointerProperties(i, props[i]);
    993             event.getPointerCoords(i, coords[i]);
    994             if (i == remappedIndex) {
    995                 coords[i].x += offsetX;
    996                 coords[i].y += offsetY;
    997             }
    998         }
    999         return MotionEvent.obtain(event.getDownTime(),
   1000                 event.getEventTime(), event.getAction(), event.getPointerCount(),
   1001                 props, coords, event.getMetaState(), event.getButtonState(),
   1002                 1.0f, 1.0f, event.getDeviceId(), event.getEdgeFlags(),
   1003                 event.getSource(), event.getFlags());
   1004     }
   1005 
   1006     /**
   1007      * Computes the action for an injected event based on a masked action
   1008      * and a pointer index.
   1009      *
   1010      * @param actionMasked The masked action.
   1011      * @param pointerIndex The index of the pointer which has changed.
   1012      * @return The action to be used for injection.
   1013      */
   1014     private int computeInjectionAction(int actionMasked, int pointerIndex) {
   1015         switch (actionMasked) {
   1016             case MotionEvent.ACTION_DOWN:
   1017             case MotionEvent.ACTION_POINTER_DOWN: {
   1018                 InjectedPointerTracker injectedTracker = mInjectedPointerTracker;
   1019                 // Compute the action based on how many down pointers are injected.
   1020                 if (injectedTracker.getInjectedPointerDownCount() == 0) {
   1021                     return MotionEvent.ACTION_DOWN;
   1022                 } else {
   1023                     return (pointerIndex << MotionEvent.ACTION_POINTER_INDEX_SHIFT)
   1024                         | MotionEvent.ACTION_POINTER_DOWN;
   1025                 }
   1026             }
   1027             case MotionEvent.ACTION_POINTER_UP: {
   1028                 InjectedPointerTracker injectedTracker = mInjectedPointerTracker;
   1029                 // Compute the action based on how many down pointers are injected.
   1030                 if (injectedTracker.getInjectedPointerDownCount() == 1) {
   1031                     return MotionEvent.ACTION_UP;
   1032                 } else {
   1033                     return (pointerIndex << MotionEvent.ACTION_POINTER_INDEX_SHIFT)
   1034                         | MotionEvent.ACTION_POINTER_UP;
   1035                 }
   1036             }
   1037             default:
   1038                 return actionMasked;
   1039         }
   1040     }
   1041 
   1042     /**
   1043      * Determines whether a two pointer gesture is a dragging one.
   1044      *
   1045      * @param event The event with the pointer data.
   1046      * @return True if the gesture is a dragging one.
   1047      */
   1048     private boolean isDraggingGesture(MotionEvent event) {
   1049         ReceivedPointerTracker receivedTracker = mReceivedPointerTracker;
   1050 
   1051         final float firstPtrX = event.getX(0);
   1052         final float firstPtrY = event.getY(0);
   1053         final float secondPtrX = event.getX(1);
   1054         final float secondPtrY = event.getY(1);
   1055 
   1056         final float firstPtrDownX = receivedTracker.getReceivedPointerDownX(0);
   1057         final float firstPtrDownY = receivedTracker.getReceivedPointerDownY(0);
   1058         final float secondPtrDownX = receivedTracker.getReceivedPointerDownX(1);
   1059         final float secondPtrDownY = receivedTracker.getReceivedPointerDownY(1);
   1060 
   1061         return GestureUtils.isDraggingGesture(firstPtrDownX, firstPtrDownY, secondPtrDownX,
   1062                 secondPtrDownY, firstPtrX, firstPtrY, secondPtrX, secondPtrY,
   1063                 MAX_DRAGGING_ANGLE_COS);
   1064     }
   1065 
   1066     private int computeClickLocation(Point outLocation) {
   1067         MotionEvent lastExploreEvent = mInjectedPointerTracker.getLastInjectedHoverEventForClick();
   1068         if (lastExploreEvent != null) {
   1069             final int lastExplorePointerIndex = lastExploreEvent.getActionIndex();
   1070             outLocation.x = (int) lastExploreEvent.getX(lastExplorePointerIndex);
   1071             outLocation.y = (int) lastExploreEvent.getY(lastExplorePointerIndex);
   1072             if (!mAms.accessibilityFocusOnlyInActiveWindow()
   1073                     || mLastTouchedWindowId == mAms.getActiveWindowId()) {
   1074                 if (mAms.getAccessibilityFocusClickPointInScreen(outLocation)) {
   1075                     return CLICK_LOCATION_ACCESSIBILITY_FOCUS;
   1076                 } else {
   1077                     return CLICK_LOCATION_LAST_TOUCH_EXPLORED;
   1078                 }
   1079             }
   1080         }
   1081         if (mAms.getAccessibilityFocusClickPointInScreen(outLocation)) {
   1082             return CLICK_LOCATION_ACCESSIBILITY_FOCUS;
   1083         }
   1084         return CLICK_LOCATION_NONE;
   1085     }
   1086 
   1087     /**
   1088      * Gets the symbolic name of a state.
   1089      *
   1090      * @param state A state.
   1091      * @return The state symbolic name.
   1092      */
   1093     private static String getStateSymbolicName(int state) {
   1094         switch (state) {
   1095             case STATE_TOUCH_EXPLORING:
   1096                 return "STATE_TOUCH_EXPLORING";
   1097             case STATE_DRAGGING:
   1098                 return "STATE_DRAGGING";
   1099             case STATE_DELEGATING:
   1100                 return "STATE_DELEGATING";
   1101             case STATE_GESTURE_DETECTING:
   1102                 return "STATE_GESTURE_DETECTING";
   1103             default:
   1104                 throw new IllegalArgumentException("Unknown state: " + state);
   1105         }
   1106     }
   1107 
   1108     /**
   1109      * Class for delayed exiting from gesture detecting mode.
   1110      */
   1111     private final class ExitGestureDetectionModeDelayed implements Runnable {
   1112 
   1113         public void post() {
   1114             mHandler.postDelayed(this, EXIT_GESTURE_DETECTION_TIMEOUT);
   1115         }
   1116 
   1117         public void cancel() {
   1118             mHandler.removeCallbacks(this);
   1119         }
   1120 
   1121         @Override
   1122         public void run() {
   1123             // Announce the end of gesture recognition.
   1124             sendAccessibilityEvent(AccessibilityEvent.TYPE_GESTURE_DETECTION_END);
   1125             // Clearing puts is in touch exploration state with a finger already
   1126             // down, so announce the transition to exploration state.
   1127             sendAccessibilityEvent(AccessibilityEvent.TYPE_TOUCH_EXPLORATION_GESTURE_START);
   1128             clear();
   1129         }
   1130     }
   1131 
   1132     /**
   1133      * Class for delayed sending of hover enter and move events.
   1134      */
   1135     class SendHoverEnterAndMoveDelayed implements Runnable {
   1136         private final String LOG_TAG_SEND_HOVER_DELAYED = "SendHoverEnterAndMoveDelayed";
   1137 
   1138         private final List<MotionEvent> mEvents = new ArrayList<MotionEvent>();
   1139 
   1140         private int mPointerIdBits;
   1141         private int mPolicyFlags;
   1142 
   1143         public void post(MotionEvent event, boolean touchExplorationInProgress,
   1144                 int pointerIdBits, int policyFlags) {
   1145             cancel();
   1146             addEvent(event);
   1147             mPointerIdBits = pointerIdBits;
   1148             mPolicyFlags = policyFlags;
   1149             mHandler.postDelayed(this, mDetermineUserIntentTimeout);
   1150         }
   1151 
   1152         public void addEvent(MotionEvent event) {
   1153             mEvents.add(MotionEvent.obtain(event));
   1154         }
   1155 
   1156         public void cancel() {
   1157             if (isPending()) {
   1158                 mHandler.removeCallbacks(this);
   1159                 clear();
   1160             }
   1161         }
   1162 
   1163         private boolean isPending() {
   1164             return mHandler.hasCallbacks(this);
   1165         }
   1166 
   1167         private void clear() {
   1168             mPointerIdBits = -1;
   1169             mPolicyFlags = 0;
   1170             final int eventCount = mEvents.size();
   1171             for (int i = eventCount - 1; i >= 0; i--) {
   1172                 mEvents.remove(i).recycle();
   1173             }
   1174         }
   1175 
   1176         public void forceSendAndRemove() {
   1177             if (isPending()) {
   1178                 run();
   1179                 cancel();
   1180             }
   1181         }
   1182 
   1183         public void run() {
   1184             // Send an accessibility event to announce the touch exploration start.
   1185             sendAccessibilityEvent(AccessibilityEvent.TYPE_TOUCH_EXPLORATION_GESTURE_START);
   1186 
   1187             if (!mEvents.isEmpty()) {
   1188                 // Deliver a down event.
   1189                 sendMotionEvent(mEvents.get(0), MotionEvent.ACTION_HOVER_ENTER,
   1190                         mPointerIdBits, mPolicyFlags);
   1191                 if (DEBUG) {
   1192                     Slog.d(LOG_TAG_SEND_HOVER_DELAYED,
   1193                             "Injecting motion event: ACTION_HOVER_ENTER");
   1194                 }
   1195 
   1196                 // Deliver move events.
   1197                 final int eventCount = mEvents.size();
   1198                 for (int i = 1; i < eventCount; i++) {
   1199                     sendMotionEvent(mEvents.get(i), MotionEvent.ACTION_HOVER_MOVE,
   1200                             mPointerIdBits, mPolicyFlags);
   1201                     if (DEBUG) {
   1202                         Slog.d(LOG_TAG_SEND_HOVER_DELAYED,
   1203                                 "Injecting motion event: ACTION_HOVER_MOVE");
   1204                     }
   1205                 }
   1206             }
   1207             clear();
   1208         }
   1209     }
   1210 
   1211     /**
   1212      * Class for delayed sending of hover exit events.
   1213      */
   1214     class SendHoverExitDelayed implements Runnable {
   1215         private final String LOG_TAG_SEND_HOVER_DELAYED = "SendHoverExitDelayed";
   1216 
   1217         private MotionEvent mPrototype;
   1218         private int mPointerIdBits;
   1219         private int mPolicyFlags;
   1220 
   1221         public void post(MotionEvent prototype, int pointerIdBits, int policyFlags) {
   1222             cancel();
   1223             mPrototype = MotionEvent.obtain(prototype);
   1224             mPointerIdBits = pointerIdBits;
   1225             mPolicyFlags = policyFlags;
   1226             mHandler.postDelayed(this, mDetermineUserIntentTimeout);
   1227         }
   1228 
   1229         public void cancel() {
   1230             if (isPending()) {
   1231                 mHandler.removeCallbacks(this);
   1232                 clear();
   1233             }
   1234         }
   1235 
   1236         private boolean isPending() {
   1237             return mHandler.hasCallbacks(this);
   1238         }
   1239 
   1240         private void clear() {
   1241             mPrototype.recycle();
   1242             mPrototype = null;
   1243             mPointerIdBits = -1;
   1244             mPolicyFlags = 0;
   1245         }
   1246 
   1247         public void forceSendAndRemove() {
   1248             if (isPending()) {
   1249                 run();
   1250                 cancel();
   1251             }
   1252         }
   1253 
   1254         public void run() {
   1255             if (DEBUG) {
   1256                 Slog.d(LOG_TAG_SEND_HOVER_DELAYED, "Injecting motion event:"
   1257                         + " ACTION_HOVER_EXIT");
   1258             }
   1259             sendMotionEvent(mPrototype, MotionEvent.ACTION_HOVER_EXIT,
   1260                     mPointerIdBits, mPolicyFlags);
   1261             if (!mSendTouchExplorationEndDelayed.isPending()) {
   1262                 mSendTouchExplorationEndDelayed.cancel();
   1263                 mSendTouchExplorationEndDelayed.post();
   1264             }
   1265             if (mSendTouchInteractionEndDelayed.isPending()) {
   1266                   mSendTouchInteractionEndDelayed.cancel();
   1267                 mSendTouchInteractionEndDelayed.post();
   1268             }
   1269             clear();
   1270         }
   1271     }
   1272 
   1273     private class SendAccessibilityEventDelayed implements Runnable {
   1274         private final int mEventType;
   1275         private final int mDelay;
   1276 
   1277         public SendAccessibilityEventDelayed(int eventType, int delay) {
   1278             mEventType = eventType;
   1279             mDelay = delay;
   1280         }
   1281 
   1282         public void cancel() {
   1283             mHandler.removeCallbacks(this);
   1284         }
   1285 
   1286         public void post() {
   1287             mHandler.postDelayed(this, mDelay);
   1288         }
   1289 
   1290         public boolean isPending() {
   1291             return mHandler.hasCallbacks(this);
   1292         }
   1293 
   1294         public void forceSendAndRemove() {
   1295             if (isPending()) {
   1296                 run();
   1297                 cancel();
   1298             }
   1299         }
   1300 
   1301         @Override
   1302         public void run() {
   1303             sendAccessibilityEvent(mEventType);
   1304         }
   1305     }
   1306 
   1307     @Override
   1308     public String toString() {
   1309         return LOG_TAG;
   1310     }
   1311 
   1312     class InjectedPointerTracker {
   1313         private static final String LOG_TAG_INJECTED_POINTER_TRACKER = "InjectedPointerTracker";
   1314 
   1315         // Keep track of which pointers sent to the system are down.
   1316         private int mInjectedPointersDown;
   1317 
   1318         // The time of the last injected down.
   1319         private long mLastInjectedDownEventTime;
   1320 
   1321         // The last injected hover event.
   1322         private MotionEvent mLastInjectedHoverEvent;
   1323 
   1324         // The last injected hover event used for performing clicks.
   1325         private MotionEvent mLastInjectedHoverEventForClick;
   1326 
   1327         /**
   1328          * Processes an injected {@link MotionEvent} event.
   1329          *
   1330          * @param event The event to process.
   1331          */
   1332         public void onMotionEvent(MotionEvent event) {
   1333             final int action = event.getActionMasked();
   1334             switch (action) {
   1335                 case MotionEvent.ACTION_DOWN:
   1336                 case MotionEvent.ACTION_POINTER_DOWN: {
   1337                     final int pointerId = event.getPointerId(event.getActionIndex());
   1338                     final int pointerFlag = (1 << pointerId);
   1339                     mInjectedPointersDown |= pointerFlag;
   1340                     mLastInjectedDownEventTime = event.getDownTime();
   1341                 } break;
   1342                 case MotionEvent.ACTION_UP:
   1343                 case MotionEvent.ACTION_POINTER_UP: {
   1344                     final int pointerId = event.getPointerId(event.getActionIndex());
   1345                     final int pointerFlag = (1 << pointerId);
   1346                     mInjectedPointersDown &= ~pointerFlag;
   1347                     if (mInjectedPointersDown == 0) {
   1348                         mLastInjectedDownEventTime = 0;
   1349                     }
   1350                 } break;
   1351                 case MotionEvent.ACTION_HOVER_ENTER:
   1352                 case MotionEvent.ACTION_HOVER_MOVE:
   1353                 case MotionEvent.ACTION_HOVER_EXIT: {
   1354                     if (mLastInjectedHoverEvent != null) {
   1355                         mLastInjectedHoverEvent.recycle();
   1356                     }
   1357                     mLastInjectedHoverEvent = MotionEvent.obtain(event);
   1358                     if (mLastInjectedHoverEventForClick != null) {
   1359                         mLastInjectedHoverEventForClick.recycle();
   1360                     }
   1361                     mLastInjectedHoverEventForClick = MotionEvent.obtain(event);
   1362                 } break;
   1363             }
   1364             if (DEBUG) {
   1365                 Slog.i(LOG_TAG_INJECTED_POINTER_TRACKER, "Injected pointer:\n" + toString());
   1366             }
   1367         }
   1368 
   1369         /**
   1370          * Clears the internals state.
   1371          */
   1372         public void clear() {
   1373             mInjectedPointersDown = 0;
   1374         }
   1375 
   1376         /**
   1377          * @return The time of the last injected down event.
   1378          */
   1379         public long getLastInjectedDownEventTime() {
   1380             return mLastInjectedDownEventTime;
   1381         }
   1382 
   1383         /**
   1384          * @return The number of down pointers injected to the view hierarchy.
   1385          */
   1386         public int getInjectedPointerDownCount() {
   1387             return Integer.bitCount(mInjectedPointersDown);
   1388         }
   1389 
   1390         /**
   1391          * @return The bits of the injected pointers that are down.
   1392          */
   1393         public int getInjectedPointersDown() {
   1394             return mInjectedPointersDown;
   1395         }
   1396 
   1397         /**
   1398          * Whether an injected pointer is down.
   1399          *
   1400          * @param pointerId The unique pointer id.
   1401          * @return True if the pointer is down.
   1402          */
   1403         public boolean isInjectedPointerDown(int pointerId) {
   1404             final int pointerFlag = (1 << pointerId);
   1405             return (mInjectedPointersDown & pointerFlag) != 0;
   1406         }
   1407 
   1408         /**
   1409          * @return The the last injected hover event.
   1410          */
   1411         public MotionEvent getLastInjectedHoverEvent() {
   1412             return mLastInjectedHoverEvent;
   1413         }
   1414 
   1415         /**
   1416          * @return The the last injected hover event.
   1417          */
   1418         public MotionEvent getLastInjectedHoverEventForClick() {
   1419             return mLastInjectedHoverEventForClick;
   1420         }
   1421 
   1422         @Override
   1423         public String toString() {
   1424             StringBuilder builder = new StringBuilder();
   1425             builder.append("=========================");
   1426             builder.append("\nDown pointers #");
   1427             builder.append(Integer.bitCount(mInjectedPointersDown));
   1428             builder.append(" [ ");
   1429             for (int i = 0; i < MAX_POINTER_COUNT; i++) {
   1430                 if ((mInjectedPointersDown & i) != 0) {
   1431                     builder.append(i);
   1432                     builder.append(" ");
   1433                 }
   1434             }
   1435             builder.append("]");
   1436             builder.append("\n=========================");
   1437             return builder.toString();
   1438         }
   1439     }
   1440 
   1441     class ReceivedPointerTracker {
   1442         private static final String LOG_TAG_RECEIVED_POINTER_TRACKER = "ReceivedPointerTracker";
   1443 
   1444         // Keep track of where and when a pointer went down.
   1445         private final float[] mReceivedPointerDownX = new float[MAX_POINTER_COUNT];
   1446         private final float[] mReceivedPointerDownY = new float[MAX_POINTER_COUNT];
   1447         private final long[] mReceivedPointerDownTime = new long[MAX_POINTER_COUNT];
   1448 
   1449         // Which pointers are down.
   1450         private int mReceivedPointersDown;
   1451 
   1452         // The edge flags of the last received down event.
   1453         private int mLastReceivedDownEdgeFlags;
   1454 
   1455         // Primary pointer which is either the first that went down
   1456         // or if it goes up the next one that most recently went down.
   1457         private int mPrimaryPointerId;
   1458 
   1459         // Keep track of the last up pointer data.
   1460         private long mLastReceivedUpPointerDownTime;
   1461         private float mLastReceivedUpPointerDownX;
   1462         private float mLastReceivedUpPointerDownY;
   1463 
   1464         private MotionEvent mLastReceivedEvent;
   1465 
   1466         /**
   1467          * Clears the internals state.
   1468          */
   1469         public void clear() {
   1470             Arrays.fill(mReceivedPointerDownX, 0);
   1471             Arrays.fill(mReceivedPointerDownY, 0);
   1472             Arrays.fill(mReceivedPointerDownTime, 0);
   1473             mReceivedPointersDown = 0;
   1474             mPrimaryPointerId = 0;
   1475             mLastReceivedUpPointerDownTime = 0;
   1476             mLastReceivedUpPointerDownX = 0;
   1477             mLastReceivedUpPointerDownY = 0;
   1478         }
   1479 
   1480         /**
   1481          * Processes a received {@link MotionEvent} event.
   1482          *
   1483          * @param event The event to process.
   1484          */
   1485         public void onMotionEvent(MotionEvent event) {
   1486             if (mLastReceivedEvent != null) {
   1487                 mLastReceivedEvent.recycle();
   1488             }
   1489             mLastReceivedEvent = MotionEvent.obtain(event);
   1490 
   1491             final int action = event.getActionMasked();
   1492             switch (action) {
   1493                 case MotionEvent.ACTION_DOWN: {
   1494                     handleReceivedPointerDown(event.getActionIndex(), event);
   1495                 } break;
   1496                 case MotionEvent.ACTION_POINTER_DOWN: {
   1497                     handleReceivedPointerDown(event.getActionIndex(), event);
   1498                 } break;
   1499                 case MotionEvent.ACTION_UP: {
   1500                     handleReceivedPointerUp(event.getActionIndex(), event);
   1501                 } break;
   1502                 case MotionEvent.ACTION_POINTER_UP: {
   1503                     handleReceivedPointerUp(event.getActionIndex(), event);
   1504                 } break;
   1505             }
   1506             if (DEBUG) {
   1507                 Slog.i(LOG_TAG_RECEIVED_POINTER_TRACKER, "Received pointer:\n" + toString());
   1508             }
   1509         }
   1510 
   1511         /**
   1512          * @return The last received event.
   1513          */
   1514         public MotionEvent getLastReceivedEvent() {
   1515             return mLastReceivedEvent;
   1516         }
   1517 
   1518         /**
   1519          * @return The number of received pointers that are down.
   1520          */
   1521         public int getReceivedPointerDownCount() {
   1522             return Integer.bitCount(mReceivedPointersDown);
   1523         }
   1524 
   1525         /**
   1526          * Whether an received pointer is down.
   1527          *
   1528          * @param pointerId The unique pointer id.
   1529          * @return True if the pointer is down.
   1530          */
   1531         public boolean isReceivedPointerDown(int pointerId) {
   1532             final int pointerFlag = (1 << pointerId);
   1533             return (mReceivedPointersDown & pointerFlag) != 0;
   1534         }
   1535 
   1536         /**
   1537          * @param pointerId The unique pointer id.
   1538          * @return The X coordinate where the pointer went down.
   1539          */
   1540         public float getReceivedPointerDownX(int pointerId) {
   1541             return mReceivedPointerDownX[pointerId];
   1542         }
   1543 
   1544         /**
   1545          * @param pointerId The unique pointer id.
   1546          * @return The Y coordinate where the pointer went down.
   1547          */
   1548         public float getReceivedPointerDownY(int pointerId) {
   1549             return mReceivedPointerDownY[pointerId];
   1550         }
   1551 
   1552         /**
   1553          * @param pointerId The unique pointer id.
   1554          * @return The time when the pointer went down.
   1555          */
   1556         public long getReceivedPointerDownTime(int pointerId) {
   1557             return mReceivedPointerDownTime[pointerId];
   1558         }
   1559 
   1560         /**
   1561          * @return The id of the primary pointer.
   1562          */
   1563         public int getPrimaryPointerId() {
   1564             if (mPrimaryPointerId == INVALID_POINTER_ID) {
   1565                 mPrimaryPointerId = findPrimaryPointerId();
   1566             }
   1567             return mPrimaryPointerId;
   1568         }
   1569 
   1570         /**
   1571          * @return The time when the last up received pointer went down.
   1572          */
   1573         public long getLastReceivedUpPointerDownTime() {
   1574             return mLastReceivedUpPointerDownTime;
   1575         }
   1576 
   1577         /**
   1578          * @return The down X of the last received pointer that went up.
   1579          */
   1580         public float getLastReceivedUpPointerDownX() {
   1581             return mLastReceivedUpPointerDownX;
   1582         }
   1583 
   1584         /**
   1585          * @return The down Y of the last received pointer that went up.
   1586          */
   1587         public float getLastReceivedUpPointerDownY() {
   1588             return mLastReceivedUpPointerDownY;
   1589         }
   1590 
   1591         /**
   1592          * @return The edge flags of the last received down event.
   1593          */
   1594         public int getLastReceivedDownEdgeFlags() {
   1595             return mLastReceivedDownEdgeFlags;
   1596         }
   1597 
   1598         /**
   1599          * Handles a received pointer down event.
   1600          *
   1601          * @param pointerIndex The index of the pointer that has changed.
   1602          * @param event The event to be handled.
   1603          */
   1604         private void handleReceivedPointerDown(int pointerIndex, MotionEvent event) {
   1605             final int pointerId = event.getPointerId(pointerIndex);
   1606             final int pointerFlag = (1 << pointerId);
   1607 
   1608             mLastReceivedUpPointerDownTime = 0;
   1609             mLastReceivedUpPointerDownX = 0;
   1610             mLastReceivedUpPointerDownX = 0;
   1611 
   1612             mLastReceivedDownEdgeFlags = event.getEdgeFlags();
   1613 
   1614             mReceivedPointersDown |= pointerFlag;
   1615             mReceivedPointerDownX[pointerId] = event.getX(pointerIndex);
   1616             mReceivedPointerDownY[pointerId] = event.getY(pointerIndex);
   1617             mReceivedPointerDownTime[pointerId] = event.getEventTime();
   1618 
   1619             mPrimaryPointerId = pointerId;
   1620         }
   1621 
   1622         /**
   1623          * Handles a received pointer up event.
   1624          *
   1625          * @param pointerIndex The index of the pointer that has changed.
   1626          * @param event The event to be handled.
   1627          */
   1628         private void handleReceivedPointerUp(int pointerIndex, MotionEvent event) {
   1629             final int pointerId = event.getPointerId(pointerIndex);
   1630             final int pointerFlag = (1 << pointerId);
   1631 
   1632             mLastReceivedUpPointerDownTime = getReceivedPointerDownTime(pointerId);
   1633             mLastReceivedUpPointerDownX = mReceivedPointerDownX[pointerId];
   1634             mLastReceivedUpPointerDownY = mReceivedPointerDownY[pointerId];
   1635 
   1636             mReceivedPointersDown &= ~pointerFlag;
   1637             mReceivedPointerDownX[pointerId] = 0;
   1638             mReceivedPointerDownY[pointerId] = 0;
   1639             mReceivedPointerDownTime[pointerId] = 0;
   1640 
   1641             if (mPrimaryPointerId == pointerId) {
   1642                 mPrimaryPointerId = INVALID_POINTER_ID;
   1643             }
   1644         }
   1645 
   1646         /**
   1647          * @return The primary pointer id.
   1648          */
   1649         private int findPrimaryPointerId() {
   1650             int primaryPointerId = INVALID_POINTER_ID;
   1651             long minDownTime = Long.MAX_VALUE;
   1652 
   1653             // Find the pointer that went down first.
   1654             int pointerIdBits = mReceivedPointersDown;
   1655             while (pointerIdBits > 0) {
   1656                 final int pointerId = Integer.numberOfTrailingZeros(pointerIdBits);
   1657                 pointerIdBits &= ~(1 << pointerId);
   1658                 final long downPointerTime = mReceivedPointerDownTime[pointerId];
   1659                 if (downPointerTime < minDownTime) {
   1660                     minDownTime = downPointerTime;
   1661                     primaryPointerId = pointerId;
   1662                 }
   1663             }
   1664             return primaryPointerId;
   1665         }
   1666 
   1667         @Override
   1668         public String toString() {
   1669             StringBuilder builder = new StringBuilder();
   1670             builder.append("=========================");
   1671             builder.append("\nDown pointers #");
   1672             builder.append(getReceivedPointerDownCount());
   1673             builder.append(" [ ");
   1674             for (int i = 0; i < MAX_POINTER_COUNT; i++) {
   1675                 if (isReceivedPointerDown(i)) {
   1676                     builder.append(i);
   1677                     builder.append(" ");
   1678                 }
   1679             }
   1680             builder.append("]");
   1681             builder.append("\nPrimary pointer id [ ");
   1682             builder.append(getPrimaryPointerId());
   1683             builder.append(" ]");
   1684             builder.append("\n=========================");
   1685             return builder.toString();
   1686         }
   1687     }
   1688 }
   1689