Adding fling-to-delete.

- Also fixing issue where the drop target icon changes color slower than the text.

Change-Id: I0bfa59da5d202016342f1c3de419ebcafd81ff6f
This commit is contained in:
Winson Chung
2012-03-01 16:09:54 -08:00
parent d1eef3388a
commit 043f2af567
12 changed files with 416 additions and 44 deletions
+130 -9
View File
@@ -19,6 +19,7 @@ package com.android.launcher2;
import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.Point;
import android.graphics.PointF;
import android.graphics.Rect;
import android.os.Handler;
import android.os.IBinder;
@@ -26,6 +27,7 @@ import android.os.Vibrator;
import android.util.Log;
import android.view.KeyEvent;
import android.view.MotionEvent;
import android.view.VelocityTracker;
import android.view.View;
import android.view.ViewConfiguration;
import android.view.inputmethod.InputMethodManager;
@@ -60,6 +62,9 @@ public class DragController {
static final int SCROLL_LEFT = 0;
static final int SCROLL_RIGHT = 1;
private static final float MAX_FLING_DEGREES = 35f;
private static final int FLING_TO_DELETE_THRESHOLD_Y_VELOCITY = -1400;
private Launcher mLauncher;
private Handler mHandler;
private final Vibrator mVibrator = new Vibrator();
@@ -86,8 +91,8 @@ public class DragController {
/** Who can receive drop events */
private ArrayList<DropTarget> mDropTargets = new ArrayList<DropTarget>();
private ArrayList<DragListener> mListeners = new ArrayList<DragListener>();
private DropTarget mFlingToDeleteDropTarget;
/** The window token used as the parent for the DragView. */
private IBinder mWindowToken;
@@ -111,6 +116,9 @@ public class DragController {
private int mTmpPoint[] = new int[2];
private Rect mDragLayerRect = new Rect();
protected int mFlingToDeleteThresholdVelocity;
private VelocityTracker mVelocityTracker;
/**
* Interface to receive notifications when a drag starts or stops
*/
@@ -141,6 +149,10 @@ public class DragController {
mLauncher = launcher;
mHandler = new Handler();
mScrollZone = launcher.getResources().getDimensionPixelSize(R.dimen.scroll_zone);
mVelocityTracker = VelocityTracker.obtain();
float density = launcher.getResources().getDisplayMetrics().density;
mFlingToDeleteThresholdVelocity = (int) (FLING_TO_DELETE_THRESHOLD_Y_VELOCITY * density);
}
public boolean dragging() {
@@ -378,15 +390,35 @@ public class DragController {
if (mDragging) {
mDragging = false;
clearScrollRunnable();
for (DragListener listener : mListeners) {
listener.onDragEnd();
}
boolean isDeferred = false;
if (mDragObject.dragView != null) {
if (!mDragObject.deferDragViewCleanupPostAnimation) {
isDeferred = mDragObject.deferDragViewCleanupPostAnimation;
if (!isDeferred) {
mDragObject.dragView.remove();
}
mDragObject.dragView = null;
}
// Only end the drag if we are not deferred
if (!isDeferred) {
for (DragListener listener : mListeners) {
listener.onDragEnd();
}
}
}
releaseVelocityTracker();
}
/**
* This only gets called as a result of drag view cleanup being deferred in endDrag();
*/
void onDeferredEndDrag(DragView dragView) {
dragView.remove();
// If we skipped calling onDragEnd() before, do it now
for (DragListener listener : mListeners) {
listener.onDragEnd();
}
}
@@ -408,8 +440,11 @@ public class DragController {
Log.d(Launcher.TAG, "DragController.onInterceptTouchEvent " + ev + " mDragging="
+ mDragging);
}
final int action = ev.getAction();
// Update the velocity tracker
acquireVelocityTrackerAndAddMovement(ev);
final int action = ev.getAction();
final int[] dragLayerPos = getClampedDragLayerPos(ev.getX(), ev.getY());
final int dragLayerX = dragLayerPos[0];
final int dragLayerY = dragLayerPos[1];
@@ -425,7 +460,12 @@ public class DragController {
break;
case MotionEvent.ACTION_UP:
if (mDragging) {
drop(dragLayerX, dragLayerY);
PointF vec = isFlingingToDelete(mDragObject.dragSource);
if (vec != null) {
dropOnFlingToDeleteTarget(dragLayerX, dragLayerY, vec);
} else {
drop(dragLayerX, dragLayerY);
}
}
endDrag();
break;
@@ -529,6 +569,9 @@ public class DragController {
return false;
}
// Update the velocity tracker
acquireVelocityTrackerAndAddMovement(ev);
final int action = ev.getAction();
final int[] dragLayerPos = getClampedDragLayerPos(ev.getX(), ev.getY());
final int dragLayerX = dragLayerPos[0];
@@ -553,10 +596,15 @@ public class DragController {
case MotionEvent.ACTION_UP:
// Ensure that we've processed a move event at the current pointer location.
handleMoveEvent(dragLayerX, dragLayerY);
mHandler.removeCallbacks(mScrollRunnable);
if (mDragging) {
drop(dragLayerX, dragLayerY);
PointF vec = isFlingingToDelete(mDragObject.dragSource);
if (vec != null) {
dropOnFlingToDeleteTarget(dragLayerX, dragLayerY, vec);
} else {
drop(dragLayerX, dragLayerY);
}
}
endDrag();
break;
@@ -569,6 +617,58 @@ public class DragController {
return true;
}
/**
* Determines whether the user flung the current item to delete it.
*
* @return the vector at which the item was flung, or null if no fling was detected.
*/
private PointF isFlingingToDelete(DragSource source) {
if (mFlingToDeleteDropTarget == null) return null;
if (!source.supportsFlingToDelete()) return null;
ViewConfiguration config = ViewConfiguration.get(mLauncher);
mVelocityTracker.computeCurrentVelocity(1000, config.getScaledMaximumFlingVelocity());
if (mVelocityTracker.getYVelocity() < mFlingToDeleteThresholdVelocity) {
// Do a quick dot product test to ensure that we are flinging upwards
PointF vel = new PointF(mVelocityTracker.getXVelocity(),
mVelocityTracker.getYVelocity());
PointF upVec = new PointF(0f, -1f);
float theta = (float) Math.acos(((vel.x * upVec.x) + (vel.y * upVec.y)) /
(vel.length() * upVec.length()));
if (theta <= Math.toRadians(MAX_FLING_DEGREES)) {
return vel;
}
}
return null;
}
private void dropOnFlingToDeleteTarget(float x, float y, PointF vel) {
final int[] coordinates = mCoordinatesTemp;
mDragObject.x = coordinates[0];
mDragObject.y = coordinates[1];
mDragObject.dragComplete = true;
// Clean up dragging on the target if it's not the current fling delete target otherwise,
// start dragging to it.
if (mLastDropTarget != null && mFlingToDeleteDropTarget != mLastDropTarget) {
mLastDropTarget.onDragExit(mDragObject);
}
// Drop onto the fling-to-delete target
boolean accepted = false;
mFlingToDeleteDropTarget.onDragEnter(mDragObject);
mFlingToDeleteDropTarget.onDragExit(mDragObject);
if (mFlingToDeleteDropTarget.acceptDrop(mDragObject)) {
mFlingToDeleteDropTarget.onFlingToDelete(mDragObject, mDragObject.x, mDragObject.y,
vel);
accepted = true;
}
mDragObject.dragSource.onDropCompleted((View) mFlingToDeleteDropTarget, mDragObject,
accepted);
}
private void drop(float x, float y) {
final int[] coordinates = mCoordinatesTemp;
final DropTarget dropTarget = findDropTarget((int) x, (int) y, coordinates);
@@ -658,6 +758,27 @@ public class DragController {
mDropTargets.remove(target);
}
/**
* Sets the current fling-to-delete drop target.
*/
public void setFlingToDeleteDropTarget(DropTarget target) {
mFlingToDeleteDropTarget = target;
}
private void acquireVelocityTrackerAndAddMovement(MotionEvent ev) {
if (mVelocityTracker == null) {
mVelocityTracker = VelocityTracker.obtain();
}
mVelocityTracker.addMovement(ev);
}
private void releaseVelocityTracker() {
if (mVelocityTracker != null) {
mVelocityTracker.recycle();
mVelocityTracker = null;
}
}
/**
* Set which view scrolls for touch events near the edge of the screen.
*/