/**
  * Constructs a new swipe-to-dismiss touch listener for the given list view.
  *
  * @param recyclerView The list view whose items should be dismissable.
  * @param callbacks The callback to trigger when the user has indicated that she would like to
  *     dismiss one or more list items.
  */
 public SwipeToDismissTouchListener(
     SomeCollectionView recyclerView, DismissCallbacks<SomeCollectionView> callbacks) {
   ViewConfiguration vc = ViewConfiguration.get(recyclerView.getContext());
   mSlop = vc.getScaledTouchSlop();
   mMinFlingVelocity = vc.getScaledMinimumFlingVelocity() * 16;
   mMaxFlingVelocity = vc.getScaledMaximumFlingVelocity();
   mAnimationTime =
       recyclerView.getContext().getResources().getInteger(android.R.integer.config_shortAnimTime);
   mRecyclerView = recyclerView;
   mCallbacks = callbacks;
   mHandler = new Handler();
 }
  /**
   * Returns an {@link android.widget.AbsListView.OnScrollListener} to be added to the {@link
   * android.widget.ListView} using {@link
   * android.widget.ListView#setOnScrollListener(android.widget.AbsListView.OnScrollListener)}. If a
   * scroll listener is already assigned, the caller should still pass scroll changes through to
   * this listener. This will ensure that this {@link SwipeToDismissTouchListener} is paused during
   * list view scrolling.
   *
   * @see SwipeToDismissTouchListener
   */
  public Object makeScrollListener() {
    return mRecyclerView.makeScrollListener(
        new AbsListView.OnScrollListener() {
          @Override
          public void onScrollStateChanged(AbsListView absListView, int scrollState) {
            processPendingDismisses();
            setEnabled(scrollState != AbsListView.OnScrollListener.SCROLL_STATE_TOUCH_SCROLL);
          }

          @Override
          public void onScroll(AbsListView absListView, int i, int i1, int i2) {}
        });
  }
  @Override
  public boolean onTouch(View view, MotionEvent motionEvent) {
    if (mViewWidth < 2) {
      mViewWidth = mRecyclerView.getWidth();
    }

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

          // 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 = mRecyclerView.getChildCount();
          int[] listViewCoords = new int[2];
          mRecyclerView.getLocationOnScreen(listViewCoords);
          int x = (int) motionEvent.getRawX() - listViewCoords[0];
          int y = (int) motionEvent.getRawY() - listViewCoords[1];
          View child;
          for (int i = 0; i < childCount; i++) {
            child = mRecyclerView.getChildAt(i);
            child.getHitRect(rect);
            if (rect.contains(x, y)) {
              assert (child instanceof ViewGroup && ((ViewGroup) child).getChildCount() == 2)
                  : "Each child needs to extend from ViewGroup and have two children";

              boolean dataContainerHasBeenDismissed =
                  mPendingDismiss != null
                      && mPendingDismiss.position == mRecyclerView.getChildPosition(child)
                      && mPendingDismiss.rowContainer.dataContainerHasBeenDismissed;
              mRowContainer = new RowContainer((ViewGroup) child);
              mRowContainer.dataContainerHasBeenDismissed = dataContainerHasBeenDismissed;
              break;
            }
          }

          if (mRowContainer != null) {
            mDownX = motionEvent.getRawX();
            mDownY = motionEvent.getRawY();
            mDownPosition = mRecyclerView.getChildPosition(mRowContainer.container);
            if (mCallbacks.canDismiss(mDownPosition)) {
              mVelocityTracker = VelocityTracker.obtain();
              mVelocityTracker.addMovement(motionEvent);
            } else {
              mRowContainer = null;
            }
          }
          return false;
        }

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

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

      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 / 2 && 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
            final RowContainer downView =
                mRowContainer; // mDownView gets null'd before animation ends
            final int downPosition = mDownPosition;
            mRowContainer
                .getCurrentSwipingView()
                .animate()
                .translationX(dismissRight ? mViewWidth : -mViewWidth)
                .alpha(0)
                .setDuration(mAnimationTime)
                .setListener(
                    new AnimatorListenerAdapter() {
                      @Override
                      public void onAnimationEnd(Animator animation) {
                        performDismiss(downView, downPosition);
                      }
                    });
          } else {
            // cancel
            mRowContainer
                .getCurrentSwipingView()
                .animate()
                .translationX(0)
                .alpha(1)
                .setDuration(mAnimationTime)
                .setListener(null);
          }
          mVelocityTracker.recycle();
          mVelocityTracker = null;
          mDownX = 0;
          mDownY = 0;
          mRowContainer = 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;
          if (Math.abs(deltaX) > mSlop && Math.abs(deltaY) < Math.abs(deltaX) / 2) {
            mSwiping = true;
            mSwipingSlop = (deltaX > 0 ? mSlop : -mSlop);
            mRecyclerView.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));
            mRecyclerView.onTouchEvent(cancelEvent);
            cancelEvent.recycle();
          }

          if (mSwiping) {
            mRowContainer.getCurrentSwipingView().setTranslationX(deltaX - mSwipingSlop);
            // Comment line below to disable alpha fade on initial swipe
            mRowContainer
                .getCurrentSwipingView()
                .setAlpha(Math.max(0f, Math.min(1f, 1f - 2f * Math.abs(deltaX) / mViewWidth)));
            return true;
          }
          break;
        }
    }
    return false;
  }