mirror of
https://github.com/LawnchairLauncher/lawnchair.git
synced 2026-03-03 01:16:49 +00:00
Move the drag thing into its own window that goes around on top of everything else.
This commit is contained in:
@@ -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.
|
||||
* 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_MOVE = 0;
|
||||
|
||||
/**
|
||||
* Indicates the drag is a copy.
|
||||
*/
|
||||
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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user