public boolean onTouchEvent(MotionEvent me) {
   // If there was a sudden jump, return without processing the actual motion event.
   if (handleSuddenJumping(me)) {
     if (DEBUG_MODE) Log.w(TAG, "onTouchEvent: ignore sudden jump " + me);
     if (ProductionFlag.IS_EXPERIMENTAL) {
       ResearchLogger.suddenJumpingTouchEventHandler_onTouchEvent(me);
     }
     return true;
   }
   return mView.processMotionEvent(me);
 }
 public boolean onTouchEvent(final MotionEvent me) {
   // If there was a sudden jump, return without processing the actual motion event.
   if (handleSuddenJumping(me)) {
     if (DEBUG_MODE) Log.w(TAG, "onTouchEvent: ignore sudden jump " + me);
     if (ProductionFlag.USES_DEVELOPMENT_ONLY_DIAGNOSTICS) {
       ResearchLogger.suddenJumpingTouchEventHandler_onTouchEvent(me);
     }
     return true;
   }
   return mView.processMotionEvent(me);
 }
  /**
   * This function checks to see if we need to handle any sudden jumps in the pointer location that
   * could be due to a multi-touch being treated as a move by the firmware or hardware. Once a
   * sudden jump is detected, all subsequent move events are discarded until an UP is received.
   *
   * <p>When a sudden jump is detected, an UP event is simulated at the last position and when the
   * sudden moves subside, a DOWN event is simulated for the second key.
   *
   * @param me the motion event
   * @return true if the event was consumed, so that it doesn't continue to be handled by {@link
   *     LatinKeyboardView}.
   */
  private boolean handleSuddenJumping(MotionEvent me) {
    if (!mNeedsSuddenJumpingHack) return false;
    final int action = me.getAction();
    final int x = (int) me.getX();
    final int y = (int) me.getY();
    boolean result = false;

    // Real multi-touch event? Stop looking for sudden jumps
    if (me.getPointerCount() > 1) {
      mDisableDisambiguation = true;
    }
    if (mDisableDisambiguation) {
      // If UP, reset the multi-touch flag
      if (action == MotionEvent.ACTION_UP) mDisableDisambiguation = false;
      return false;
    }

    switch (action) {
      case MotionEvent.ACTION_DOWN:
        // Reset the "session"
        mDroppingEvents = false;
        mDisableDisambiguation = false;
        break;
      case MotionEvent.ACTION_MOVE:
        // Is this a big jump?
        final int distanceSquare = (mLastX - x) * (mLastX - x) + (mLastY - y) * (mLastY - y);
        // Check the distance.
        if (distanceSquare > mJumpThresholdSquare) {
          // If we're not yet dropping events, start dropping and send an UP event
          if (!mDroppingEvents) {
            mDroppingEvents = true;
            // Send an up event
            MotionEvent translated =
                MotionEvent.obtain(
                    me.getEventTime(),
                    me.getEventTime(),
                    MotionEvent.ACTION_UP,
                    mLastX,
                    mLastY,
                    me.getMetaState());
            mView.processMotionEvent(translated);
            translated.recycle();
          }
          result = true;
        } else if (mDroppingEvents) {
          // If moves are small and we're already dropping events, continue dropping
          result = true;
        }
        break;
      case MotionEvent.ACTION_UP:
        if (mDroppingEvents) {
          // Send a down event first, as we dropped a bunch of sudden jumps and assume that
          // the user is releasing the touch on the second key.
          MotionEvent translated =
              MotionEvent.obtain(
                  me.getEventTime(),
                  me.getEventTime(),
                  MotionEvent.ACTION_DOWN,
                  x,
                  y,
                  me.getMetaState());
          mView.processMotionEvent(translated);
          translated.recycle();
          mDroppingEvents = false;
          // Let the up event get processed as well, result = false
        }
        break;
    }
    // Track the previous coordinate
    mLastX = x;
    mLastY = y;
    return result;
  }