private void postClick() {
    synchronized (mLock) {
      if (!mPostClickScheduled) {
        return;
      }
      mPostClickScheduled = false;

      MotionEvent event = mPostTouchStream.getLastEvent();
      if (event == null || event.getAction() != MotionEvent.ACTION_UP) {
        return;
      }

      showTapCandidateLocked();
      MotionEvent eventToEnqueue = MotionEvent.obtainNoHistory(event);
      DispatchEvent d =
          obtainDispatchEventLocked(
              eventToEnqueue,
              EVENT_TYPE_CLICK,
              0,
              mPostLastWebKitXOffset,
              mPostLastWebKitYOffset,
              mPostLastWebKitScale);
      enqueueEventLocked(d);
    }
  }
  private void postLongPress() {
    synchronized (mLock) {
      if (!mPostLongPressScheduled) {
        return;
      }
      mPostLongPressScheduled = false;

      MotionEvent event = mPostTouchStream.getLastEvent();
      if (event == null) {
        return;
      }

      switch (event.getActionMasked()) {
        case MotionEvent.ACTION_DOWN:
        case MotionEvent.ACTION_MOVE:
        case MotionEvent.ACTION_POINTER_DOWN:
        case MotionEvent.ACTION_POINTER_UP:
          break;
        default:
          return;
      }

      MotionEvent eventToEnqueue = MotionEvent.obtainNoHistory(event);
      eventToEnqueue.setAction(MotionEvent.ACTION_MOVE);
      DispatchEvent d =
          obtainDispatchEventLocked(
              eventToEnqueue,
              EVENT_TYPE_LONG_PRESS,
              0,
              mPostLastWebKitXOffset,
              mPostLastWebKitYOffset,
              mPostLastWebKitScale);
      enqueueEventLocked(d);
    }
  }
 private void enqueueUiCancelTouchEventIfNeededLocked() {
   // We want to cancel touch events that were delivered to the UI.
   // Enqueue a null event at the end of the queue if needed.
   if (mUiTouchStream.isCancelNeeded() || !mUiDispatchEventQueue.isEmpty()) {
     DispatchEvent d = obtainDispatchEventLocked(null, EVENT_TYPE_TOUCH, FLAG_PRIVATE, 0, 0, 1.0f);
     enqueueUiEventUnbatchedLocked(d);
   }
 }
 private void enqueueWebKitCancelTouchEventIfNeededLocked() {
   // We want to cancel touch events that were delivered to web kit.
   // Enqueue a null event at the end of the queue if needed.
   if (mWebKitTouchStream.isCancelNeeded() || !mWebKitDispatchEventQueue.isEmpty()) {
     DispatchEvent d = obtainDispatchEventLocked(null, EVENT_TYPE_TOUCH, FLAG_PRIVATE, 0, 0, 1.0f);
     enqueueWebKitEventUnbatchedLocked(d);
     mPostDoNotSendTouchEventsToWebKitUntilNextGesture = true;
   }
 }
  private void dispatchUiEvents(boolean calledFromHandler) {
    for (; ; ) {
      MotionEvent event;
      final int eventType;
      final int flags;
      synchronized (mLock) {
        DispatchEvent d = mUiDispatchEventQueue.dequeue();
        if (d == null) {
          if (mUiDispatchScheduled) {
            mUiDispatchScheduled = false;
            if (!calledFromHandler) {
              mUiHandler.removeMessages(UiHandler.MSG_DISPATCH_UI_EVENTS);
            }
          }
          return;
        }

        event = d.mEvent;
        if (event != null && (d.mFlags & FLAG_WEBKIT_TRANSFORMED_EVENT) != 0) {
          event.scale(1.0f / d.mWebKitScale);
          event.offsetLocation(-d.mWebKitXOffset, -d.mWebKitYOffset);
          d.mFlags &= ~FLAG_WEBKIT_TRANSFORMED_EVENT;
        }

        eventType = d.mEventType;
        if (eventType == EVENT_TYPE_TOUCH) {
          event = mUiTouchStream.update(event);
          if (DEBUG && event == null && d.mEvent != null) {
            Log.d(TAG, "dispatchUiEvents: dropped event " + d.mEvent);
          }
        }

        flags = d.mFlags;

        if (event == d.mEvent) {
          d.mEvent = null; // retain ownership of event, don't recycle it yet
        }
        recycleDispatchEventLocked(d);

        if (eventType == EVENT_TYPE_CLICK) {
          scheduleHideTapHighlightLocked();
        }
      }

      // Handle the event.
      if (event != null) {
        dispatchUiEvent(event, eventType, flags);
        event.recycle();
      }
    }
  }
  private void dispatchWebKitEvents(boolean calledFromHandler) {
    for (; ; ) {
      // Get the next event, but leave it in the queue so we can move it to the UI
      // queue if a timeout occurs.
      DispatchEvent d;
      MotionEvent event;
      final int eventType;
      int flags;
      synchronized (mLock) {
        if (!ENABLE_EVENT_BATCHING) {
          drainStaleWebKitEventsLocked();
        }
        d = mWebKitDispatchEventQueue.mHead;
        if (d == null) {
          if (mWebKitDispatchScheduled) {
            mWebKitDispatchScheduled = false;
            if (!calledFromHandler) {
              mWebKitHandler.removeMessages(WebKitHandler.MSG_DISPATCH_WEBKIT_EVENTS);
            }
          }
          return;
        }

        event = d.mEvent;
        if (event != null) {
          event.offsetLocation(d.mWebKitXOffset, d.mWebKitYOffset);
          event.scale(d.mWebKitScale);
          d.mFlags |= FLAG_WEBKIT_TRANSFORMED_EVENT;
        }

        eventType = d.mEventType;
        if (eventType == EVENT_TYPE_TOUCH) {
          event = mWebKitTouchStream.update(event);
          if (DEBUG && event == null && d.mEvent != null) {
            Log.d(TAG, "dispatchWebKitEvents: dropped event " + d.mEvent);
          }
        }

        d.mFlags |= FLAG_WEBKIT_IN_PROGRESS;
        flags = d.mFlags;
      }

      // Handle the event.
      final boolean preventDefault;
      if (event == null) {
        preventDefault = false;
      } else {
        preventDefault = dispatchWebKitEvent(event, eventType, flags);
      }

      synchronized (mLock) {
        /// M: update last event prevent status. @ {
        boolean lastPreventDefault = false;
        if (d.mEventType == EVENT_TYPE_TOUCH) {
          lastPreventDefault = mWebKitPreventTouchStream.update(event, preventDefault);
        }
        /// @ }
        flags = d.mFlags;
        d.mFlags = flags & ~FLAG_WEBKIT_IN_PROGRESS;
        boolean recycleEvent = event != d.mEvent;

        if ((flags & FLAG_WEBKIT_TIMEOUT) != 0) {
          // A timeout occurred!
          recycleDispatchEventLocked(d);
        } else {
          // Web kit finished in a timely manner.  Dequeue the event.
          assert mWebKitDispatchEventQueue.mHead == d;
          mWebKitDispatchEventQueue.dequeue();

          updateWebKitTimeoutLocked();

          if ((flags & FLAG_PRIVATE) != 0) {
            // Event was intended for web kit only.  All done.
            recycleDispatchEventLocked(d);
          } else if (preventDefault) {
            // Web kit has decided to consume the event!
            if (d.mEventType == EVENT_TYPE_TOUCH) {
              /// M: Consider two consistent events is preventDefault. @ {
              if (lastPreventDefault) {
                Log.d(TAG, "Webkit prevent current and last event, cancel ui event");
                enqueueUiCancelTouchEventIfNeededLocked();
              } else {
                Log.d(TAG, "Webkit prevent current but not last event, enqueue ui event");
                /// Only one event is preventDefault, Ui also handle this touch event.
                enqueueUiEventUnbatchedLocked(d);
              }
              unscheduleLongPressLocked();
              /// @ }
            }
          } else {
            // Web kit is being friendly.  Pass the event to the UI.
            enqueueUiEventUnbatchedLocked(d);
          }
        }

        if (event != null && recycleEvent) {
          event.recycle();
        }

        if (eventType == EVENT_TYPE_CLICK) {
          scheduleHideTapHighlightLocked();
        }
      }
    }
  }
  /**
   * Posts a pointer event to the dispatch queue.
   *
   * @param event The event to post.
   * @param webKitXOffset X offset to apply to events before dispatching them to web kit.
   * @param webKitYOffset Y offset to apply to events before dispatching them to web kit.
   * @param webKitScale The scale factor to apply to translated events before dispatching them to
   *     web kit.
   * @return True if the dispatcher will handle the event, false if the event is unsupported.
   */
  public boolean postPointerEvent(
      MotionEvent event, int webKitXOffset, int webKitYOffset, float webKitScale) {
    if (event == null) {
      throw new IllegalArgumentException("event cannot be null");
    }

    if (DEBUG) {
      Log.d(TAG, "postPointerEvent: " + event);
    }

    final int action = event.getActionMasked();
    final int eventType;
    switch (action) {
      case MotionEvent.ACTION_DOWN:
      case MotionEvent.ACTION_MOVE:
      case MotionEvent.ACTION_UP:
      case MotionEvent.ACTION_POINTER_DOWN:
      case MotionEvent.ACTION_POINTER_UP:
      case MotionEvent.ACTION_CANCEL:
        eventType = EVENT_TYPE_TOUCH;
        break;
      case MotionEvent.ACTION_SCROLL:
        eventType = EVENT_TYPE_SCROLL;
        break;
      case MotionEvent.ACTION_HOVER_ENTER:
      case MotionEvent.ACTION_HOVER_MOVE:
      case MotionEvent.ACTION_HOVER_EXIT:
        eventType = EVENT_TYPE_HOVER;
        break;
      default:
        return false; // currently unsupported event type
    }

    synchronized (mLock) {
      // Ensure that the event is consistent and should be delivered.
      MotionEvent eventToEnqueue = event;
      if (eventType == EVENT_TYPE_TOUCH) {
        eventToEnqueue = mPostTouchStream.update(event);
        if (eventToEnqueue == null) {
          if (DEBUG) {
            Log.d(TAG, "postPointerEvent: dropped event " + event);
          }
          unscheduleLongPressLocked();
          unscheduleClickLocked();
          hideTapCandidateLocked();
          return false;
        }

        if (action == MotionEvent.ACTION_DOWN && mPostSendTouchEventsToWebKit) {
          if (mUiCallbacks.shouldInterceptTouchEvent(eventToEnqueue)) {
            mPostDoNotSendTouchEventsToWebKitUntilNextGesture = true;
          } else if (mPostDoNotSendTouchEventsToWebKitUntilNextGesture) {
            // Recover from a previous web kit timeout.
            mPostDoNotSendTouchEventsToWebKitUntilNextGesture = false;
          }
        }
      }

      // Copy the event because we need to retain ownership.
      if (eventToEnqueue == event) {
        eventToEnqueue = event.copy();
      }

      DispatchEvent d =
          obtainDispatchEventLocked(
              eventToEnqueue, eventType, 0, webKitXOffset, webKitYOffset, webKitScale);
      updateStateTrackersLocked(d, event);
      enqueueEventLocked(d);
    }
    return true;
  }