diff options
author | Joe Onorato <joeo@android.com> | 2009-08-04 16:04:30 -0400 |
---|---|---|
committer | Joe Onorato <joeo@android.com> | 2009-08-06 12:45:43 -0700 |
commit | 00acb123c5100f06b8e89e8ec8978ebafc6f6d26 (patch) | |
tree | 77c09f4bcd3174e687ce4d5f31a0f89a55e52bb8 /src/com/android/launcher2/DragController.java | |
parent | 17721426111214db82ea00abd8bfafc66e65fe71 (diff) | |
download | packages_apps_trebuchet-00acb123c5100f06b8e89e8ec8978ebafc6f6d26.zip packages_apps_trebuchet-00acb123c5100f06b8e89e8ec8978ebafc6f6d26.tar.gz packages_apps_trebuchet-00acb123c5100f06b8e89e8ec8978ebafc6f6d26.tar.bz2 |
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.java | 467 |
1 files changed, 451 insertions, 16 deletions
diff --git a/src/com/android/launcher2/DragController.java b/src/com/android/launcher2/DragController.java index f8c8077..da2df5c 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; + } + } } |