blob: 71c5227270382649f4d1d42e698254926bfd3dee [file] [log] [blame]
/**
* Copyright (C) 2014 Google Inc.
* Licensed to The Android Open Source Project.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.android.mail.ui;
import android.content.Context;
import android.support.annotation.Nullable;
import android.view.MotionEvent;
import android.view.VelocityTracker;
import android.view.ViewConfiguration;
/**
* Generic utility class that deals with capturing drag events in a particular horizontal direction
* and calls the callback interface for drag events.
*
* Usage:
*
* <code>
*
* class CustomView extends ... {
* private boolean mShouldInterceptDrag;
* private int mDragMode;
*
* public boolean onInterceptTouchEvent(MotionEvent ev) {
* switch (ev.getAction()) {
* case MotionEvent.ACTION_DOWN:
* // Check if the event is in the draggable area
* mShouldInterceptDrag = ...;
* mDragMode = ...;
* }
* return mShouldInterceptDrag && GmailDragHelper.processTouchEvent(ev, mDragMode);
* }
*
* public boolean onTouchEvent(MotionEvent ev) {
* if (mShouldInterceptDrag) {
* GmailDragHelper.processTouchEvent(ev, mDragMode);
* return true;
* }
* return super.onTouchEvent(ev);
* }
* }
*
* </code>
*/
public class GmailDragHelper {
public static final int CAPTURE_LEFT_TO_RIGHT = 0;
public static final int CAPTURE_RIGHT_TO_LEFT = 1;
private final GmailDragHelperCallback mCallback;
private final ViewConfiguration mConfiguration;
private boolean mDragging;
private VelocityTracker mVelocityTracker;
private float mInitialInterceptedX;
private float mInitialInterceptedY;
private float mStartDragX;
public interface GmailDragHelperCallback {
public void onDragStarted();
public void onDrag(float deltaX);
public void onDragEnded(float deltaX, float velocityX, boolean isFling);
}
/**
*/
public GmailDragHelper(Context context, GmailDragHelperCallback callback) {
mCallback = callback;
mConfiguration = ViewConfiguration.get(context);
}
/**
* Process incoming MotionEvent to compute the new drag state and coordinates.
*
* @param ev the captured MotionEvent
* @param dragMode either {@link GmailDragHelper#CAPTURE_LEFT_TO_RIGHT} or
* {@link GmailDragHelper#CAPTURE_RIGHT_TO_LEFT}
* @return whether if drag is happening
*/
public boolean processTouchEvent(MotionEvent ev, int dragMode) {
return processTouchEvent(ev, dragMode, null);
}
/**
* @param xThreshold optional parameter to specify that the drag can only happen if it crosses
* the threshold coordinate. This can be used to only start the drag once the user hits the
* edge of the view.
*/
public boolean processTouchEvent(MotionEvent ev, int dragMode, @Nullable Float xThreshold) {
if (mVelocityTracker == null) {
mVelocityTracker = VelocityTracker.obtain();
}
mVelocityTracker.addMovement(ev);
switch (ev.getAction()) {
case MotionEvent.ACTION_DOWN:
mInitialInterceptedX = ev.getX();
mInitialInterceptedY = ev.getY();
break;
case MotionEvent.ACTION_MOVE:
if (mDragging) {
mCallback.onDrag(ev.getX() - mStartDragX);
} else {
// Try to start dragging
final float evX = ev.getX();
// Check for directional drag
if ((dragMode == CAPTURE_LEFT_TO_RIGHT && evX <= mInitialInterceptedX) ||
(dragMode == CAPTURE_RIGHT_TO_LEFT && evX >= mInitialInterceptedX)) {
break;
}
// Check for optional threshold
boolean passedThreshold = true;
if (xThreshold != null) {
if (dragMode == CAPTURE_LEFT_TO_RIGHT) {
passedThreshold = evX > xThreshold;
} else {
passedThreshold = evX < xThreshold;
}
}
// Check for drag threshold
final float deltaX = Math.abs(evX - mInitialInterceptedX);
final float deltaY = Math.abs(ev.getY() - mInitialInterceptedY);
if (deltaX >= mConfiguration.getScaledTouchSlop() && deltaX >= deltaY
&& passedThreshold) {
setDragging(true, evX);
}
}
break;
case MotionEvent.ACTION_UP:
if (mDragging) {
setDragging(false, ev.getX());
}
break;
}
return mDragging;
}
/**
* Set the internal dragging state and calls the appropriate callbacks.
*/
private void setDragging(boolean dragging, float evX) {
mDragging = dragging;
if (mDragging) {
mStartDragX = evX;
mCallback.onDragStarted();
} else {
// Here velocity is in pixel/second, let's take that into account for evX.
mVelocityTracker.computeCurrentVelocity(1000);
// Check for fling
final float xVelocity = mVelocityTracker.getXVelocity();
final boolean isFling =
Math.abs(xVelocity) > mConfiguration.getScaledMinimumFlingVelocity();
mVelocityTracker.clear();
mCallback.onDragEnded(evX - mStartDragX, xVelocity, isFling);
}
}
}