summaryrefslogtreecommitdiffstats
path: root/src/com/android/launcher3/DragController.java
diff options
context:
space:
mode:
Diffstat (limited to 'src/com/android/launcher3/DragController.java')
-rw-r--r--src/com/android/launcher3/DragController.java821
1 files changed, 821 insertions, 0 deletions
diff --git a/src/com/android/launcher3/DragController.java b/src/com/android/launcher3/DragController.java
new file mode 100644
index 000000000..86355890e
--- /dev/null
+++ b/src/com/android/launcher3/DragController.java
@@ -0,0 +1,821 @@
+/*
+ * Copyright (C) 2008 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.launcher3;
+
+import android.content.Context;
+import android.content.res.Resources;
+import android.graphics.Bitmap;
+import android.graphics.Point;
+import android.graphics.PointF;
+import android.graphics.Rect;
+import android.os.Handler;
+import android.os.IBinder;
+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;
+
+import com.android.launcher3.R;
+
+import java.util.ArrayList;
+
+/**
+ * Class for initiating a drag within a view or across multiple views.
+ */
+public class DragController {
+ private static final String TAG = "Launcher.DragController";
+
+ /** Indicates the drag is a move. */
+ public static int DRAG_ACTION_MOVE = 0;
+
+ /** Indicates the drag is a copy. */
+ public static int DRAG_ACTION_COPY = 1;
+
+ private static final int SCROLL_DELAY = 500;
+ private static final int RESCROLL_DELAY = 750;
+ private static final int VIBRATE_DURATION = 15;
+
+ private static final boolean PROFILE_DRAWING_DURING_DRAG = false;
+
+ private static final int SCROLL_OUTSIDE_ZONE = 0;
+ private static final int SCROLL_WAITING_IN_ZONE = 1;
+
+ static final int SCROLL_NONE = -1;
+ static final int SCROLL_LEFT = 0;
+ static final int SCROLL_RIGHT = 1;
+
+ private static final float MAX_FLING_DEGREES = 35f;
+
+ private Launcher mLauncher;
+ private Handler mHandler;
+ private final Vibrator mVibrator;
+
+ // temporaries to avoid gc thrash
+ private Rect mRectTemp = new Rect();
+ private final int[] mCoordinatesTemp = new int[2];
+
+ /** Whether or not we're dragging. */
+ private boolean mDragging;
+
+ /** X coordinate of the down event. */
+ private int mMotionDownX;
+
+ /** Y coordinate of the down event. */
+ private int mMotionDownY;
+
+ /** the area at the edge of the screen that makes the workspace go left
+ * or right while you're dragging.
+ */
+ private int mScrollZone;
+
+ private DropTarget.DragObject mDragObject;
+
+ /** 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;
+
+ /** The view that will be scrolled when dragging to the left and right edges of the screen. */
+ private View mScrollView;
+
+ private View mMoveTarget;
+
+ private DragScroller mDragScroller;
+ private int mScrollState = SCROLL_OUTSIDE_ZONE;
+ private ScrollRunnable mScrollRunnable = new ScrollRunnable();
+
+ private DropTarget mLastDropTarget;
+
+ private InputMethodManager mInputMethodManager;
+
+ private int mLastTouch[] = new int[2];
+ private long mLastTouchUpTime = -1;
+ private int mDistanceSinceScroll = 0;
+
+ 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
+ */
+ interface DragListener {
+
+ /**
+ * A drag has begun
+ *
+ * @param source An object representing where the drag originated
+ * @param info The data associated with the object that is being dragged
+ * @param dragAction The drag action: either {@link DragController#DRAG_ACTION_MOVE}
+ * or {@link DragController#DRAG_ACTION_COPY}
+ */
+ void onDragStart(DragSource source, Object info, int dragAction);
+
+ /**
+ * The drag has ended
+ */
+ void onDragEnd();
+ }
+
+ /**
+ * Used to create a new DragLayer from XML.
+ *
+ * @param context The application's context.
+ */
+ public DragController(Launcher launcher) {
+ Resources r = launcher.getResources();
+ mLauncher = launcher;
+ mHandler = new Handler();
+ mScrollZone = r.getDimensionPixelSize(R.dimen.scroll_zone);
+ mVelocityTracker = VelocityTracker.obtain();
+ mVibrator = (Vibrator) launcher.getSystemService(Context.VIBRATOR_SERVICE);
+
+ float density = r.getDisplayMetrics().density;
+ mFlingToDeleteThresholdVelocity =
+ (int) (r.getInteger(R.integer.config_flingToDeleteMinVelocity) * density);
+ }
+
+ public boolean dragging() {
+ return mDragging;
+ }
+
+ /**
+ * Starts a drag.
+ *
+ * @param v The view that is being dragged
+ * @param bmp The bitmap that represents the view being dragged
+ * @param source An object representing where the drag originated
+ * @param dragInfo The data associated with the object that is being dragged
+ * @param dragAction The drag action: either {@link #DRAG_ACTION_MOVE} or
+ * {@link #DRAG_ACTION_COPY}
+ * @param dragRegion Coordinates within the bitmap b for the position of item being dragged.
+ * Makes dragging feel more precise, e.g. you can clip out a transparent border
+ */
+ public void startDrag(View v, Bitmap bmp, DragSource source, Object dragInfo, int dragAction,
+ Point extraPadding, float initialDragViewScale) {
+ int[] loc = mCoordinatesTemp;
+ mLauncher.getDragLayer().getLocationInDragLayer(v, loc);
+ int viewExtraPaddingLeft = extraPadding != null ? extraPadding.x : 0;
+ int viewExtraPaddingTop = extraPadding != null ? extraPadding.y : 0;
+ int dragLayerX = loc[0] + v.getPaddingLeft() + viewExtraPaddingLeft +
+ (int) ((initialDragViewScale * bmp.getWidth() - bmp.getWidth()) / 2);
+ int dragLayerY = loc[1] + v.getPaddingTop() + viewExtraPaddingTop +
+ (int) ((initialDragViewScale * bmp.getHeight() - bmp.getHeight()) / 2);
+
+ startDrag(bmp, dragLayerX, dragLayerY, source, dragInfo, dragAction, null,
+ null, initialDragViewScale);
+
+ if (dragAction == DRAG_ACTION_MOVE) {
+ v.setVisibility(View.GONE);
+ }
+ }
+
+ /**
+ * Starts a drag.
+ *
+ * @param b The bitmap to display as the drag image. It will be re-scaled to the
+ * enlarged size.
+ * @param dragLayerX The x position in the DragLayer of the left-top of the bitmap.
+ * @param dragLayerY The y position in the DragLayer of the left-top of the bitmap.
+ * @param source An object representing where the drag originated
+ * @param dragInfo The data associated with the object that is being dragged
+ * @param dragAction The drag action: either {@link #DRAG_ACTION_MOVE} or
+ * {@link #DRAG_ACTION_COPY}
+ * @param dragRegion Coordinates within the bitmap b for the position of item being dragged.
+ * Makes dragging feel more precise, e.g. you can clip out a transparent border
+ */
+ public void startDrag(Bitmap b, int dragLayerX, int dragLayerY,
+ DragSource source, Object dragInfo, int dragAction, Point dragOffset, Rect dragRegion,
+ float initialDragViewScale) {
+ if (PROFILE_DRAWING_DURING_DRAG) {
+ android.os.Debug.startMethodTracing("Launcher");
+ }
+
+ // Hide soft keyboard, if visible
+ if (mInputMethodManager == null) {
+ mInputMethodManager = (InputMethodManager)
+ mLauncher.getSystemService(Context.INPUT_METHOD_SERVICE);
+ }
+ mInputMethodManager.hideSoftInputFromWindow(mWindowToken, 0);
+
+ for (DragListener listener : mListeners) {
+ listener.onDragStart(source, dragInfo, dragAction);
+ }
+
+ final int registrationX = mMotionDownX - dragLayerX;
+ final int registrationY = mMotionDownY - dragLayerY;
+
+ final int dragRegionLeft = dragRegion == null ? 0 : dragRegion.left;
+ final int dragRegionTop = dragRegion == null ? 0 : dragRegion.top;
+
+ mDragging = true;
+
+ mDragObject = new DropTarget.DragObject();
+
+ mDragObject.dragComplete = false;
+ mDragObject.xOffset = mMotionDownX - (dragLayerX + dragRegionLeft);
+ mDragObject.yOffset = mMotionDownY - (dragLayerY + dragRegionTop);
+ mDragObject.dragSource = source;
+ mDragObject.dragInfo = dragInfo;
+
+ mVibrator.vibrate(VIBRATE_DURATION);
+
+ final DragView dragView = mDragObject.dragView = new DragView(mLauncher, b, registrationX,
+ registrationY, 0, 0, b.getWidth(), b.getHeight(), initialDragViewScale);
+
+ if (dragOffset != null) {
+ dragView.setDragVisualizeOffset(new Point(dragOffset));
+ }
+ if (dragRegion != null) {
+ dragView.setDragRegion(new Rect(dragRegion));
+ }
+
+ dragView.show(mMotionDownX, mMotionDownY);
+ handleMoveEvent(mMotionDownX, mMotionDownY);
+ }
+
+ /**
+ * Draw the view into a bitmap.
+ */
+ Bitmap getViewBitmap(View v) {
+ v.clearFocus();
+ v.setPressed(false);
+
+ boolean willNotCache = v.willNotCacheDrawing();
+ v.setWillNotCacheDrawing(false);
+
+ // Reset the drawing cache background color to fully transparent
+ // for the duration of this operation
+ int color = v.getDrawingCacheBackgroundColor();
+ v.setDrawingCacheBackgroundColor(0);
+ float alpha = v.getAlpha();
+ v.setAlpha(1.0f);
+
+ if (color != 0) {
+ v.destroyDrawingCache();
+ }
+ v.buildDrawingCache();
+ Bitmap cacheBitmap = v.getDrawingCache();
+ if (cacheBitmap == null) {
+ Log.e(TAG, "failed getViewBitmap(" + v + ")", new RuntimeException());
+ return null;
+ }
+
+ Bitmap bitmap = Bitmap.createBitmap(cacheBitmap);
+
+ // Restore the view
+ v.destroyDrawingCache();
+ v.setAlpha(alpha);
+ v.setWillNotCacheDrawing(willNotCache);
+ v.setDrawingCacheBackgroundColor(color);
+
+ return bitmap;
+ }
+
+ /**
+ * Call this from a drag source view like this:
+ *
+ * <pre>
+ * @Override
+ * public boolean dispatchKeyEvent(KeyEvent event) {
+ * return mDragController.dispatchKeyEvent(this, event)
+ * || super.dispatchKeyEvent(event);
+ * </pre>
+ */
+ public boolean dispatchKeyEvent(KeyEvent event) {
+ return mDragging;
+ }
+
+ public boolean isDragging() {
+ return mDragging;
+ }
+
+ /**
+ * Stop dragging without dropping.
+ */
+ public void cancelDrag() {
+ if (mDragging) {
+ if (mLastDropTarget != null) {
+ mLastDropTarget.onDragExit(mDragObject);
+ }
+ mDragObject.deferDragViewCleanupPostAnimation = false;
+ mDragObject.cancelled = true;
+ mDragObject.dragComplete = true;
+ mDragObject.dragSource.onDropCompleted(null, mDragObject, false, false);
+ }
+ endDrag();
+ }
+ public void onAppsRemoved(ArrayList<ApplicationInfo> appInfos, Context context) {
+ // Cancel the current drag if we are removing an app that we are dragging
+ if (mDragObject != null) {
+ Object rawDragInfo = mDragObject.dragInfo;
+ if (rawDragInfo instanceof ShortcutInfo) {
+ ShortcutInfo dragInfo = (ShortcutInfo) rawDragInfo;
+ for (ApplicationInfo info : appInfos) {
+ // Added null checks to prevent NPE we've seen in the wild
+ if (dragInfo != null &&
+ dragInfo.intent != null) {
+ boolean isSameComponent =
+ dragInfo.intent.getComponent().equals(info.componentName);
+ if (isSameComponent) {
+ cancelDrag();
+ return;
+ }
+ }
+ }
+ }
+ }
+ }
+
+ private void endDrag() {
+ if (mDragging) {
+ mDragging = false;
+ clearScrollRunnable();
+ boolean isDeferred = false;
+ if (mDragObject.dragView != null) {
+ 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();
+ }
+ }
+
+ void onDeferredEndFling(DropTarget.DragObject d) {
+ d.dragSource.onFlingToDeleteCompleted();
+ }
+
+ /**
+ * Clamps the position to the drag layer bounds.
+ */
+ private int[] getClampedDragLayerPos(float x, float y) {
+ mLauncher.getDragLayer().getLocalVisibleRect(mDragLayerRect);
+ mTmpPoint[0] = (int) Math.max(mDragLayerRect.left, Math.min(x, mDragLayerRect.right - 1));
+ mTmpPoint[1] = (int) Math.max(mDragLayerRect.top, Math.min(y, mDragLayerRect.bottom - 1));
+ return mTmpPoint;
+ }
+
+ long getLastGestureUpTime() {
+ if (mDragging) {
+ return System.currentTimeMillis();
+ } else {
+ return mLastTouchUpTime;
+ }
+ }
+
+ void resetLastGestureUpTime() {
+ mLastTouchUpTime = -1;
+ }
+
+ /**
+ * Call this from a drag source view.
+ */
+ public boolean onInterceptTouchEvent(MotionEvent ev) {
+ @SuppressWarnings("all") // suppress dead code warning
+ final boolean debug = false;
+ if (debug) {
+ Log.d(Launcher.TAG, "DragController.onInterceptTouchEvent " + ev + " mDragging="
+ + mDragging);
+ }
+
+ // 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];
+
+ switch (action) {
+ case MotionEvent.ACTION_MOVE:
+ break;
+ case MotionEvent.ACTION_DOWN:
+ // Remember location of down touch
+ mMotionDownX = dragLayerX;
+ mMotionDownY = dragLayerY;
+ mLastDropTarget = null;
+ break;
+ case MotionEvent.ACTION_UP:
+ mLastTouchUpTime = System.currentTimeMillis();
+ if (mDragging) {
+ PointF vec = isFlingingToDelete(mDragObject.dragSource);
+ if (vec != null) {
+ dropOnFlingToDeleteTarget(dragLayerX, dragLayerY, vec);
+ } else {
+ drop(dragLayerX, dragLayerY);
+ }
+ }
+ endDrag();
+ break;
+ case MotionEvent.ACTION_CANCEL:
+ cancelDrag();
+ break;
+ }
+
+ return mDragging;
+ }
+
+ /**
+ * Sets the view that should handle move events.
+ */
+ void setMoveTarget(View view) {
+ mMoveTarget = view;
+ }
+
+ public boolean dispatchUnhandledMove(View focused, int direction) {
+ return mMoveTarget != null && mMoveTarget.dispatchUnhandledMove(focused, direction);
+ }
+
+ private void clearScrollRunnable() {
+ mHandler.removeCallbacks(mScrollRunnable);
+ if (mScrollState == SCROLL_WAITING_IN_ZONE) {
+ mScrollState = SCROLL_OUTSIDE_ZONE;
+ mScrollRunnable.setDirection(SCROLL_RIGHT);
+ mDragScroller.onExitScrollArea();
+ mLauncher.getDragLayer().onExitScrollArea();
+ }
+ }
+
+ private void handleMoveEvent(int x, int y) {
+ mDragObject.dragView.move(x, y);
+
+ // Drop on someone?
+ final int[] coordinates = mCoordinatesTemp;
+ DropTarget dropTarget = findDropTarget(x, y, coordinates);
+ mDragObject.x = coordinates[0];
+ mDragObject.y = coordinates[1];
+ checkTouchMove(dropTarget);
+
+ // Check if we are hovering over the scroll areas
+ mDistanceSinceScroll +=
+ Math.sqrt(Math.pow(mLastTouch[0] - x, 2) + Math.pow(mLastTouch[1] - y, 2));
+ mLastTouch[0] = x;
+ mLastTouch[1] = y;
+ checkScrollState(x, y);
+ }
+
+ public void forceTouchMove() {
+ int[] dummyCoordinates = mCoordinatesTemp;
+ DropTarget dropTarget = findDropTarget(mLastTouch[0], mLastTouch[1], dummyCoordinates);
+ checkTouchMove(dropTarget);
+ }
+
+ private void checkTouchMove(DropTarget dropTarget) {
+ if (dropTarget != null) {
+ DropTarget delegate = dropTarget.getDropTargetDelegate(mDragObject);
+ if (delegate != null) {
+ dropTarget = delegate;
+ }
+
+ if (mLastDropTarget != dropTarget) {
+ if (mLastDropTarget != null) {
+ mLastDropTarget.onDragExit(mDragObject);
+ }
+ dropTarget.onDragEnter(mDragObject);
+ }
+ dropTarget.onDragOver(mDragObject);
+ } else {
+ if (mLastDropTarget != null) {
+ mLastDropTarget.onDragExit(mDragObject);
+ }
+ }
+ mLastDropTarget = dropTarget;
+ }
+
+ private void checkScrollState(int x, int y) {
+ final int slop = ViewConfiguration.get(mLauncher).getScaledWindowTouchSlop();
+ final int delay = mDistanceSinceScroll < slop ? RESCROLL_DELAY : SCROLL_DELAY;
+ final DragLayer dragLayer = mLauncher.getDragLayer();
+ final boolean isRtl = (dragLayer.getLayoutDirection() == View.LAYOUT_DIRECTION_RTL);
+ final int forwardDirection = isRtl ? SCROLL_RIGHT : SCROLL_LEFT;
+ final int backwardsDirection = isRtl ? SCROLL_LEFT : SCROLL_RIGHT;
+
+ if (x < mScrollZone) {
+ if (mScrollState == SCROLL_OUTSIDE_ZONE) {
+ mScrollState = SCROLL_WAITING_IN_ZONE;
+ if (mDragScroller.onEnterScrollArea(x, y, forwardDirection)) {
+ dragLayer.onEnterScrollArea(forwardDirection);
+ mScrollRunnable.setDirection(forwardDirection);
+ mHandler.postDelayed(mScrollRunnable, delay);
+ }
+ }
+ } else if (x > mScrollView.getWidth() - mScrollZone) {
+ if (mScrollState == SCROLL_OUTSIDE_ZONE) {
+ mScrollState = SCROLL_WAITING_IN_ZONE;
+ if (mDragScroller.onEnterScrollArea(x, y, backwardsDirection)) {
+ dragLayer.onEnterScrollArea(backwardsDirection);
+ mScrollRunnable.setDirection(backwardsDirection);
+ mHandler.postDelayed(mScrollRunnable, delay);
+ }
+ }
+ } else {
+ clearScrollRunnable();
+ }
+ }
+
+ /**
+ * Call this from a drag source view.
+ */
+ public boolean onTouchEvent(MotionEvent ev) {
+ if (!mDragging) {
+ 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];
+ final int dragLayerY = dragLayerPos[1];
+
+ switch (action) {
+ case MotionEvent.ACTION_DOWN:
+ // Remember where the motion event started
+ mMotionDownX = dragLayerX;
+ mMotionDownY = dragLayerY;
+
+ if ((dragLayerX < mScrollZone) || (dragLayerX > mScrollView.getWidth() - mScrollZone)) {
+ mScrollState = SCROLL_WAITING_IN_ZONE;
+ mHandler.postDelayed(mScrollRunnable, SCROLL_DELAY);
+ } else {
+ mScrollState = SCROLL_OUTSIDE_ZONE;
+ }
+ break;
+ case MotionEvent.ACTION_MOVE:
+ handleMoveEvent(dragLayerX, dragLayerY);
+ break;
+ 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) {
+ PointF vec = isFlingingToDelete(mDragObject.dragSource);
+ if (vec != null) {
+ dropOnFlingToDeleteTarget(dragLayerX, dragLayerY, vec);
+ } else {
+ drop(dragLayerX, dragLayerY);
+ }
+ }
+ endDrag();
+ break;
+ case MotionEvent.ACTION_CANCEL:
+ mHandler.removeCallbacks(mScrollRunnable);
+ cancelDrag();
+ break;
+ }
+
+ 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];
+
+ // 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);
+ // We must set dragComplete to true _only_ after we "enter" the fling-to-delete target for
+ // "drop"
+ mDragObject.dragComplete = true;
+ mFlingToDeleteDropTarget.onDragExit(mDragObject);
+ if (mFlingToDeleteDropTarget.acceptDrop(mDragObject)) {
+ mFlingToDeleteDropTarget.onFlingToDelete(mDragObject, mDragObject.x, mDragObject.y,
+ vel);
+ accepted = true;
+ }
+ mDragObject.dragSource.onDropCompleted((View) mFlingToDeleteDropTarget, mDragObject, true,
+ accepted);
+ }
+
+ private void drop(float x, float y) {
+ final int[] coordinates = mCoordinatesTemp;
+ final DropTarget dropTarget = findDropTarget((int) x, (int) y, coordinates);
+
+ mDragObject.x = coordinates[0];
+ mDragObject.y = coordinates[1];
+ boolean accepted = false;
+ if (dropTarget != null) {
+ mDragObject.dragComplete = true;
+ dropTarget.onDragExit(mDragObject);
+ if (dropTarget.acceptDrop(mDragObject)) {
+ dropTarget.onDrop(mDragObject);
+ accepted = true;
+ }
+ }
+ mDragObject.dragSource.onDropCompleted((View) dropTarget, mDragObject, false, accepted);
+ }
+
+ private DropTarget findDropTarget(int x, int y, int[] dropCoordinates) {
+ final Rect r = mRectTemp;
+
+ final ArrayList<DropTarget> dropTargets = mDropTargets;
+ final int count = dropTargets.size();
+ for (int i=count-1; i>=0; i--) {
+ DropTarget target = dropTargets.get(i);
+ if (!target.isDropEnabled())
+ continue;
+
+ target.getHitRect(r);
+
+ // Convert the hit rect to DragLayer coordinates
+ target.getLocationInDragLayer(dropCoordinates);
+ r.offset(dropCoordinates[0] - target.getLeft(), dropCoordinates[1] - target.getTop());
+
+ mDragObject.x = x;
+ mDragObject.y = y;
+ if (r.contains(x, y)) {
+ DropTarget delegate = target.getDropTargetDelegate(mDragObject);
+ if (delegate != null) {
+ target = delegate;
+ target.getLocationInDragLayer(dropCoordinates);
+ }
+
+ // Make dropCoordinates relative to the DropTarget
+ dropCoordinates[0] = x - dropCoordinates[0];
+ dropCoordinates[1] = y - dropCoordinates[1];
+
+ return target;
+ }
+ }
+ return null;
+ }
+
+ public void setDragScoller(DragScroller scroller) {
+ mDragScroller = scroller;
+ }
+
+ public void setWindowToken(IBinder token) {
+ mWindowToken = token;
+ }
+
+ /**
+ * Sets the drag listner which will be notified when a drag starts or ends.
+ */
+ public void addDragListener(DragListener l) {
+ mListeners.add(l);
+ }
+
+ /**
+ * Remove a previously installed drag listener.
+ */
+ public void removeDragListener(DragListener l) {
+ mListeners.remove(l);
+ }
+
+ /**
+ * Add a DropTarget to the list of potential places to receive drop events.
+ */
+ public void addDropTarget(DropTarget target) {
+ mDropTargets.add(target);
+ }
+
+ /**
+ * Don't send drop events to <em>target</em> any more.
+ */
+ public void removeDropTarget(DropTarget target) {
+ 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.
+ */
+ public void setScrollView(View v) {
+ mScrollView = v;
+ }
+
+ DragView getDragView() {
+ return mDragObject.dragView;
+ }
+
+ private class ScrollRunnable implements Runnable {
+ private int mDirection;
+
+ ScrollRunnable() {
+ }
+
+ public void run() {
+ if (mDragScroller != null) {
+ if (mDirection == SCROLL_LEFT) {
+ mDragScroller.scrollLeft();
+ } else {
+ mDragScroller.scrollRight();
+ }
+ mScrollState = SCROLL_OUTSIDE_ZONE;
+ mDistanceSinceScroll = 0;
+ mDragScroller.onExitScrollArea();
+ mLauncher.getDragLayer().onExitScrollArea();
+
+ if (isDragging()) {
+ // Check the scroll again so that we can requeue the scroller if necessary
+ checkScrollState(mLastTouch[0], mLastTouch[1]);
+ }
+ }
+ }
+
+ void setDirection(int direction) {
+ mDirection = direction;
+ }
+ }
+}