private void reset() {
   if (mPrevEvent != null) {
     mPrevEvent.recycle();
     mPrevEvent = null;
   }
   if (mCurrEvent != null) {
     mCurrEvent.recycle();
     mCurrEvent = null;
   }
   mSloppyGesture = false;
   mGestureInProgress = false;
 }
  public void onDragOver(DragObject d) {
    final DragView dragView = d.dragView;
    final int scrollOffset = mScrollView.getScrollY();
    final float[] r = getDragViewVisualCenter(d.x, d.y, d.xOffset, d.yOffset, dragView, null);
    r[0] -= getPaddingLeft();
    r[1] -= getPaddingTop();

    final long downTime = SystemClock.uptimeMillis();
    final MotionEvent translatedEv =
        MotionEvent.obtain(downTime, downTime, MotionEvent.ACTION_MOVE, d.x, d.y, 0);

    if (!mAutoScrollHelper.isEnabled()) {
      mAutoScrollHelper.setEnabled(true);
    }

    final boolean handled = mAutoScrollHelper.onTouch(this, translatedEv);
    translatedEv.recycle();

    if (handled) {
      mReorderAlarm.cancelAlarm();
    } else {
      mTargetCell =
          mContent.findNearestArea((int) r[0], (int) r[1] + scrollOffset, 1, 1, mTargetCell);
      if (isLayoutRtl()) {
        mTargetCell[0] = mContent.getCountX() - mTargetCell[0] - 1;
      }
      if (mTargetCell[0] != mPreviousTargetCell[0] || mTargetCell[1] != mPreviousTargetCell[1]) {
        mReorderAlarm.cancelAlarm();
        mReorderAlarm.setOnAlarmListener(mReorderAlarmListener);
        mReorderAlarm.setAlarm(REORDER_DELAY);
        mPreviousTargetCell[0] = mTargetCell[0];
        mPreviousTargetCell[1] = mTargetCell[1];
      }
    }
  }
Esempio n. 3
0
  private boolean processTouch(MotionEvent event) {
    // Checking if that event was already processed
    // (by onInterceptTouchEvent prior to onTouchEvent)
    long eventTime = event.getEventTime();
    int action = event.getActionMasked();

    if (lastTouchEventTime == eventTime && lastTouchEventAction == action) {
      return lastTouchEventResult;
    }

    lastTouchEventTime = eventTime;
    lastTouchEventAction = action;

    if (getCount() > 0) {
      // Fixing event's Y position due to performed translation
      MotionEvent eventCopy = MotionEvent.obtain(event);
      eventCopy.offsetLocation(0, getTranslationY());
      lastTouchEventResult = gestureDetector.onTouchEvent(eventCopy);
      eventCopy.recycle();
    } else {
      lastTouchEventResult = false;
    }

    if (action == MotionEvent.ACTION_UP || action == MotionEvent.ACTION_CANCEL) {
      onUpOrCancel();
    }

    return lastTouchEventResult;
  }
  private void setContext(MotionEvent curr) {
    if (mCurrEvent != null) {
      mCurrEvent.recycle();
    }
    mCurrEvent = MotionEvent.obtain(curr);

    mCurrLen = -1;
    mPrevLen = -1;
    mScaleFactor = -1;

    final MotionEvent prev = mPrevEvent;

    final float px0 = prev.getX(0);
    final float py0 = prev.getY(0);
    final float px1 = prev.getX(1);
    final float py1 = prev.getY(1);
    final float cx0 = curr.getX(0);
    final float cy0 = curr.getY(0);
    final float cx1 = curr.getX(1);
    final float cy1 = curr.getY(1);

    final float pvx = px1 - px0;
    final float pvy = py1 - py0;
    final float cvx = cx1 - cx0;
    final float cvy = cy1 - cy0;
    mPrevFingerDiffX = pvx;
    mPrevFingerDiffY = pvy;
    mCurrFingerDiffX = cvx;
    mCurrFingerDiffY = cvy;

    mTimeDelta = curr.getEventTime() - prev.getEventTime();
    mCurrPressure = curr.getPressure(0) + curr.getPressure(1);
    mPrevPressure = prev.getPressure(0) + prev.getPressure(1);
  }
  @Override
  public boolean onTouchEvent(MotionEvent event) {
    final int action = event.getAction();
    final float x = event.getX();
    final float y = event.getY();

    switch (action) {
      case MotionEvent.ACTION_DOWN:
        if (mCurrentDownEvent != null) {
          mCurrentDownEvent.recycle();
        }
        mCurrentDownEvent = MotionEvent.obtain(event);
        mAlwaysInTapRegion = true;
        showPress(mCurrentDownEvent, true);
        break;
      case MotionEvent.ACTION_MOVE:
        if (mAlwaysInTapRegion) {
          final int deltaX = (int) (x - mCurrentDownEvent.getX());
          final int deltaY = (int) (y - mCurrentDownEvent.getY());
          int distance = (deltaX * deltaX) + (deltaY * deltaY);
          if (distance > mTouchSlopSquare) {
            mAlwaysInTapRegion = false;
            showPress(mCurrentDownEvent, false);
          }
        }
        break;
      case MotionEvent.ACTION_UP:
        showPress(mCurrentDownEvent, false);
        if (mAlwaysInTapRegion) {
          onTap(mCurrentDownEvent);
        }
    }

    return true;
  }
Esempio n. 6
0
  /** Force show keyboard */
  public void showKeyboard() {
    this.requestFocus();

    Context context = getContext();
    if (Activity.class.isInstance(context)) {
      ((Activity) context)
          .getWindow()
          .setSoftInputMode(WindowManager.LayoutParams.SOFT_INPUT_STATE_ALWAYS_VISIBLE);
    }

    // mImm.showSoftInput(this, InputMethodManager.SHOW_IMPLICIT | InputMethodManager.SHOW_FORCED);
    // mImm.toggleSoftInput(0, 0);

    // Trick used to create a fake touch event on the editText
    MotionEvent event =
        MotionEvent.obtain(
            0,
            SystemClock.uptimeMillis(),
            MotionEvent.ACTION_DOWN | MotionEvent.ACTION_UP,
            this.getMeasuredWidth(),
            0,
            0);
    this.onTouchEvent(event);
    event.recycle();
  }
Esempio n. 7
0
 public void init(MotionEvent e) {
   if (mInitialEvent != null) {
     mInitialEvent.recycle();
   }
   mInitialEvent = MotionEvent.obtain(e);
   mIsPending = true;
 }
Esempio n. 8
0
 public void dispatchPointer(MotionEvent event) {
   if (mEngine != null) {
     mEngine.dispatchPointer(event);
   } else {
     event.recycle();
   }
 }
 private void clearTouchTarget() {
   mTouchTarget = null;
   if (mDownEvent != null) {
     mDownEvent.recycle();
     mDownEvent = null;
   }
 }
Esempio n. 10
0
 public boolean dispatchTouchEvent(MotionEvent paramMotionEvent)
 {
   float f1 = paramMotionEvent.getRawX();
   float f2 = paramMotionEvent.getRawY();
   if ((this.f == null) && (this.a != null) && (this.a.getVisibility() == 0) && (this.b != null))
   {
     this.b.getLocationOnScreen(this.g);
     if ((f1 >= this.g[0]) && (f1 < this.g[0] + this.b.getWidth()) && (f2 >= this.g[1]) && (f2 < this.g[1] + this.b.getHeight())) {
       this.f = this.a;
     }
   }
   if ((this.f == null) && (this.a != null) && (this.a.getVisibility() == 0))
   {
     if ((this.d == null) || (this.d.getVisibility() != 0)) {
       break label269;
     }
     this.d.getLocationOnScreen(this.g);
     if (f2 >= this.g[1]) {
       this.f = this.a;
     }
     if (f2 >= this.g[1]) {
       this.f = this.a;
     }
   }
   int i = paramMotionEvent.getAction();
   if (this.f != null)
   {
     localView = this.f;
     if (paramMotionEvent.getAction() == 3)
     {
       localView.dispatchTouchEvent(paramMotionEvent);
       if ((i == 3) || (i == 1)) {
         this.f = null;
       }
     }
   }
   label269:
   while (this.e == null) {
     for (;;)
     {
       View localView;
       return true;
       if ((this.c != null) && (this.c.getVisibility() == 0))
       {
         this.c.getLocationOnScreen(this.g);
         break;
       }
       this.g[0] = 2147483647;
       this.g[1] = 2147483647;
       break;
       MotionEvent localMotionEvent = MotionEvent.obtain(paramMotionEvent);
       localMotionEvent.offsetLocation(getScrollX() - localView.getLeft(), getScrollY() - localView.getTop());
       localView.dispatchTouchEvent(localMotionEvent);
       localMotionEvent.recycle();
     }
   }
   this.e.a(i);
   return true;
 }
 private void clear() {
   mActionDownInside.recycle();
   mActionDownOutside.recycle();
   mActionUpInside.recycle();
   mActionUpOutside.recycle();
   mActionMoveInside.recycle();
   mActionMoveOutside.recycle();
   mActionCancelInside.recycle();
   mActionCancelOutside.recycle();
 }
  @Override
  public boolean dispatchTouchEvent(MotionEvent ev) {

    final float x = ev.getX();
    final float y = ev.getY();
    final int action = ev.getAction();

    if (action == MotionEvent.ACTION_DOWN
        && mTouchTarget == null
        && mPinnedSection != null
        && isPinnedViewTouched(mPinnedSection.view, x, y)) { // create touch target

      // user touched pinned view
      mTouchTarget = mPinnedSection.view;
      mTouchPoint.x = x;
      mTouchPoint.y = y;

      // copy down event for eventually be used later
      mDownEvent = MotionEvent.obtain(ev);
    }

    if (mTouchTarget != null) {
      if (isPinnedViewTouched(mTouchTarget, x, y)) { // forward event to pinned view
        mTouchTarget.dispatchTouchEvent(ev);
      }

      if (action == MotionEvent.ACTION_UP) { // perform onClick on pinned view
        super.dispatchTouchEvent(ev);
        performPinnedItemClick();
        clearTouchTarget();

      } else if (action == MotionEvent.ACTION_CANCEL) { // cancel
        clearTouchTarget();

      } else if (action == MotionEvent.ACTION_MOVE) {
        if (Math.abs(y - mTouchPoint.y) > mTouchSlop) {

          // cancel sequence on touch target
          MotionEvent event = MotionEvent.obtain(ev);
          event.setAction(MotionEvent.ACTION_CANCEL);
          mTouchTarget.dispatchTouchEvent(event);
          event.recycle();

          // provide correct sequence to super class for further handling
          super.dispatchTouchEvent(mDownEvent);
          super.dispatchTouchEvent(ev);
          clearTouchTarget();
        }
      }

      return true;
    }

    // call super if this was not our pinned view
    return super.dispatchTouchEvent(ev);
  }
Esempio n. 13
0
 private static void injectMotionEvent(
     @NonNull ViewGroup container, @NonNull MotionEvent base, int action) {
   long downTime = SystemClock.uptimeMillis();
   long eventTime = SystemClock.uptimeMillis() + 100;
   MotionEvent event =
       MotionEvent.obtain(
           downTime, eventTime, action, base.getX(), base.getY(), 0.0f, 0.0f, 0, 1.0f, 1.0f, 0, 0);
   container.dispatchTouchEvent(event);
   event.recycle();
 }
 private void removeOneComponent(GLView component) {
   if (mMotionTarget == component) {
     long now = SystemClock.uptimeMillis();
     MotionEvent cancelEvent = MotionEvent.obtain(now, now, MotionEvent.ACTION_CANCEL, 0, 0, 0);
     dispatchTouchEvent(cancelEvent);
     cancelEvent.recycle();
   }
   component.onDetachFromRoot();
   component.mParent = null;
 }
 @Override
 public boolean onTouchEvent(MotionEvent event) {
   final int action = event.getAction();
   boolean handled = false;
   switch (action) {
     case MotionEvent.ACTION_DOWN:
       mCurrPercentage = 0;
       mDownEvent = MotionEvent.obtain(event);
       mPrevY = mDownEvent.getY();
       break;
     case MotionEvent.ACTION_MOVE:
       if (mDownEvent != null && !mReturningToStart) {
         final float eventY = event.getY();
         float yDiff = eventY - mDownEvent.getY();
         if (yDiff > mTouchSlop) {
           // User velocity passed min velocity; trigger a refresh
           if (yDiff > mDistanceToTriggerSync) {
             // User movement passed distance; trigger a refresh
             startRefresh();
             handled = true;
             break;
           } else {
             // Just track the user's movement
             setTriggerPercentage(
                 mAccelerateInterpolator.getInterpolation(yDiff / mDistanceToTriggerSync));
             float offsetTop = yDiff;
             if (mPrevY > eventY) {
               offsetTop = yDiff - mTouchSlop;
             }
             updateContentOffsetTop((int) (offsetTop));
             if (mPrevY > eventY && (mTarget.getTop() < mTouchSlop)) {
               // If the user puts the view back at the top, we
               // don't need to. This shouldn't be considered
               // cancelling the gesture as the user can restart from the top.
               removeCallbacks(mCancel);
             } else {
               updatePositionTimeout();
             }
             mPrevY = event.getY();
             handled = true;
           }
         }
       }
       break;
     case MotionEvent.ACTION_UP:
     case MotionEvent.ACTION_CANCEL:
       if (mDownEvent != null) {
         mDownEvent.recycle();
         mDownEvent = null;
       }
       break;
   }
   return handled;
 }
 public boolean onTouchEvent(MotionEvent paramMotionEvent)
 {
   Object localObject = null;
   int i1 = t.a(paramMotionEvent);
   boolean bool2;
   if (q == null)
   {
     bool2 = a(paramMotionEvent, 1);
     if (!bool2) {}
   }
   for (;;)
   {
     Behavior localBehavior = ((d)q.getLayoutParams()).b();
     boolean bool1;
     if (localBehavior != null) {
       bool1 = localBehavior.a(this, q, paramMotionEvent);
     }
     for (;;)
     {
       boolean bool3;
       if (q == null)
       {
         bool3 = bool1 | super.onTouchEvent(paramMotionEvent);
         paramMotionEvent = (MotionEvent)localObject;
       }
       do
       {
         if (((bool3) || (i1 != 0)) || (paramMotionEvent != null)) {
           paramMotionEvent.recycle();
         }
         if ((i1 == 1) || (i1 == 3)) {
           e();
         }
         return bool3;
         paramMotionEvent = (MotionEvent)localObject;
         bool3 = bool1;
       } while (!bool2);
       long l1;
       if (0 == 0) {
         l1 = SystemClock.uptimeMillis();
       }
       for (paramMotionEvent = MotionEvent.obtain(l1, l1, 3, 0.0F, 0.0F, 0);; paramMotionEvent = null)
       {
         super.onTouchEvent(paramMotionEvent);
         bool3 = bool1;
         break;
       }
       bool1 = false;
       continue;
       bool1 = false;
     }
     bool2 = false;
   }
 }
 protected void cancelContentTouch() {
   final long now = SystemClock.uptimeMillis();
   final MotionEvent cancelEvent =
       MotionEvent.obtain(now, now, MotionEvent.ACTION_CANCEL, 0.0f, 0.0f, 0);
   final int childCount = getChildCount();
   for (int i = 0; i < childCount; i++) {
     getChildAt(i).dispatchTouchEvent(cancelEvent);
   }
   mContentContainer.dispatchTouchEvent(cancelEvent);
   cancelEvent.recycle();
 }
Esempio n. 18
0
  @Override
  public boolean dispatchTouchEvent(MotionEvent ev) {
    if (((!mEnabled || !mInterceptTouch) && mMode == MODE_READY) || mAlwaysOpened) {
      return super.dispatchTouchEvent(ev);
    }

    if (mMode != MODE_FINISHED) {
      onTouchEvent(ev);

      if (mMode != MODE_SLIDE) {
        super.dispatchTouchEvent(ev);
      } else {
        MotionEvent cancelEvent = MotionEvent.obtain(ev);
        cancelEvent.setAction(MotionEvent.ACTION_CANCEL);
        super.dispatchTouchEvent(cancelEvent);
        cancelEvent.recycle();
      }

      return true;
    } else {
      final int action = ev.getAction();

      Rect rect = new Rect();
      View menu = getChildAt(0);
      menu.getHitRect(rect);

      if (!rect.contains((int) ev.getX(), (int) ev.getY())) {
        if (action == MotionEvent.ACTION_UP && mCloseOnRelease && !mDispatchWhenOpened) {
          close();
          mCloseOnRelease = false;
        } else {
          if (action == MotionEvent.ACTION_DOWN && !mDispatchWhenOpened) {
            mCloseOnRelease = true;
          }

          onTouchEvent(ev);
        }

        if (mDispatchWhenOpened) {
          super.dispatchTouchEvent(ev);
        }

        return true;
      } else {
        onTouchEvent(ev);

        ev.offsetLocation(-menu.getLeft(), -menu.getTop());
        menu.dispatchTouchEvent(ev);

        return true;
      }
    }
  }
  @Override
  public boolean dispatchTouchEvent(MotionEvent ev) {
    int action = ev.getAction() & MotionEvent.ACTION_MASK;
    if (action == MotionEvent.ACTION_DOWN) {
      mDownY = ev.getY();
      mHeaderOwnsTouch = mHeader != null && mDownY <= mHeader.getHeight() + mHeaderOffset;
    }

    boolean handled;
    if (mHeaderOwnsTouch) {
      if (mHeader != null && Math.abs(mDownY - ev.getY()) <= mTouchSlop) {
        handled = mHeader.dispatchTouchEvent(ev);
      } else {
        if (mHeader != null) {
          MotionEvent cancelEvent = MotionEvent.obtain(ev);
          cancelEvent.setAction(MotionEvent.ACTION_CANCEL);
          mHeader.dispatchTouchEvent(cancelEvent);
          cancelEvent.recycle();
        }

        MotionEvent downEvent =
            MotionEvent.obtain(
                ev.getDownTime(),
                ev.getEventTime(),
                ev.getAction(),
                ev.getX(),
                mDownY,
                ev.getMetaState());
        downEvent.setAction(MotionEvent.ACTION_DOWN);
        handled = mList.dispatchTouchEvent(downEvent);
        downEvent.recycle();
        mHeaderOwnsTouch = false;
      }
    } else {
      handled = mList.dispatchTouchEvent(ev);
    }

    return handled;
  }
Esempio n. 20
0
 public void startSelectionActionMode() {
   // Force the EditText to show the CAB (hacky!)
   MotionEvent event =
       MotionEvent.obtain(
           SystemClock.uptimeMillis(),
           SystemClock.uptimeMillis(),
           MotionEvent.ACTION_DOWN,
           0,
           0,
           0);
   dispatchTouchEvent(event);
   event.recycle();
 }
  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();
      }
    }
  }
Esempio n. 22
0
 private void dispatchPointer(MotionEvent event) {
   if (event.isTouchEvent()) {
     synchronized (mLock) {
       if (event.getAction() == MotionEvent.ACTION_MOVE) {
         mPendingMove = event;
       } else {
         mPendingMove = null;
       }
     }
     Message msg = mCaller.obtainMessageO(MSG_TOUCH_EVENT, event);
     mCaller.sendMessage(msg);
   } else {
     event.recycle();
   }
 }
  public void computeScroll() {

    if (mDecelerationVelocity.x == 0.f && mDecelerationVelocity.y == 0.f)
      return; // There's no deceleration in progress

    final long currentTime = AnimationUtils.currentAnimationTimeMillis();

    mDecelerationVelocity.x *= mChart.getDragDecelerationFrictionCoef();
    mDecelerationVelocity.y *= mChart.getDragDecelerationFrictionCoef();

    final float timeInterval = (float) (currentTime - mDecelerationLastTime) / 1000.f;

    float distanceX = mDecelerationVelocity.x * timeInterval;
    float distanceY = mDecelerationVelocity.y * timeInterval;

    mDecelerationCurrentPoint.x += distanceX;
    mDecelerationCurrentPoint.y += distanceY;

    MotionEvent event =
        MotionEvent.obtain(
            currentTime,
            currentTime,
            MotionEvent.ACTION_MOVE,
            mDecelerationCurrentPoint.x,
            mDecelerationCurrentPoint.y,
            0);
    performDrag(event);
    event.recycle();
    mMatrix = mChart.getViewPortHandler().refresh(mMatrix, mChart, false);

    mDecelerationLastTime = currentTime;

    if (Math.abs(mDecelerationVelocity.x) >= 0.01 || Math.abs(mDecelerationVelocity.y) >= 0.01)
      Utils.postInvalidateOnAnimation(
          mChart); // This causes computeScroll to fire, recommended for this by Google
    else {
      // Range might have changed, which means that Y-axis labels
      // could have changed in size, affecting Y-axis size.
      // So we need to recalculate offsets.
      mChart.calculateOffsets();
      mChart.postInvalidate();

      stopDeceleration();
    }
  }
 private void e()
 {
   int i1 = 0;
   if (q != null)
   {
     Behavior localBehavior = ((d)q.getLayoutParams()).b();
     if (localBehavior != null)
     {
       long l1 = SystemClock.uptimeMillis();
       MotionEvent localMotionEvent = MotionEvent.obtain(l1, l1, 3, 0.0F, 0.0F, 0);
       localBehavior.a(this, q, localMotionEvent);
       localMotionEvent.recycle();
     }
     q = null;
   }
   int i2 = getChildCount();
   while (i1 < i2)
   {
     ((d)getChildAt(i1).getLayoutParams()).f();
     i1 += 1;
   }
 }
 @Override
 public boolean onInterceptTouchEvent(MotionEvent ev) {
   boolean intercept = false;
   if (mDoubleTapToSleepEnabled) {
     if (mService.getBarState() == StatusBarState.SHADE && ev.getY() < mStatusBarHeaderHeight) {
       if (DEBUG) Log.w(TAG, "logging double tap gesture");
       mDoubleTapGesture.onTouchEvent(ev);
     }
   }
   final int h = getMeasuredHeight();
   if (mDoubleTapToSleepLockScreen
       && mService.getBarState() == StatusBarState.KEYGUARD
       && (ev.getY() < (h / 3) || ev.getY() > (h - mStatusBarHeaderHeight))) {
     if (DEBUG) Log.w(TAG, "logging lock screen double tap gesture");
     mDoubleTapGesture.onTouchEvent(ev);
   }
   if (mNotificationPanel.isFullyExpanded()
       && mStackScrollLayout.getVisibility() == View.VISIBLE
       && mService.getBarState() == StatusBarState.KEYGUARD
       && !mService.isBouncerShowing()) {
     intercept = mDragDownHelper.onInterceptTouchEvent(ev);
     // wake up on a touch down event, if dozing
     if (ev.getActionMasked() == MotionEvent.ACTION_DOWN) {
       mService.wakeUpIfDozing(ev.getEventTime(), ev);
     }
   }
   if (!intercept) {
     super.onInterceptTouchEvent(ev);
   }
   if (intercept) {
     MotionEvent cancellation = MotionEvent.obtain(ev);
     cancellation.setAction(MotionEvent.ACTION_CANCEL);
     mStackScrollLayout.onInterceptTouchEvent(cancellation);
     mNotificationPanel.onInterceptTouchEvent(cancellation);
     cancellation.recycle();
   }
   return intercept;
 }
  @Override
  public boolean onTouch(View v, MotionEvent event) {
    if (!callback.canSwipe()) return false;

    event.offsetLocation(translationX, 0);
    if (targetWidth < 2) targetWidth = view.getWidth();

    switch (event.getActionMasked()) {
      case MotionEvent.ACTION_DOWN:
        {
          downX = event.getRawX();

          velocityTracker = VelocityTracker.obtain();
          velocityTracker.addMovement(event);

          return false;
        }
      case MotionEvent.ACTION_UP:
        {
          if (velocityTracker == null) break;

          velocityTracker.addMovement(event);
          velocityTracker.computeCurrentVelocity(1000);

          if (swiping) {
            view.animate()
                .translationX(0f)
                .setDuration(animTime)
                .setListener(
                    new AnimatorListenerAdapter() {
                      @Override
                      public void onAnimationEnd(Animator animation) {
                        callback.onBound(canSwitch, swipingLeft);
                      }
                    });
          }

          downX = 0;
          translationX = 0;
          swiping = false;
          velocityTracker.recycle();
          velocityTracker = null;

          break;
        }
      case MotionEvent.ACTION_CANCEL:
        {
          if (velocityTracker == null) {
            break;
          }

          view.animate().translationX(0f).setDuration(animTime).setListener(null);

          downX = 0;
          translationX = 0;
          swiping = false;
          velocityTracker.recycle();
          velocityTracker = null;

          break;
        }
      case MotionEvent.ACTION_MOVE:
        {
          if (velocityTracker == null) {
            break;
          }

          velocityTracker.addMovement(event);

          float deltaX = event.getRawX() - downX;
          if (Math.abs(deltaX) > slop) {
            swiping = true;
            swipingLeft = deltaX < 0;
            canSwitch =
                Math.abs(deltaX)
                    >= ViewUnit.dp2px(
                        view.getContext(),
                        48); // Can switch tabs when deltaX >= 48 to prevent misuse
            swipingSlop = (deltaX > 0 ? slop : -slop);
            view.getParent().requestDisallowInterceptTouchEvent(true);

            MotionEvent cancelEvent = MotionEvent.obtainNoHistory(event);
            cancelEvent.setAction(
                MotionEvent.ACTION_CANCEL
                    | (event.getActionIndex() << MotionEvent.ACTION_POINTER_INDEX_SHIFT));
            view.onTouchEvent(cancelEvent);
            cancelEvent.recycle();
          }

          if (swiping) {
            translationX = deltaX;
            view.setTranslationX(deltaX - swipingSlop);
            callback.onSwipe();

            return true;
          }

          break;
        }
    }

    return false;
  }
  /**
   * 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;
  }
  @Override
  public boolean onTouch(View view, MotionEvent motionEvent) {
    if (mViewWidth < 2) {
      mViewWidth = mListView.getWidth();
    }

    switch (motionEvent.getActionMasked()) {
      case MotionEvent.ACTION_DOWN:
        {
          if (mPaused) {
            return false;
          }

          if (mSwiping) {
            return true;
          }

          // TODO: ensure this is a finger, and set a flag

          // Find the child view that was touched (perform a hit test)
          Rect rect = new Rect();
          int childCount = mListView.getChildCount();
          int headerCount = mListView.getHeaderViewsCount();
          int footerCount = mListView.getFooterViewsCount();
          int[] listViewCoords = new int[2];
          mListView.getLocationOnScreen(listViewCoords);
          int x = (int) motionEvent.getRawX() - listViewCoords[0];
          int y = (int) motionEvent.getRawY() - listViewCoords[1];
          View child = null;
          for (int i = headerCount; i < (childCount - footerCount); i++) {
            child = mListView.getChildAt(i);
            child.getHitRect(rect);
            if (rect.contains(x, y)) {
              mDownView = child;
              break;
            }
          }

          if (mDownView != null) {

            mDownX = motionEvent.getRawX();
            mDownY = motionEvent.getRawY();
            mDownPosition = mListView.getPositionForView(mDownView);
            if (mDownPosition != ListView.INVALID_POSITION
                && mDownPosition < mListView.getAdapter().getCount()) {
              if (mListView.getAdapter().getItem(mDownPosition) instanceof Card) {
                if (mCallbacks.canDismiss(
                    mDownPosition, (Card) mListView.getAdapter().getItem(mDownPosition))) {
                  mVelocityTracker = VelocityTracker.obtain();
                  mVelocityTracker.addMovement(motionEvent);
                } else {
                  mDownView = null;
                }
              } else {
                mDownView = null;
              }
            } else {
              mDownView = null;
            }
          }
          view.onTouchEvent(motionEvent);
          return true;
          // return false;
        }

      case MotionEvent.ACTION_UP:
        {
          if (mVelocityTracker == null) {
            break;
          }

          float deltaX = motionEvent.getRawX() - mDownX;
          mVelocityTracker.addMovement(motionEvent);
          mVelocityTracker.computeCurrentVelocity(1000);
          float velocityX = mVelocityTracker.getXVelocity();
          float absVelocityX = Math.abs(velocityX);
          float absVelocityY = Math.abs(mVelocityTracker.getYVelocity());
          boolean dismiss = false;
          boolean dismissRight = false;
          if (Math.abs(deltaX) > mViewWidth / swipeDistanceDivisor && mSwiping) {
            dismiss = true;
            dismissRight = deltaX > 0;
          } else if (mMinFlingVelocity <= absVelocityX
              && absVelocityX <= mMaxFlingVelocity
              && absVelocityY < absVelocityX
              && mSwiping) {
            // dismiss only if flinging in the same direction as dragging
            dismiss = (velocityX < 0) == (deltaX < 0);
            dismissRight = mVelocityTracker.getXVelocity() > 0;
          }
          if (dismiss && mDownPosition != ListView.INVALID_POSITION) {
            // dismiss
            dismiss(mDownView, mDownPosition - mListView.getHeaderViewsCount(), dismissRight);

          } else {
            // cancel
            mDownView
                .animate()
                .translationX(0)
                .alpha(1)
                .setDuration(mAnimationTime)
                .setListener(null);
          }

          mVelocityTracker.recycle();
          mVelocityTracker = null;
          mDownX = 0;
          mDownY = 0;
          mDownView = null;
          mDownPosition = ListView.INVALID_POSITION;
          if (mSwiping) {
            // To prevent onClick event with a fast swipe
            mSwiping = false;
            return true;
          }
          mSwiping = false;
          break;
        }

      case MotionEvent.ACTION_CANCEL:
        {
          if (mVelocityTracker == null) {
            break;
          }

          if (mDownView != null) {
            // cancel
            mDownView
                .animate()
                .translationX(0)
                .alpha(1)
                .setDuration(mAnimationTime)
                .setListener(null);
          }
          mVelocityTracker.recycle();
          mVelocityTracker = null;
          mDownX = 0;
          mDownY = 0;
          mDownView = null;
          mDownPosition = ListView.INVALID_POSITION;
          mSwiping = false;
          break;
        }

      case MotionEvent.ACTION_MOVE:
        {
          if (mVelocityTracker == null || mPaused) {
            break;
          }

          mVelocityTracker.addMovement(motionEvent);
          float deltaX = motionEvent.getRawX() - mDownX;
          float deltaY = motionEvent.getRawY() - mDownY;
          boolean movementAllowed = isSwipeMovementAllowed(deltaX);
          if (Math.abs(deltaX) > mSlop
              && Math.abs(deltaY) < Math.abs(deltaX) / 2
              && movementAllowed) {
            mSwiping = true;
            mSwipingSlop = (deltaX > 0 ? mSlop : -mSlop);
            mListView.requestDisallowInterceptTouchEvent(true);

            // Cancel ListView's touch (un-highlighting the item)
            MotionEvent cancelEvent = MotionEvent.obtain(motionEvent);
            cancelEvent.setAction(
                MotionEvent.ACTION_CANCEL
                    | (motionEvent.getActionIndex() << MotionEvent.ACTION_POINTER_INDEX_SHIFT));
            mListView.onTouchEvent(cancelEvent);
            view.onTouchEvent(cancelEvent);
            cancelEvent.recycle();
          }

          if (mSwiping) {
            mDownView.setTranslationX(deltaX - mSwipingSlop);
            mDownView.setAlpha(Math.max(0f, Math.min(1f, 1f - 2f * Math.abs(deltaX) / mViewWidth)));
            return true;
          }
          break;
        }
    }
    return false;
  }
    /**
     * Analyzes the given motion event and if applicable triggers the appropriate callbacks on the
     * {@link OnGestureListener} supplied.
     *
     * @param ev The current motion event.
     * @return true if the {@link OnGestureListener} consumed the event, else false.
     */
    public boolean onTouchEvent(MotionEvent ev) {
      final int action = ev.getAction();

      if (mVelocityTracker == null) {
        mVelocityTracker = VelocityTracker.obtain();
      }
      mVelocityTracker.addMovement(ev);

      final boolean pointerUp =
          (action & MotionEventCompat.ACTION_MASK) == MotionEventCompat.ACTION_POINTER_UP;
      final int skipIndex = pointerUp ? MotionEventCompat.getActionIndex(ev) : -1;

      // Determine focal point
      float sumX = 0, sumY = 0;
      final int count = MotionEventCompat.getPointerCount(ev);
      for (int i = 0; i < count; i++) {
        if (skipIndex == i) continue;
        sumX += MotionEventCompat.getX(ev, i);
        sumY += MotionEventCompat.getY(ev, i);
      }
      final int div = pointerUp ? count - 1 : count;
      final float focusX = sumX / div;
      final float focusY = sumY / div;

      boolean handled = false;

      switch (action & MotionEventCompat.ACTION_MASK) {
        case MotionEventCompat.ACTION_POINTER_DOWN:
          mDownFocusX = mLastFocusX = focusX;
          mDownFocusY = mLastFocusY = focusY;
          // Cancel long press and taps
          cancelTaps();
          break;

        case MotionEventCompat.ACTION_POINTER_UP:
          mDownFocusX = mLastFocusX = focusX;
          mDownFocusY = mLastFocusY = focusY;

          // Check the dot product of current velocities.
          // If the pointer that left was opposing another velocity vector, clear.
          mVelocityTracker.computeCurrentVelocity(1000, mMaximumFlingVelocity);
          final int upIndex = MotionEventCompat.getActionIndex(ev);
          final int id1 = MotionEventCompat.getPointerId(ev, upIndex);
          final float x1 = VelocityTrackerCompat.getXVelocity(mVelocityTracker, id1);
          final float y1 = VelocityTrackerCompat.getYVelocity(mVelocityTracker, id1);
          for (int i = 0; i < count; i++) {
            if (i == upIndex) continue;

            final int id2 = MotionEventCompat.getPointerId(ev, i);
            final float x = x1 * VelocityTrackerCompat.getXVelocity(mVelocityTracker, id2);
            final float y = y1 * VelocityTrackerCompat.getYVelocity(mVelocityTracker, id2);

            final float dot = x + y;
            if (dot < 0) {
              mVelocityTracker.clear();
              break;
            }
          }
          break;

        case MotionEvent.ACTION_DOWN:
          if (mDoubleTapListener != null) {
            boolean hadTapMessage = mHandler.hasMessages(TAP);
            if (hadTapMessage) mHandler.removeMessages(TAP);
            if ((mCurrentDownEvent != null)
                && (mPreviousUpEvent != null)
                && hadTapMessage
                && isConsideredDoubleTap(mCurrentDownEvent, mPreviousUpEvent, ev)) {
              // This is a second tap
              mIsDoubleTapping = true;
              // Give a callback with the first tap of the double-tap
              handled |= mDoubleTapListener.onDoubleTap(mCurrentDownEvent);
              // Give a callback with down event of the double-tap
              handled |= mDoubleTapListener.onDoubleTapEvent(ev);
            } else {
              // This is a first tap
              mHandler.sendEmptyMessageDelayed(TAP, DOUBLE_TAP_TIMEOUT);
            }
          }

          mDownFocusX = mLastFocusX = focusX;
          mDownFocusY = mLastFocusY = focusY;
          if (mCurrentDownEvent != null) {
            mCurrentDownEvent.recycle();
          }
          mCurrentDownEvent = MotionEvent.obtain(ev);
          mAlwaysInTapRegion = true;
          mAlwaysInBiggerTapRegion = true;
          mStillDown = true;
          mInLongPress = false;

          if (mIsLongpressEnabled) {
            mHandler.removeMessages(LONG_PRESS);
            mHandler.sendEmptyMessageAtTime(
                LONG_PRESS, mCurrentDownEvent.getDownTime() + TAP_TIMEOUT + LONGPRESS_TIMEOUT);
          }
          mHandler.sendEmptyMessageAtTime(
              SHOW_PRESS, mCurrentDownEvent.getDownTime() + TAP_TIMEOUT);
          handled |= mListener.onDown(ev);
          break;

        case MotionEvent.ACTION_MOVE:
          if (mInLongPress) {
            break;
          }
          final float scrollX = mLastFocusX - focusX;
          final float scrollY = mLastFocusY - focusY;
          if (mIsDoubleTapping) {
            // Give the move events of the double-tap
            handled |= mDoubleTapListener.onDoubleTapEvent(ev);
          } else if (mAlwaysInTapRegion) {
            final int deltaX = (int) (focusX - mDownFocusX);
            final int deltaY = (int) (focusY - mDownFocusY);
            int distance = (deltaX * deltaX) + (deltaY * deltaY);
            if (distance > mTouchSlopSquare) {
              handled = mListener.onScroll(mCurrentDownEvent, ev, scrollX, scrollY);
              mLastFocusX = focusX;
              mLastFocusY = focusY;
              mAlwaysInTapRegion = false;
              mHandler.removeMessages(TAP);
              mHandler.removeMessages(SHOW_PRESS);
              mHandler.removeMessages(LONG_PRESS);
            }
            if (distance > mTouchSlopSquare) {
              mAlwaysInBiggerTapRegion = false;
            }
          } else if ((Math.abs(scrollX) >= 1) || (Math.abs(scrollY) >= 1)) {
            handled = mListener.onScroll(mCurrentDownEvent, ev, scrollX, scrollY);
            mLastFocusX = focusX;
            mLastFocusY = focusY;
          }
          break;

        case MotionEvent.ACTION_UP:
          mStillDown = false;
          MotionEvent currentUpEvent = MotionEvent.obtain(ev);
          if (mIsDoubleTapping) {
            // Finally, give the up event of the double-tap
            handled |= mDoubleTapListener.onDoubleTapEvent(ev);
          } else if (mInLongPress) {
            mHandler.removeMessages(TAP);
            mInLongPress = false;
          } else if (mAlwaysInTapRegion) {
            handled = mListener.onSingleTapUp(ev);
          } else {

            // A fling must travel the minimum tap distance
            final VelocityTracker velocityTracker = mVelocityTracker;
            final int pointerId = MotionEventCompat.getPointerId(ev, 0);
            velocityTracker.computeCurrentVelocity(1000, mMaximumFlingVelocity);
            final float velocityY = VelocityTrackerCompat.getYVelocity(velocityTracker, pointerId);
            final float velocityX = VelocityTrackerCompat.getXVelocity(velocityTracker, pointerId);

            if ((Math.abs(velocityY) > mMinimumFlingVelocity)
                || (Math.abs(velocityX) > mMinimumFlingVelocity)) {
              handled = mListener.onFling(mCurrentDownEvent, ev, velocityX, velocityY);
            }
          }
          if (mPreviousUpEvent != null) {
            mPreviousUpEvent.recycle();
          }
          // Hold the event we obtained above - listeners may have changed the original.
          mPreviousUpEvent = currentUpEvent;
          if (mVelocityTracker != null) {
            // This may have been cleared when we called out to the
            // application above.
            mVelocityTracker.recycle();
            mVelocityTracker = null;
          }
          mIsDoubleTapping = false;
          mHandler.removeMessages(SHOW_PRESS);
          mHandler.removeMessages(LONG_PRESS);
          break;

        case MotionEvent.ACTION_CANCEL:
          cancel();
          break;
      }

      return handled;
    }
  public boolean onTouchEvent(MotionEvent event) {
    final int action = event.getAction();
    boolean handled = true;

    if (!mGestureInProgress) {
      switch (action & MotionEvent.ACTION_MASK) {
        case MotionEvent.ACTION_POINTER_DOWN:
          {
            // We have a new multi-finger gesture

            // as orientation can change, query the metrics in touch down
            DisplayMetrics metrics = mContext.getResources().getDisplayMetrics();
            mRightSlopEdge = metrics.widthPixels - mEdgeSlop;
            mBottomSlopEdge = metrics.heightPixels - mEdgeSlop;

            // Be paranoid in case we missed an event
            reset();

            mPrevEvent = MotionEvent.obtain(event);
            mTimeDelta = 0;

            setContext(event);

            // Check if we have a sloppy gesture. If so, delay
            // the beginning of the gesture until we're sure that's
            // what the user wanted. Sloppy gestures can happen if the
            // edge of the user's hand is touching the screen, for example.
            final float edgeSlop = mEdgeSlop;
            final float rightSlop = mRightSlopEdge;
            final float bottomSlop = mBottomSlopEdge;
            final float x0 = event.getRawX();
            final float y0 = event.getRawY();
            final float x1 = getRawX(event, 1);
            final float y1 = getRawY(event, 1);

            boolean p0sloppy = x0 < edgeSlop || y0 < edgeSlop || x0 > rightSlop || y0 > bottomSlop;
            boolean p1sloppy = x1 < edgeSlop || y1 < edgeSlop || x1 > rightSlop || y1 > bottomSlop;

            if (p0sloppy && p1sloppy) {
              mSloppyGesture = true;
            } else if (p0sloppy) {
              mSloppyGesture = true;
            } else if (p1sloppy) {
              mSloppyGesture = true;
            } else {
              mGestureInProgress = mListener.onScaleBegin(this);
            }
          }
          break;

        case MotionEvent.ACTION_MOVE:
          if (mSloppyGesture) {
            //					Log.d(TAG, "scale gesture move mSloppyGesture");
            // Initiate sloppy gestures if we've moved outside of the
            // slop area.
            final float edgeSlop = mEdgeSlop;
            final float rightSlop = mRightSlopEdge;
            final float bottomSlop = mBottomSlopEdge;
            final float x0 = event.getRawX();
            final float y0 = event.getRawY();
            final float x1 = getRawX(event, 1);
            final float y1 = getRawY(event, 1);

            boolean p0sloppy = x0 < edgeSlop || y0 < edgeSlop || x0 > rightSlop || y0 > bottomSlop;
            boolean p1sloppy = x1 < edgeSlop || y1 < edgeSlop || x1 > rightSlop || y1 > bottomSlop;

            if (p0sloppy && p1sloppy) {
            } else if (p0sloppy) {
            } else if (p1sloppy) {
            } else {
              mSloppyGesture = false;
              mGestureInProgress = mListener.onScaleBegin(this);
            }
          }
          break;

        case MotionEvent.ACTION_POINTER_UP:
          if (mSloppyGesture) {}
          break;
      }
    } else {
      // Transform gesture in progress - attempt to handle it
      switch (action & MotionEvent.ACTION_MASK) {
        case MotionEvent.ACTION_POINTER_UP:
          // Gesture ended
          setContext(event);

          if (!mSloppyGesture) {
            mListener.onScaleEnd(this);
          }

          reset();
          break;

        case MotionEvent.ACTION_CANCEL:
          if (!mSloppyGesture) {
            mListener.onScaleEnd(this);
          }

          reset();
          break;

        case MotionEvent.ACTION_MOVE:
          //				Log.d(TAG, "scale gesture move");
          setContext(event);

          mMiddleX = (event.getX(0) + event.getX(1)) / 2;
          mMiddleY = (event.getY(0) + event.getY(1)) / 2;

          // Only accept the event if our relative pressure is within
          // a certain limit - this can help filter shaky data as a
          // finger is lifted.
          if (mCurrPressure / mPrevPressure > PRESSURE_THRESHOLD) {
            final boolean updatePrevious = mListener.onScale(this, mMiddleX, mMiddleY);

            if (updatePrevious) {
              mPrevEvent.recycle();
              mPrevEvent = MotionEvent.obtain(event);
            }
          }
          break;
      }
    }
    return handled;
  }