summaryrefslogtreecommitdiffstats
path: root/src/com/android/launcher2/DragController.java
diff options
context:
space:
mode:
authorJoe Onorato <joeo@android.com>2009-08-04 16:04:30 -0400
committerJoe Onorato <joeo@android.com>2009-08-06 12:45:43 -0700
commit00acb123c5100f06b8e89e8ec8978ebafc6f6d26 (patch)
tree77c09f4bcd3174e687ce4d5f31a0f89a55e52bb8 /src/com/android/launcher2/DragController.java
parent17721426111214db82ea00abd8bfafc66e65fe71 (diff)
downloadandroid_packages_apps_Trebuchet-00acb123c5100f06b8e89e8ec8978ebafc6f6d26.tar.gz
android_packages_apps_Trebuchet-00acb123c5100f06b8e89e8ec8978ebafc6f6d26.tar.bz2
android_packages_apps_Trebuchet-00acb123c5100f06b8e89e8ec8978ebafc6f6d26.zip
Move the drag thing into its own window that goes around on top of everything else.
Diffstat (limited to 'src/com/android/launcher2/DragController.java')
-rw-r--r--src/com/android/launcher2/DragController.java467
1 files changed, 451 insertions, 16 deletions
diff --git a/src/com/android/launcher2/DragController.java b/src/com/android/launcher2/DragController.java
index f8c80775d..da2df5c97 100644
--- a/src/com/android/launcher2/DragController.java
+++ b/src/com/android/launcher2/DragController.java
@@ -16,14 +16,108 @@
package com.android.launcher2;
+import android.content.Context;
+import android.graphics.Bitmap;
+import android.graphics.Canvas;
+import android.graphics.Matrix;
+import android.graphics.Rect;
+import android.graphics.RectF;
+import android.os.IBinder;
+import android.os.Handler;
+import android.os.Vibrator;
+import android.os.SystemClock;
+import android.util.AttributeSet;
+import android.util.Log;
import android.view.View;
+import android.view.ViewGroup;
+import android.view.KeyEvent;
+import android.view.MotionEvent;
+import android.view.inputmethod.InputMethodManager;
+import android.widget.FrameLayout;
+import android.widget.ImageView;
+
+import java.util.ArrayList;
/**
- * Interface for initiating a drag within a view or across multiple views.
- *
+ * Class for initiating a drag within a view or across multiple views.
*/
-public interface DragController {
-
+public class 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 = 600;
+ private static final int SCROLL_ZONE = 20;
+ private static final int VIBRATE_DURATION = 35;
+
+ 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;
+
+ private static final int SCROLL_LEFT = 0;
+ private static final int SCROLL_RIGHT = 1;
+
+ private Context mContext;
+ private Handler mHandler;
+ private final Vibrator mVibrator = new Vibrator();
+
+ // 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 float mMotionDownX;
+
+ /** Y coordinate of the down event. */
+ private float mMotionDownY;
+
+ /** Original view that is being dragged. */
+ private View mOriginator;
+
+ /** The contents of mOriginator with no scaling. */
+ private Bitmap mDragBitmap;
+
+ /** X offset from the upper-left corner of the cell to where we touched. */
+ private float mTouchOffsetX;
+
+ /** Y offset from the upper-left corner of the cell to where we touched. */
+ private float mTouchOffsetY;
+
+ /** Where the drag originated */
+ private DragSource mDragSource;
+
+ /** The data associated with the object being dragged */
+ private Object mDragInfo;
+
+ /** The view that moves around while you drag. */
+ private DragView mDragView;
+
+ /** Who can receive drop events */
+ private ArrayList<DropTarget> mDropTargets = new ArrayList<DropTarget>();
+
+ private DragListener mListener;
+
+ /** 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 DragScroller mDragScroller;
+ private int mScrollState = SCROLL_OUTSIDE_ZONE;
+ private ScrollRunnable mScrollRunnable = new ScrollRunnable();
+
+ private RectF mDeleteRegion;
+ private DropTarget mLastDropTarget;
+
+ private InputMethodManager mInputMethodManager;
+
/**
* Interface to receive notifications when a drag starts or stops
*/
@@ -47,14 +141,15 @@ public interface DragController {
}
/**
- * Indicates the drag is a move.
- */
- public static int DRAG_ACTION_MOVE = 0;
-
- /**
- * Indicates the drag is a copy.
+ * Used to create a new DragLayer from XML.
+ *
+ * @param context The application's context.
+ * @param attrs The attribtues set containing the Workspace's customization values.
*/
- public static int DRAG_ACTION_COPY = 1;
+ public DragController(Context context) {
+ mContext = context;
+ mHandler = new Handler();
+ }
/**
* Starts a drag
@@ -65,15 +160,355 @@ public interface DragController {
* @param dragAction The drag action: either {@link #DRAG_ACTION_MOVE} or
* {@link #DRAG_ACTION_COPY}
*/
- void startDrag(View v, DragSource source, Object info, int dragAction);
-
+ public void startDrag(View v, DragSource source, Object dragInfo, int dragAction) {
+ if (PROFILE_DRAWING_DURING_DRAG) {
+ android.os.Debug.startMethodTracing("Launcher");
+ }
+
+ // Hide soft keyboard, if visible
+ if (mInputMethodManager == null) {
+ mInputMethodManager = (InputMethodManager)
+ mContext.getSystemService(Context.INPUT_METHOD_SERVICE);
+ }
+ mInputMethodManager.hideSoftInputFromWindow(mWindowToken, 0);
+
+ if (mListener != null) {
+ mListener.onDragStart(v, source, dragInfo, dragAction);
+ }
+
+ int[] loc = mCoordinatesTemp;
+ v.getLocationOnScreen(loc);
+ int screenX = loc[0];
+ int screenY = loc[1];
+
+ int registrationX = ((int)mMotionDownX) - screenX;
+ int registrationY = ((int)mMotionDownY) - screenY;
+
+ mTouchOffsetX = mMotionDownX - screenX;
+ mTouchOffsetY = mMotionDownY - screenY;
+
+ mDragging = true;
+ mOriginator = v;
+ mDragSource = source;
+ mDragInfo = dragInfo;
+
+ mVibrator.vibrate(VIBRATE_DURATION);
+
+ mDragBitmap = getViewBitmap(v);
+ DragView dragView = mDragView = new DragView(mContext, mDragBitmap,
+ registrationX, registrationY);
+ dragView.show(mWindowToken, (int)mMotionDownX, (int)mMotionDownY);
+
+ if (dragAction == DRAG_ACTION_MOVE) {
+ v.setVisibility(View.GONE);
+ }
+ }
+
+ /**
+ * Draw the view into a bitmap.
+ */
+ private 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);
+
+ if (color != 0) {
+ v.destroyDrawingCache();
+ }
+ v.buildDrawingCache();
+ Bitmap cacheBitmap = v.getDrawingCache();
+
+ Bitmap bitmap = Bitmap.createBitmap(cacheBitmap);
+
+ // Restore the view
+ v.destroyDrawingCache();
+ 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;
+ }
+
+ private void endDrag() {
+ if (mDragging) {
+ mDragging = false;
+ if (mOriginator != null) {
+ mOriginator.setVisibility(View.VISIBLE);
+ }
+ if (mListener != null) {
+ mListener.onDragEnd();
+ }
+ if (mDragView != null) {
+ mDragView.remove();
+ mDragView = null;
+ }
+ if (mDragBitmap != null) {
+ mDragBitmap.recycle();
+ mDragBitmap = null;
+ }
+ }
+ }
+
+ /**
+ * Call this from a drag source view.
+ */
+ public boolean onInterceptTouchEvent(MotionEvent ev) {
+ final int action = ev.getAction();
+
+ final float screenX = ev.getRawX();
+ final float screenY = ev.getRawY();
+
+ switch (action) {
+ case MotionEvent.ACTION_MOVE:
+ break;
+
+ case MotionEvent.ACTION_DOWN:
+ // Remember location of down touch
+ mMotionDownX = screenX;
+ mMotionDownY = screenY;
+ mLastDropTarget = null;
+ break;
+
+ case MotionEvent.ACTION_CANCEL:
+ case MotionEvent.ACTION_UP:
+ if (mDragging) {
+ drop(screenX, screenY);
+ }
+ endDrag();
+ break;
+ }
+
+ return mDragging;
+ }
+
+ /**
+ * Call this from a drag source view.
+ */
+ public boolean onTouchEvent(MotionEvent ev) {
+ View scrollView = mScrollView;
+
+ if (!mDragging) {
+ return false;
+ }
+
+ final int action = ev.getAction();
+ final float x = ev.getRawX();
+ final float y = ev.getRawY();
+
+ switch (action) {
+ case MotionEvent.ACTION_DOWN:
+
+ // Remember where the motion event started
+ mMotionDownX = x;
+ mMotionDownY = y;
+
+ if ((x < SCROLL_ZONE) || (x > scrollView.getWidth() - SCROLL_ZONE)) {
+ mScrollState = SCROLL_WAITING_IN_ZONE;
+ mHandler.postDelayed(mScrollRunnable, SCROLL_DELAY);
+ } else {
+ mScrollState = SCROLL_OUTSIDE_ZONE;
+ }
+
+ break;
+ case MotionEvent.ACTION_MOVE:
+ // Update the drag view.
+ mDragView.move((int)ev.getRawX(), (int)ev.getRawY());
+
+ // Drop on someone?
+ final int[] coordinates = mCoordinatesTemp;
+ DropTarget dropTarget = findDropTarget((int) x, (int) y, coordinates);
+ if (dropTarget != null) {
+ if (mLastDropTarget == dropTarget) {
+ dropTarget.onDragOver(mDragSource, coordinates[0], coordinates[1],
+ (int) mTouchOffsetX, (int) mTouchOffsetY, mDragView, mDragInfo);
+ } else {
+ if (mLastDropTarget != null) {
+ mLastDropTarget.onDragExit(mDragSource, coordinates[0], coordinates[1],
+ (int) mTouchOffsetX, (int) mTouchOffsetY, mDragView, mDragInfo);
+ }
+ dropTarget.onDragEnter(mDragSource, coordinates[0], coordinates[1],
+ (int) mTouchOffsetX, (int) mTouchOffsetY, mDragView, mDragInfo);
+ }
+ } else {
+ if (mLastDropTarget != null) {
+ mLastDropTarget.onDragExit(mDragSource, coordinates[0], coordinates[1],
+ (int) mTouchOffsetX, (int) mTouchOffsetY, mDragView, mDragInfo);
+ }
+ }
+ mLastDropTarget = dropTarget;
+
+ // Scroll, maybe, but not if we're in the delete region.
+ boolean inDeleteRegion = false;
+ if (mDeleteRegion != null) {
+ inDeleteRegion = mDeleteRegion.contains(ev.getRawX(), ev.getRawY());
+ }
+ if (!inDeleteRegion && x < SCROLL_ZONE) {
+ if (mScrollState == SCROLL_OUTSIDE_ZONE) {
+ mScrollState = SCROLL_WAITING_IN_ZONE;
+ mScrollRunnable.setDirection(SCROLL_LEFT);
+ mHandler.postDelayed(mScrollRunnable, SCROLL_DELAY);
+ }
+ } else if (!inDeleteRegion && x > scrollView.getWidth() - SCROLL_ZONE) {
+ if (mScrollState == SCROLL_OUTSIDE_ZONE) {
+ mScrollState = SCROLL_WAITING_IN_ZONE;
+ mScrollRunnable.setDirection(SCROLL_RIGHT);
+ mHandler.postDelayed(mScrollRunnable, SCROLL_DELAY);
+ }
+ } else {
+ if (mScrollState == SCROLL_WAITING_IN_ZONE) {
+ mScrollState = SCROLL_OUTSIDE_ZONE;
+ mScrollRunnable.setDirection(SCROLL_RIGHT);
+ mHandler.removeCallbacks(mScrollRunnable);
+ }
+ }
+
+ break;
+ case MotionEvent.ACTION_UP:
+ mHandler.removeCallbacks(mScrollRunnable);
+ if (mDragging) {
+ drop(x, y);
+ }
+ endDrag();
+
+ break;
+ case MotionEvent.ACTION_CANCEL:
+ endDrag();
+ }
+
+ return true;
+ }
+
+ private boolean drop(float x, float y) {
+ final int[] coordinates = mCoordinatesTemp;
+ DropTarget dropTarget = findDropTarget((int) x, (int) y, coordinates);
+
+ if (dropTarget != null) {
+ dropTarget.onDragExit(mDragSource, coordinates[0], coordinates[1],
+ (int) mTouchOffsetX, (int) mTouchOffsetY, mDragView, mDragInfo);
+ if (dropTarget.acceptDrop(mDragSource, coordinates[0], coordinates[1],
+ (int) mTouchOffsetX, (int) mTouchOffsetY, mDragView, mDragInfo)) {
+ dropTarget.onDrop(mDragSource, coordinates[0], coordinates[1],
+ (int) mTouchOffsetX, (int) mTouchOffsetY, mDragView, mDragInfo);
+ mDragSource.onDropCompleted((View) dropTarget, true);
+ return true;
+ } else {
+ mDragSource.onDropCompleted((View) dropTarget, false);
+ return true;
+ }
+ }
+ return false;
+ }
+
+ 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--) {
+ final DropTarget target = dropTargets.get(i);
+ target.getHitRect(r);
+ target.getLocationOnScreen(dropCoordinates);
+ r.offset(dropCoordinates[0] - target.getLeft(), dropCoordinates[1] - target.getTop());
+ if (r.contains(x, y)) {
+ 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.
*/
- void setDragListener(DragListener l);
-
+ public void setDragListener(DragListener l) {
+ mListener = l;
+ }
+
/**
* Remove a previously installed drag listener.
*/
- void removeDragListener(DragListener l);
+ public void removeDragListener(DragListener l) {
+ mListener = null;
+ }
+
+ /**
+ * 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);
+ }
+
+ /**
+ * Set which view scrolls for touch events near the edge of the screen.
+ */
+ public void setScrollView(View v) {
+ mScrollView = v;
+ }
+
+ /**
+ * Specifies the delete region. We won't scroll on touch events over the delete region.
+ *
+ * @param region The rectangle in screen coordinates of the delete region.
+ */
+ void setDeleteRegion(RectF region) {
+ mDeleteRegion = region;
+ }
+
+ 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;
+ }
+ }
+
+ void setDirection(int direction) {
+ mDirection = direction;
+ }
+ }
}