4a9e1e2117
Bug: 147899070 Change-Id: I73bc7d5addde5fb3304cac29f0ecce17b2cc48ea
699 lines
24 KiB
Java
699 lines
24 KiB
Java
/*
|
|
* 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.dragndrop;
|
|
|
|
import static com.android.launcher3.AbstractFloatingView.TYPE_DISCOVERY_BOUNCE;
|
|
import static com.android.launcher3.LauncherAnimUtils.SPRING_LOADED_EXIT_DELAY;
|
|
import static com.android.launcher3.LauncherState.NORMAL;
|
|
import static com.android.launcher3.Utilities.ATLEAST_Q;
|
|
|
|
import android.animation.ValueAnimator;
|
|
import android.content.ComponentName;
|
|
import android.content.res.Resources;
|
|
import android.graphics.Bitmap;
|
|
import android.graphics.Point;
|
|
import android.graphics.Rect;
|
|
import android.os.IBinder;
|
|
import android.view.DragEvent;
|
|
import android.view.HapticFeedbackConstants;
|
|
import android.view.KeyEvent;
|
|
import android.view.MotionEvent;
|
|
import android.view.View;
|
|
|
|
import com.android.launcher3.AbstractFloatingView;
|
|
import com.android.launcher3.DragSource;
|
|
import com.android.launcher3.DropTarget;
|
|
import com.android.launcher3.ItemInfo;
|
|
import com.android.launcher3.Launcher;
|
|
import com.android.launcher3.R;
|
|
import com.android.launcher3.WorkspaceItemInfo;
|
|
import com.android.launcher3.accessibility.DragViewStateAnnouncer;
|
|
import com.android.launcher3.util.ItemInfoMatcher;
|
|
import com.android.launcher3.util.Thunk;
|
|
import com.android.launcher3.util.TouchController;
|
|
import com.android.launcher3.util.UiThreadHelper;
|
|
|
|
import java.util.ArrayList;
|
|
|
|
/**
|
|
* Class for initiating a drag within a view or across multiple views.
|
|
*/
|
|
public class DragController implements DragDriver.EventListener, TouchController {
|
|
private static final boolean PROFILE_DRAWING_DURING_DRAG = false;
|
|
|
|
/**
|
|
* When a drag is started from a deep press, you need to drag this much farther than normal to
|
|
* end a pre-drag. See {@link DragOptions.PreDragCondition#shouldStartDrag(double)}.
|
|
*/
|
|
private static final int DEEP_PRESS_DISTANCE_FACTOR = 3;
|
|
|
|
@Thunk Launcher mLauncher;
|
|
private FlingToDeleteHelper mFlingToDeleteHelper;
|
|
|
|
// temporaries to avoid gc thrash
|
|
private Rect mRectTemp = new Rect();
|
|
private final int[] mCoordinatesTemp = new int[2];
|
|
|
|
/**
|
|
* Drag driver for the current drag/drop operation, or null if there is no active DND operation.
|
|
* It's null during accessible drag operations.
|
|
*/
|
|
private DragDriver mDragDriver = null;
|
|
|
|
/** Options controlling the drag behavior. */
|
|
private DragOptions mOptions;
|
|
|
|
/** X coordinate of the down event. */
|
|
private int mMotionDownX;
|
|
|
|
/** Y coordinate of the down event. */
|
|
private int mMotionDownY;
|
|
|
|
private DropTarget.DragObject mDragObject;
|
|
|
|
/** Who can receive drop events */
|
|
private ArrayList<DropTarget> mDropTargets = new ArrayList<>();
|
|
private ArrayList<DragListener> mListeners = new ArrayList<>();
|
|
|
|
/** The window token used as the parent for the DragView. */
|
|
private IBinder mWindowToken;
|
|
|
|
private View mMoveTarget;
|
|
|
|
private DropTarget mLastDropTarget;
|
|
|
|
private final int[] mLastTouch = new int[2];
|
|
private long mLastTouchUpTime = -1;
|
|
private int mLastTouchClassification;
|
|
private int mDistanceSinceScroll = 0;
|
|
|
|
private int mTmpPoint[] = new int[2];
|
|
private Rect mDragLayerRect = new Rect();
|
|
|
|
private boolean mIsInPreDrag;
|
|
|
|
/**
|
|
* Interface to receive notifications when a drag starts or stops
|
|
*/
|
|
public interface DragListener {
|
|
/**
|
|
* A drag has begun
|
|
*
|
|
* @param dragObject The object being dragged
|
|
* @param options Options used to start the drag
|
|
*/
|
|
void onDragStart(DropTarget.DragObject dragObject, DragOptions options);
|
|
|
|
/**
|
|
* The drag has ended
|
|
*/
|
|
void onDragEnd();
|
|
}
|
|
|
|
/**
|
|
* Used to create a new DragLayer from XML.
|
|
*/
|
|
public DragController(Launcher launcher) {
|
|
mLauncher = launcher;
|
|
mFlingToDeleteHelper = new FlingToDeleteHelper(launcher);
|
|
}
|
|
|
|
/**
|
|
* Starts a drag.
|
|
* When the drag is started, the UI automatically goes into spring loaded mode. On a successful
|
|
* drop, it is the responsibility of the {@link DropTarget} to exit out of the spring loaded
|
|
* mode. If the drop was cancelled for some reason, the UI will automatically exit out of this mode.
|
|
*
|
|
* @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 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 DragView startDrag(Bitmap b, int dragLayerX, int dragLayerY,
|
|
DragSource source, ItemInfo dragInfo, Point dragOffset, Rect dragRegion,
|
|
float initialDragViewScale, float dragViewScaleOnDrop, DragOptions options) {
|
|
if (PROFILE_DRAWING_DURING_DRAG) {
|
|
android.os.Debug.startMethodTracing("Launcher");
|
|
}
|
|
|
|
// Hide soft keyboard, if visible
|
|
UiThreadHelper.hideKeyboardAsync(mLauncher, mWindowToken);
|
|
AbstractFloatingView.closeOpenViews(mLauncher, false, TYPE_DISCOVERY_BOUNCE);
|
|
|
|
mOptions = options;
|
|
if (mOptions.systemDndStartPoint != null) {
|
|
mLastTouch[0] = mMotionDownX = mOptions.systemDndStartPoint.x;
|
|
mLastTouch[1] = mMotionDownY = mOptions.systemDndStartPoint.y;
|
|
}
|
|
|
|
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;
|
|
|
|
mLastDropTarget = null;
|
|
|
|
mDragObject = new DropTarget.DragObject(mLauncher.getApplicationContext());
|
|
|
|
mIsInPreDrag = mOptions.preDragCondition != null
|
|
&& !mOptions.preDragCondition.shouldStartDrag(0);
|
|
|
|
final Resources res = mLauncher.getResources();
|
|
final float scaleDps = mIsInPreDrag
|
|
? res.getDimensionPixelSize(R.dimen.pre_drag_view_scale) : 0f;
|
|
final DragView dragView = mDragObject.dragView = new DragView(mLauncher, b, registrationX,
|
|
registrationY, initialDragViewScale, dragViewScaleOnDrop, scaleDps);
|
|
dragView.setItemInfo(dragInfo);
|
|
mDragObject.dragComplete = false;
|
|
if (mOptions.isAccessibleDrag) {
|
|
// For an accessible drag, we assume the view is being dragged from the center.
|
|
mDragObject.xOffset = b.getWidth() / 2;
|
|
mDragObject.yOffset = b.getHeight() / 2;
|
|
mDragObject.accessibleDrag = true;
|
|
} else {
|
|
mDragObject.xOffset = mMotionDownX - (dragLayerX + dragRegionLeft);
|
|
mDragObject.yOffset = mMotionDownY - (dragLayerY + dragRegionTop);
|
|
mDragObject.stateAnnouncer = DragViewStateAnnouncer.createFor(dragView);
|
|
|
|
mDragDriver = DragDriver.create(mLauncher, this, mDragObject, mOptions);
|
|
}
|
|
|
|
mDragObject.dragSource = source;
|
|
mDragObject.dragInfo = dragInfo;
|
|
mDragObject.originalDragInfo = new ItemInfo();
|
|
mDragObject.originalDragInfo.copyFrom(dragInfo);
|
|
|
|
if (dragOffset != null) {
|
|
dragView.setDragVisualizeOffset(new Point(dragOffset));
|
|
}
|
|
if (dragRegion != null) {
|
|
dragView.setDragRegion(new Rect(dragRegion));
|
|
}
|
|
|
|
mLauncher.getDragLayer().performHapticFeedback(HapticFeedbackConstants.LONG_PRESS);
|
|
dragView.show(mLastTouch[0], mLastTouch[1]);
|
|
mDistanceSinceScroll = 0;
|
|
|
|
if (!mIsInPreDrag) {
|
|
callOnDragStart();
|
|
} else if (mOptions.preDragCondition != null) {
|
|
mOptions.preDragCondition.onPreDragStart(mDragObject);
|
|
}
|
|
|
|
handleMoveEvent(mLastTouch[0], mLastTouch[1]);
|
|
mLauncher.getUserEventDispatcher().resetActionDurationMillis();
|
|
return dragView;
|
|
}
|
|
|
|
private void callOnDragStart() {
|
|
if (mOptions.preDragCondition != null) {
|
|
mOptions.preDragCondition.onPreDragEnd(mDragObject, true /* dragStarted*/);
|
|
}
|
|
mIsInPreDrag = false;
|
|
for (DragListener listener : new ArrayList<>(mListeners)) {
|
|
listener.onDragStart(mDragObject, mOptions);
|
|
}
|
|
}
|
|
|
|
public void addFirstFrameAnimationHelper(ValueAnimator anim) {
|
|
if (mDragObject != null && mDragObject.dragView != null) {
|
|
mDragObject.dragView.mFirstFrameAnimatorHelper.addTo(anim);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* 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 mDragDriver != null;
|
|
}
|
|
|
|
public boolean isDragging() {
|
|
return mDragDriver != null || (mOptions != null && mOptions.isAccessibleDrag);
|
|
}
|
|
|
|
/**
|
|
* Stop dragging without dropping.
|
|
*/
|
|
public void cancelDrag() {
|
|
if (isDragging()) {
|
|
if (mLastDropTarget != null) {
|
|
mLastDropTarget.onDragExit(mDragObject);
|
|
}
|
|
mDragObject.deferDragViewCleanupPostAnimation = false;
|
|
mDragObject.cancelled = true;
|
|
mDragObject.dragComplete = true;
|
|
if (!mIsInPreDrag) {
|
|
dispatchDropComplete(null, false);
|
|
}
|
|
}
|
|
endDrag();
|
|
}
|
|
|
|
private void dispatchDropComplete(View dropTarget, boolean accepted) {
|
|
if (!accepted) {
|
|
// If it was not accepted, cleanup the state. If it was accepted, it is the
|
|
// responsibility of the drop target to cleanup the state.
|
|
mLauncher.getStateManager().goToState(NORMAL, SPRING_LOADED_EXIT_DELAY);
|
|
mDragObject.deferDragViewCleanupPostAnimation = false;
|
|
}
|
|
|
|
mDragObject.dragSource.onDropCompleted(dropTarget, mDragObject, accepted);
|
|
}
|
|
|
|
public void onAppsRemoved(ItemInfoMatcher matcher) {
|
|
// Cancel the current drag if we are removing an app that we are dragging
|
|
if (mDragObject != null) {
|
|
ItemInfo dragInfo = mDragObject.dragInfo;
|
|
if (dragInfo instanceof WorkspaceItemInfo) {
|
|
ComponentName cn = dragInfo.getTargetComponent();
|
|
if (cn != null && matcher.matches(dragInfo, cn)) {
|
|
cancelDrag();
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
private void endDrag() {
|
|
if (isDragging()) {
|
|
mDragDriver = null;
|
|
boolean isDeferred = false;
|
|
if (mDragObject.dragView != null) {
|
|
isDeferred = mDragObject.deferDragViewCleanupPostAnimation;
|
|
if (!isDeferred) {
|
|
mDragObject.dragView.remove();
|
|
} else if (mIsInPreDrag) {
|
|
animateDragViewToOriginalPosition(null, null, -1);
|
|
}
|
|
mDragObject.dragView = null;
|
|
}
|
|
|
|
// Only end the drag if we are not deferred
|
|
if (!isDeferred) {
|
|
callOnDragEnd();
|
|
}
|
|
}
|
|
|
|
mFlingToDeleteHelper.releaseVelocityTracker();
|
|
}
|
|
|
|
public void animateDragViewToOriginalPosition(final Runnable onComplete,
|
|
final View originalIcon, int duration) {
|
|
Runnable onCompleteRunnable = new Runnable() {
|
|
@Override
|
|
public void run() {
|
|
if (originalIcon != null) {
|
|
originalIcon.setVisibility(View.VISIBLE);
|
|
}
|
|
if (onComplete != null) {
|
|
onComplete.run();
|
|
}
|
|
}
|
|
};
|
|
mDragObject.dragView.animateTo(mMotionDownX, mMotionDownY, onCompleteRunnable, duration);
|
|
}
|
|
|
|
private void callOnDragEnd() {
|
|
if (mIsInPreDrag && mOptions.preDragCondition != null) {
|
|
mOptions.preDragCondition.onPreDragEnd(mDragObject, false /* dragStarted*/);
|
|
}
|
|
mIsInPreDrag = false;
|
|
mOptions = null;
|
|
for (DragListener listener : new ArrayList<>(mListeners)) {
|
|
listener.onDragEnd();
|
|
}
|
|
}
|
|
|
|
/**
|
|
* This only gets called as a result of drag view cleanup being deferred in endDrag();
|
|
*/
|
|
void onDeferredEndDrag(DragView dragView) {
|
|
dragView.remove();
|
|
|
|
if (mDragObject.deferDragViewCleanupPostAnimation) {
|
|
// If we skipped calling onDragEnd() before, do it now
|
|
callOnDragEnd();
|
|
}
|
|
}
|
|
|
|
/**
|
|
* 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;
|
|
}
|
|
|
|
public long getLastGestureUpTime() {
|
|
if (mDragDriver != null) {
|
|
return System.currentTimeMillis();
|
|
} else {
|
|
return mLastTouchUpTime;
|
|
}
|
|
}
|
|
|
|
public void resetLastGestureUpTime() {
|
|
mLastTouchUpTime = -1;
|
|
}
|
|
|
|
@Override
|
|
public void onDriverDragMove(float x, float y) {
|
|
final int[] dragLayerPos = getClampedDragLayerPos(x, y);
|
|
|
|
handleMoveEvent(dragLayerPos[0], dragLayerPos[1]);
|
|
}
|
|
|
|
@Override
|
|
public void onDriverDragExitWindow() {
|
|
if (mLastDropTarget != null) {
|
|
mLastDropTarget.onDragExit(mDragObject);
|
|
mLastDropTarget = null;
|
|
}
|
|
}
|
|
|
|
@Override
|
|
public void onDriverDragEnd(float x, float y) {
|
|
DropTarget dropTarget;
|
|
Runnable flingAnimation = mFlingToDeleteHelper.getFlingAnimation(mDragObject, mOptions);
|
|
if (flingAnimation != null) {
|
|
dropTarget = mFlingToDeleteHelper.getDropTarget();
|
|
} else {
|
|
dropTarget = findDropTarget((int) x, (int) y, mCoordinatesTemp);
|
|
}
|
|
|
|
drop(dropTarget, flingAnimation);
|
|
|
|
endDrag();
|
|
}
|
|
|
|
@Override
|
|
public void onDriverDragCancel() {
|
|
cancelDrag();
|
|
}
|
|
|
|
/**
|
|
* Call this from a drag source view.
|
|
*/
|
|
public boolean onControllerInterceptTouchEvent(MotionEvent ev) {
|
|
if (mOptions != null && mOptions.isAccessibleDrag) {
|
|
return false;
|
|
}
|
|
|
|
// Update the velocity tracker
|
|
mFlingToDeleteHelper.recordMotionEvent(ev);
|
|
|
|
final int action = ev.getAction();
|
|
final int[] dragLayerPos = getClampedDragLayerPos(ev.getX(), ev.getY());
|
|
final int dragLayerX = dragLayerPos[0];
|
|
final int dragLayerY = dragLayerPos[1];
|
|
mLastTouch[0] = dragLayerX;
|
|
mLastTouch[1] = dragLayerY;
|
|
if (ATLEAST_Q) {
|
|
mLastTouchClassification = ev.getClassification();
|
|
}
|
|
|
|
switch (action) {
|
|
case MotionEvent.ACTION_DOWN:
|
|
// Remember location of down touch
|
|
mMotionDownX = dragLayerX;
|
|
mMotionDownY = dragLayerY;
|
|
break;
|
|
case MotionEvent.ACTION_UP:
|
|
mLastTouchUpTime = System.currentTimeMillis();
|
|
break;
|
|
}
|
|
|
|
return mDragDriver != null && mDragDriver.onInterceptTouchEvent(ev);
|
|
}
|
|
|
|
/**
|
|
* Call this from a drag source view.
|
|
*/
|
|
public boolean onDragEvent(long dragStartTime, DragEvent event) {
|
|
mFlingToDeleteHelper.recordDragEvent(dragStartTime, event);
|
|
return mDragDriver != null && mDragDriver.onDragEvent(event);
|
|
}
|
|
|
|
/**
|
|
* Call this from a drag view.
|
|
*/
|
|
public void onDragViewAnimationEnd() {
|
|
if (mDragDriver != null) {
|
|
mDragDriver.onDragViewAnimationEnd();
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Sets the view that should handle move events.
|
|
*/
|
|
public void setMoveTarget(View view) {
|
|
mMoveTarget = view;
|
|
}
|
|
|
|
public boolean dispatchUnhandledMove(View focused, int direction) {
|
|
return mMoveTarget != null && mMoveTarget.dispatchUnhandledMove(focused, direction);
|
|
}
|
|
|
|
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.hypot(mLastTouch[0] - x, mLastTouch[1] - y);
|
|
mLastTouch[0] = x;
|
|
mLastTouch[1] = y;
|
|
|
|
int distanceDragged = mDistanceSinceScroll;
|
|
if (ATLEAST_Q && mLastTouchClassification == MotionEvent.CLASSIFICATION_DEEP_PRESS) {
|
|
distanceDragged /= DEEP_PRESS_DISTANCE_FACTOR;
|
|
}
|
|
if (mIsInPreDrag && mOptions.preDragCondition != null
|
|
&& mOptions.preDragCondition.shouldStartDrag(distanceDragged)) {
|
|
callOnDragStart();
|
|
}
|
|
}
|
|
|
|
public float getDistanceDragged() {
|
|
return mDistanceSinceScroll;
|
|
}
|
|
|
|
public void forceTouchMove() {
|
|
int[] dummyCoordinates = mCoordinatesTemp;
|
|
DropTarget dropTarget = findDropTarget(mLastTouch[0], mLastTouch[1], dummyCoordinates);
|
|
mDragObject.x = dummyCoordinates[0];
|
|
mDragObject.y = dummyCoordinates[1];
|
|
checkTouchMove(dropTarget);
|
|
}
|
|
|
|
private void checkTouchMove(DropTarget dropTarget) {
|
|
if (dropTarget != null) {
|
|
if (mLastDropTarget != dropTarget) {
|
|
if (mLastDropTarget != null) {
|
|
mLastDropTarget.onDragExit(mDragObject);
|
|
}
|
|
dropTarget.onDragEnter(mDragObject);
|
|
}
|
|
dropTarget.onDragOver(mDragObject);
|
|
} else {
|
|
if (mLastDropTarget != null) {
|
|
mLastDropTarget.onDragExit(mDragObject);
|
|
}
|
|
}
|
|
mLastDropTarget = dropTarget;
|
|
}
|
|
|
|
/**
|
|
* Call this from a drag source view.
|
|
*/
|
|
public boolean onControllerTouchEvent(MotionEvent ev) {
|
|
if (mDragDriver == null || mOptions == null || mOptions.isAccessibleDrag) {
|
|
return false;
|
|
}
|
|
|
|
// Update the velocity tracker
|
|
mFlingToDeleteHelper.recordMotionEvent(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;
|
|
break;
|
|
}
|
|
|
|
return mDragDriver.onTouchEvent(ev);
|
|
}
|
|
|
|
/**
|
|
* Since accessible drag and drop won't cause the same sequence of touch events, we manually
|
|
* inject the appropriate state which would have been otherwise initiated via touch events.
|
|
*/
|
|
public void prepareAccessibleDrag(int x, int y) {
|
|
mMotionDownX = x;
|
|
mMotionDownY = y;
|
|
mLastTouch[0] = x;
|
|
mLastTouch[1] = y;
|
|
}
|
|
|
|
/**
|
|
* As above, since accessible drag and drop won't cause the same sequence of touch events,
|
|
* we manually ensure appropriate drag and drop events get emulated for accessible drag.
|
|
*/
|
|
public void completeAccessibleDrag(int[] location) {
|
|
final int[] coordinates = mCoordinatesTemp;
|
|
|
|
// We make sure that we prime the target for drop.
|
|
DropTarget dropTarget = findDropTarget(location[0], location[1], coordinates);
|
|
mDragObject.x = coordinates[0];
|
|
mDragObject.y = coordinates[1];
|
|
checkTouchMove(dropTarget);
|
|
|
|
dropTarget.prepareAccessibilityDrop();
|
|
// Perform the drop
|
|
drop(dropTarget, null);
|
|
endDrag();
|
|
}
|
|
|
|
private void drop(DropTarget dropTarget, Runnable flingAnimation) {
|
|
final int[] coordinates = mCoordinatesTemp;
|
|
mDragObject.x = coordinates[0];
|
|
mDragObject.y = coordinates[1];
|
|
|
|
// Move dragging to the final target.
|
|
if (dropTarget != mLastDropTarget) {
|
|
if (mLastDropTarget != null) {
|
|
mLastDropTarget.onDragExit(mDragObject);
|
|
}
|
|
mLastDropTarget = dropTarget;
|
|
if (dropTarget != null) {
|
|
dropTarget.onDragEnter(mDragObject);
|
|
}
|
|
}
|
|
|
|
mDragObject.dragComplete = true;
|
|
if (mIsInPreDrag) {
|
|
if (dropTarget != null) {
|
|
dropTarget.onDragExit(mDragObject);
|
|
}
|
|
return;
|
|
}
|
|
|
|
// Drop onto the target.
|
|
boolean accepted = false;
|
|
if (dropTarget != null) {
|
|
dropTarget.onDragExit(mDragObject);
|
|
if (dropTarget.acceptDrop(mDragObject)) {
|
|
if (flingAnimation != null) {
|
|
flingAnimation.run();
|
|
} else {
|
|
dropTarget.onDrop(mDragObject, mOptions);
|
|
}
|
|
accepted = true;
|
|
}
|
|
}
|
|
final View dropTargetAsView = dropTarget instanceof View ? (View) dropTarget : null;
|
|
mLauncher.getUserEventDispatcher().logDragNDrop(mDragObject, dropTargetAsView);
|
|
dispatchDropComplete(dropTargetAsView, accepted);
|
|
}
|
|
|
|
private DropTarget findDropTarget(int x, int y, int[] dropCoordinates) {
|
|
mDragObject.x = x;
|
|
mDragObject.y = y;
|
|
|
|
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.getHitRectRelativeToDragLayer(r);
|
|
if (r.contains(x, y)) {
|
|
dropCoordinates[0] = x;
|
|
dropCoordinates[1] = y;
|
|
mLauncher.getDragLayer().mapCoordInSelfToDescendant((View) target, dropCoordinates);
|
|
return target;
|
|
}
|
|
}
|
|
// Pass all unhandled drag to workspace. Workspace finds the correct
|
|
// cell layout to drop to in the existing drag/drop logic.
|
|
dropCoordinates[0] = x;
|
|
dropCoordinates[1] = y;
|
|
mLauncher.getDragLayer().mapCoordInSelfToDescendant(mLauncher.getWorkspace(),
|
|
dropCoordinates);
|
|
return mLauncher.getWorkspace();
|
|
}
|
|
|
|
public void setWindowToken(IBinder token) {
|
|
mWindowToken = token;
|
|
}
|
|
|
|
/**
|
|
* Sets the drag listener 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);
|
|
}
|
|
}
|