diff options
19 files changed, 227 insertions, 327 deletions
diff --git a/src/com/android/launcher3/AnotherWindowDropTarget.java b/src/com/android/launcher3/AnotherWindowDropTarget.java index 7074f7838..052e5d09a 100644 --- a/src/com/android/launcher3/AnotherWindowDropTarget.java +++ b/src/com/android/launcher3/AnotherWindowDropTarget.java @@ -48,9 +48,6 @@ public class AnotherWindowDropTarget implements DropTarget { public void onDragExit(DragObject dragObject) {} @Override - public void onFlingToDelete(DragObject dragObject, PointF vec) {} - - @Override public boolean acceptDrop(DragObject dragObject) { return dragObject.dragInfo instanceof ShortcutInfo; } diff --git a/src/com/android/launcher3/ButtonDropTarget.java b/src/com/android/launcher3/ButtonDropTarget.java index 60a2cc325..e613b3b2f 100644 --- a/src/com/android/launcher3/ButtonDropTarget.java +++ b/src/com/android/launcher3/ButtonDropTarget.java @@ -28,7 +28,6 @@ import android.content.res.Resources; import android.content.res.TypedArray; import android.graphics.ColorMatrix; import android.graphics.ColorMatrixColorFilter; -import android.graphics.PointF; import android.graphics.Rect; import android.graphics.drawable.Drawable; import android.os.Build; @@ -119,9 +118,6 @@ public abstract class ButtonDropTarget extends TextView } @Override - public void onFlingToDelete(DragObject d, PointF vec) { } - - @Override public final void onDragEnter(DragObject d) { d.dragView.setColor(mHoverColor); if (Utilities.ATLEAST_LOLLIPOP) { @@ -243,10 +239,7 @@ public abstract class ButtonDropTarget extends TextView final Rect from = new Rect(); dragLayer.getViewRectRelativeToSelf(d.dragView, from); - int width = mDrawable.getIntrinsicWidth(); - int height = mDrawable.getIntrinsicHeight(); - final Rect to = getIconRect(d.dragView.getMeasuredWidth(), d.dragView.getMeasuredHeight(), - width, height); + final Rect to = getIconRect(d); final float scale = (float) to.width() / from.width(); mDropTargetBar.deferOnDragEnd(); @@ -268,7 +261,7 @@ public abstract class ButtonDropTarget extends TextView @Override public void prepareAccessibilityDrop() { } - @Thunk abstract void completeDrop(DragObject d); + public abstract void completeDrop(DragObject d); @Override public void getHitRectRelativeToDragLayer(android.graphics.Rect outRect) { @@ -280,7 +273,11 @@ public abstract class ButtonDropTarget extends TextView outRect.offsetTo(coords[0], coords[1]); } - protected Rect getIconRect(int viewWidth, int viewHeight, int drawableWidth, int drawableHeight) { + public Rect getIconRect(DragObject dragObject) { + int viewWidth = dragObject.dragView.getMeasuredWidth(); + int viewHeight = dragObject.dragView.getMeasuredHeight(); + int drawableWidth = mDrawable.getIntrinsicWidth(); + int drawableHeight = mDrawable.getIntrinsicHeight(); DragLayer dragLayer = mLauncher.getDragLayer(); // Find the rect to animate to (the view is center aligned) diff --git a/src/com/android/launcher3/DeleteDropTarget.java b/src/com/android/launcher3/DeleteDropTarget.java index 705f84101..9097ed23d 100644 --- a/src/com/android/launcher3/DeleteDropTarget.java +++ b/src/com/android/launcher3/DeleteDropTarget.java @@ -16,19 +16,13 @@ package com.android.launcher3; -import android.animation.TimeInterpolator; import android.content.Context; -import android.graphics.PointF; import android.text.TextUtils; import android.util.AttributeSet; import android.view.View; -import android.view.animation.AnimationUtils; -import com.android.launcher3.dragndrop.DragLayer; import com.android.launcher3.dragndrop.DragOptions; import com.android.launcher3.folder.Folder; -import com.android.launcher3.util.FlingAnimation; -import com.android.launcher3.util.Thunk; public class DeleteDropTarget extends ButtonDropTarget { @@ -78,7 +72,7 @@ public class DeleteDropTarget extends ButtonDropTarget { } @Override - @Thunk void completeDrop(DragObject d) { + public void completeDrop(DragObject d) { ItemInfo item = d.dragInfo; if ((d.dragSource instanceof Workspace) || (d.dragSource instanceof Folder)) { removeWorkspaceOrFolderItem(mLauncher, item, null); @@ -96,53 +90,4 @@ public class DeleteDropTarget extends ButtonDropTarget { launcher.getWorkspace().stripEmptyScreens(); launcher.getDragLayer().announceForAccessibility(launcher.getString(R.string.item_removed)); } - - @Override - public void onFlingToDelete(final DragObject d, PointF vel) { - // Don't highlight the icon as it's animating - d.dragView.setColor(0); - - final DragLayer dragLayer = mLauncher.getDragLayer(); - FlingAnimation fling = new FlingAnimation(d, vel, - getIconRect(d.dragView.getMeasuredWidth(), d.dragView.getMeasuredHeight(), - mDrawable.getIntrinsicWidth(), mDrawable.getIntrinsicHeight()), - dragLayer); - - final int duration = fling.getDuration(); - final long startTime = AnimationUtils.currentAnimationTimeMillis(); - - // NOTE: Because it takes time for the first frame of animation to actually be - // called and we expect the animation to be a continuation of the fling, we have - // to account for the time that has elapsed since the fling finished. And since - // we don't have a startDelay, we will always get call to update when we call - // start() (which we want to ignore). - final TimeInterpolator tInterpolator = new TimeInterpolator() { - private int mCount = -1; - private float mOffset = 0f; - - @Override - public float getInterpolation(float t) { - if (mCount < 0) { - mCount++; - } else if (mCount == 0) { - mOffset = Math.min(0.5f, (float) (AnimationUtils.currentAnimationTimeMillis() - - startTime) / duration); - mCount++; - } - return Math.min(1f, mOffset + t); - } - }; - - Runnable onAnimationEndRunnable = new Runnable() { - @Override - public void run() { - mLauncher.exitSpringLoadedDragMode(); - completeDrop(d); - mLauncher.getDragController().onDeferredEndFling(d); - } - }; - - dragLayer.animateView(d.dragView, fling, duration, tInterpolator, onAnimationEndRunnable, - DragLayer.ANIMATION_END_DISAPPEAR, null); - } } diff --git a/src/com/android/launcher3/DragSource.java b/src/com/android/launcher3/DragSource.java index 2fb495fdd..dcd8f589a 100644 --- a/src/com/android/launcher3/DragSource.java +++ b/src/com/android/launcher3/DragSource.java @@ -27,11 +27,6 @@ import com.android.launcher3.logging.UserEventDispatcher.LogContainerProvider; public interface DragSource extends LogContainerProvider { /** - * @return whether items dragged from this source supports - */ - boolean supportsFlingToDelete(); - - /** * @return whether items dragged from this source supports 'App Info' */ boolean supportsAppInfoDropTarget(); @@ -48,13 +43,6 @@ public interface DragSource extends LogContainerProvider { float getIntrinsicIconScaleFactor(); /** - * A callback specifically made back to the source after an item from this source has been flung - * to be deleted on a DropTarget. In such a situation, this method will be called after - * onDropCompleted, and more importantly, after the fling animation has completed. - */ - void onFlingToDeleteCompleted(); - - /** * A callback made back to the source after an item from this source has been dropped on a * DropTarget. */ diff --git a/src/com/android/launcher3/DropTarget.java b/src/com/android/launcher3/DropTarget.java index e91fc15e0..7d047d748 100644 --- a/src/com/android/launcher3/DropTarget.java +++ b/src/com/android/launcher3/DropTarget.java @@ -16,12 +16,10 @@ package com.android.launcher3; -import com.android.launcher3.dragndrop.DragView; - -import android.graphics.PointF; import android.graphics.Rect; import com.android.launcher3.accessibility.DragViewStateAnnouncer; +import com.android.launcher3.dragndrop.DragView; /** * Interface defining an object that can receive a drag. @@ -117,13 +115,6 @@ public interface DropTarget { void onDragExit(DragObject dragObject); /** - * Handle an object being dropped as a result of flinging to delete and will be called in place - * of onDrop(). (This is only called on objects that are set as the DragController's - * fling-to-delete target. - */ - void onFlingToDelete(DragObject dragObject, PointF vec); - - /** * Check if a drop action can occur at, or near, the requested location. * This will be called just before onDrop. * @return True if the drop will be accepted, false otherwise. diff --git a/src/com/android/launcher3/DropTargetBar.java b/src/com/android/launcher3/DropTargetBar.java index 42bab4705..0840b7015 100644 --- a/src/com/android/launcher3/DropTargetBar.java +++ b/src/com/android/launcher3/DropTargetBar.java @@ -21,6 +21,7 @@ import android.content.Context; import android.util.AttributeSet; import android.view.View; import android.view.ViewDebug; +import android.view.ViewGroup; import android.view.ViewPropertyAnimator; import android.view.accessibility.AccessibilityManager; import android.view.animation.AccelerateInterpolator; @@ -56,11 +57,6 @@ public class DropTargetBar extends LinearLayout implements DragController.DragLi private ViewPropertyAnimator mCurrentAnimation; - // Drop targets - private ButtonDropTarget mDeleteDropTarget; - private ButtonDropTarget mAppInfoDropTarget; - private ButtonDropTarget mUninstallDropTarget; - public DropTargetBar(Context context, AttributeSet attrs) { super(context, attrs); } @@ -73,30 +69,27 @@ public class DropTargetBar extends LinearLayout implements DragController.DragLi protected void onFinishInflate() { super.onFinishInflate(); - // Get the individual components - mDeleteDropTarget = (ButtonDropTarget) findViewById(R.id.delete_target_text); - mAppInfoDropTarget = (ButtonDropTarget) findViewById(R.id.info_target_text); - mUninstallDropTarget = (ButtonDropTarget) findViewById(R.id.uninstall_target_text); - - mDeleteDropTarget.setDropTargetBar(this); - mAppInfoDropTarget.setDropTargetBar(this); - mUninstallDropTarget.setDropTargetBar(this); - // Initialize with hidden state setAlpha(0f); } public void setup(DragController dragController) { dragController.addDragListener(this); - dragController.setFlingToDeleteDropTarget(mDeleteDropTarget); - - dragController.addDragListener(mDeleteDropTarget); - dragController.addDragListener(mAppInfoDropTarget); - dragController.addDragListener(mUninstallDropTarget); + setupButtonDropTarget(this, dragController); + } - dragController.addDropTarget(mDeleteDropTarget); - dragController.addDropTarget(mAppInfoDropTarget); - dragController.addDropTarget(mUninstallDropTarget); + private void setupButtonDropTarget(View view, DragController dragController) { + if (view instanceof ButtonDropTarget) { + ButtonDropTarget bdt = (ButtonDropTarget) view; + bdt.setDropTargetBar(this); + dragController.addDragListener(bdt); + dragController.addDropTarget(bdt); + } else if (view instanceof ViewGroup) { + ViewGroup vg = (ViewGroup) view; + for (int i = vg.getChildCount() - 1; i >= 0; i--) { + setupButtonDropTarget(vg.getChildAt(i), dragController); + } + } } private void animateToVisibility(boolean isVisible) { diff --git a/src/com/android/launcher3/InfoDropTarget.java b/src/com/android/launcher3/InfoDropTarget.java index 398c9d2a2..6a6d2859a 100644 --- a/src/com/android/launcher3/InfoDropTarget.java +++ b/src/com/android/launcher3/InfoDropTarget.java @@ -49,7 +49,7 @@ public class InfoDropTarget extends UninstallDropTarget { } @Override - void completeDrop(DragObject d) { + public void completeDrop(DragObject d) { DropTargetResultCallback callback = d.dragSource instanceof DropTargetResultCallback ? (DropTargetResultCallback) d.dragSource : null; startDetailsActivityForInfo(d.dragInfo, mLauncher, callback); diff --git a/src/com/android/launcher3/Launcher.java b/src/com/android/launcher3/Launcher.java index accc3cd61..7775a6bc5 100644 --- a/src/com/android/launcher3/Launcher.java +++ b/src/com/android/launcher3/Launcher.java @@ -3068,7 +3068,7 @@ public class Launcher extends Activity || mState == State.WIDGETS_SPRING_LOADED; } - void exitSpringLoadedDragMode() { + public void exitSpringLoadedDragMode() { if (mState == State.APPS_SPRING_LOADED) { showAppsView(true /* animated */, false /* updatePredictedApps */, false /* focusSearchBar */); diff --git a/src/com/android/launcher3/UninstallDropTarget.java b/src/com/android/launcher3/UninstallDropTarget.java index 7ea9aca7c..00c613a88 100644 --- a/src/com/android/launcher3/UninstallDropTarget.java +++ b/src/com/android/launcher3/UninstallDropTarget.java @@ -95,7 +95,7 @@ public class UninstallDropTarget extends ButtonDropTarget { } @Override - void completeDrop(final DragObject d) { + public void completeDrop(final DragObject d) { DropTargetResultCallback callback = d.dragSource instanceof DropTargetResultCallback ? (DropTargetResultCallback) d.dragSource : null; startUninstallActivity(mLauncher, d.dragInfo, callback); diff --git a/src/com/android/launcher3/Workspace.java b/src/com/android/launcher3/Workspace.java index 13bea2077..49e14e494 100644 --- a/src/com/android/launcher3/Workspace.java +++ b/src/com/android/launcher3/Workspace.java @@ -33,7 +33,6 @@ import android.content.res.Resources; import android.graphics.Bitmap; import android.graphics.Canvas; import android.graphics.Point; -import android.graphics.PointF; import android.graphics.Rect; import android.graphics.drawable.Drawable; import android.os.Handler; @@ -3722,11 +3721,6 @@ public class Workspace extends PagedView } @Override - public boolean supportsFlingToDelete() { - return true; - } - - @Override public boolean supportsAppInfoDropTarget() { return !FeatureFlags.LAUNCHER3_LEGACY_WORKSPACE_DND; } @@ -3736,16 +3730,6 @@ public class Workspace extends PagedView return true; } - @Override - public void onFlingToDelete(DragObject d, PointF vec) { - // Do nothing - } - - @Override - public void onFlingToDeleteCompleted() { - // Do nothing - } - public boolean isDropEnabled() { return true; } diff --git a/src/com/android/launcher3/allapps/AllAppsContainerView.java b/src/com/android/launcher3/allapps/AllAppsContainerView.java index e0d145cee..24fb06601 100644 --- a/src/com/android/launcher3/allapps/AllAppsContainerView.java +++ b/src/com/android/launcher3/allapps/AllAppsContainerView.java @@ -397,11 +397,6 @@ public class AllAppsContainerView extends BaseContainerView implements DragSourc } @Override - public boolean supportsFlingToDelete() { - return true; - } - - @Override public boolean supportsAppInfoDropTarget() { return true; } @@ -418,14 +413,6 @@ public class AllAppsContainerView extends BaseContainerView implements DragSourc } @Override - public void onFlingToDeleteCompleted() { - // We just dismiss the drag when we fling, so cleanup here - mLauncher.exitSpringLoadedDragModeDelayed(true, - Launcher.EXIT_SPRINGLOADED_MODE_SHORT_TIMEOUT, null); - mLauncher.unlockScreenOrientation(false); - } - - @Override public void onDropCompleted(View target, DropTarget.DragObject d, boolean isFlingToDelete, boolean success) { if (isFlingToDelete || !success || (target != mLauncher.getWorkspace() && diff --git a/src/com/android/launcher3/dragndrop/AnotherWindowDragSource.java b/src/com/android/launcher3/dragndrop/AnotherWindowDragSource.java index e968f36a7..162301069 100644 --- a/src/com/android/launcher3/dragndrop/AnotherWindowDragSource.java +++ b/src/com/android/launcher3/dragndrop/AnotherWindowDragSource.java @@ -37,11 +37,6 @@ public class AnotherWindowDragSource implements DragSource { } @Override - public boolean supportsFlingToDelete() { - return false; - } - - @Override public boolean supportsAppInfoDropTarget() { return false; } @@ -57,10 +52,6 @@ public class AnotherWindowDragSource implements DragSource { } @Override - public void onFlingToDeleteCompleted() { - } - - @Override public void onDropCompleted(View target, DragObject d, boolean isFlingToDelete, boolean success) { if (!success) { diff --git a/src/com/android/launcher3/dragndrop/DragController.java b/src/com/android/launcher3/dragndrop/DragController.java index 67ef5fc3c..a5a54c1ec 100644 --- a/src/com/android/launcher3/dragndrop/DragController.java +++ b/src/com/android/launcher3/dragndrop/DragController.java @@ -17,11 +17,9 @@ package com.android.launcher3.dragndrop; import android.content.ComponentName; -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; @@ -29,7 +27,6 @@ import android.view.DragEvent; import android.view.HapticFeedbackConstants; 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; @@ -54,8 +51,6 @@ 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 String TAG = "Launcher.DragController"; - public static final int SCROLL_DELAY = 500; public static final int RESCROLL_DELAY = PagedView.PAGE_SNAP_ANIMATION_DURATION + 150; @@ -68,10 +63,9 @@ public class DragController implements DragDriver.EventListener, TouchController public static final int SCROLL_LEFT = 0; public static final int SCROLL_RIGHT = 1; - private static final float MAX_FLING_DEGREES = 35f; - @Thunk Launcher mLauncher; private Handler mHandler; + private FlingToDeleteHelper mFlingToDeleteHelper; // temporaries to avoid gc thrash private Rect mRectTemp = new Rect(); @@ -103,7 +97,6 @@ public class DragController implements DragDriver.EventListener, TouchController /** 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; @@ -119,8 +112,6 @@ public class DragController implements DragDriver.EventListener, TouchController private DropTarget mLastDropTarget; - private InputMethodManager mInputMethodManager; - @Thunk int mLastTouch[] = new int[2]; @Thunk long mLastTouchUpTime = -1; @Thunk int mDistanceSinceScroll = 0; @@ -128,9 +119,6 @@ public class DragController implements DragDriver.EventListener, TouchController private int mTmpPoint[] = new int[2]; private Rect mDragLayerRect = new Rect(); - protected final int mFlingToDeleteThresholdVelocity; - private VelocityTracker mVelocityTracker; - private boolean mIsInPreDrag; /** @@ -159,11 +147,8 @@ public class DragController implements DragDriver.EventListener, TouchController mLauncher = launcher; mHandler = new Handler(); mScrollZone = r.getDimensionPixelSize(R.dimen.scroll_zone); - mVelocityTracker = VelocityTracker.obtain(); - - mFlingToDeleteThresholdVelocity = - r.getDimensionPixelSize(R.dimen.drag_flingToDeleteMinVelocity); mIsRtl = Utilities.isRtl(r); + mFlingToDeleteHelper = new FlingToDeleteHelper(launcher); } /** @@ -208,11 +193,8 @@ public class DragController implements DragDriver.EventListener, TouchController } // Hide soft keyboard, if visible - if (mInputMethodManager == null) { - mInputMethodManager = (InputMethodManager) - mLauncher.getSystemService(Context.INPUT_METHOD_SERVICE); - } - mInputMethodManager.hideSoftInputFromWindow(mWindowToken, 0); + mLauncher.getSystemService(InputMethodManager.class) + .hideSoftInputFromWindow(mWindowToken, 0); mOptions = options; if (mOptions.systemDndStartPoint != null) { @@ -369,7 +351,7 @@ public class DragController implements DragDriver.EventListener, TouchController } } - releaseVelocityTracker(); + mFlingToDeleteHelper.releaseVelocityTracker(); } public void animateDragViewToOriginalPosition(final Runnable onComplete, @@ -411,10 +393,6 @@ public class DragController implements DragDriver.EventListener, TouchController } } - public void onDeferredEndFling(DropTarget.DragObject d) { - d.dragSource.onFlingToDeleteCompleted(); - } - /** * Clamps the position to the drag layer bounds. */ @@ -453,22 +431,16 @@ public class DragController implements DragDriver.EventListener, TouchController } @Override - public void onDriverDragEnd(float x, float y, DropTarget dropTargetOverride) { + public void onDriverDragEnd(float x, float y) { DropTarget dropTarget; - PointF vec = null; - - if (dropTargetOverride != null) { - dropTarget = dropTargetOverride; + Runnable flingAnimation = mFlingToDeleteHelper.getFlingAnimation(mDragObject); + if (flingAnimation != null) { + dropTarget = mFlingToDeleteHelper.getDropTarget(); } else { - vec = isFlingingToDelete(mDragObject.dragSource); - if (vec != null) { - dropTarget = mFlingToDeleteDropTarget; - } else { - dropTarget = findDropTarget((int) x, (int) y, mCoordinatesTemp); - } + dropTarget = findDropTarget((int) x, (int) y, mCoordinatesTemp); } - drop(dropTarget, x, y, vec); + drop(dropTarget, flingAnimation); endDrag(); } @@ -487,7 +459,7 @@ public class DragController implements DragDriver.EventListener, TouchController } // Update the velocity tracker - acquireVelocityTrackerAndAddMovement(ev); + mFlingToDeleteHelper.recordMotionEvent(ev); final int action = ev.getAction(); final int[] dragLayerPos = getClampedDragLayerPos(ev.getX(), ev.getY()); @@ -635,7 +607,7 @@ public class DragController implements DragDriver.EventListener, TouchController } // Update the velocity tracker - acquireVelocityTrackerAndAddMovement(ev); + mFlingToDeleteHelper.recordMotionEvent(ev); final int action = ev.getAction(); final int[] dragLayerPos = getClampedDragLayerPos(ev.getX(), ev.getY()); @@ -688,47 +660,12 @@ public class DragController implements DragDriver.EventListener, TouchController dropTarget.prepareAccessibilityDrop(); // Perform the drop - drop(dropTarget, location[0], location[1], null); + drop(dropTarget, null); endDrag(); } - /** - * 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()); - PointF vel = new PointF(mVelocityTracker.getXVelocity(), mVelocityTracker.getYVelocity()); - float theta = MAX_FLING_DEGREES + 1; - if (mVelocityTracker.getYVelocity() < mFlingToDeleteThresholdVelocity) { - // Do a quick dot product test to ensure that we are flinging upwards - PointF upVec = new PointF(0f, -1f); - theta = getAngleBetweenVectors(vel, upVec); - } else if (mLauncher.getDeviceProfile().isVerticalBarLayout() && - mVelocityTracker.getXVelocity() < mFlingToDeleteThresholdVelocity) { - // Remove icon is on left side instead of top, so check if we are flinging to the left. - PointF leftVec = new PointF(-1f, 0f); - theta = getAngleBetweenVectors(vel, leftVec); - } - if (theta <= Math.toRadians(MAX_FLING_DEGREES)) { - return vel; - } - return null; - } - - private float getAngleBetweenVectors(PointF vec1, PointF vec2) { - return (float) Math.acos(((vec1.x * vec2.x) + (vec1.y * vec2.y)) / - (vec1.length() * vec2.length())); - } - - private void drop(DropTarget dropTarget, float x, float y, PointF flingVel) { + private void drop(DropTarget dropTarget, Runnable flingAnimation) { final int[] coordinates = mCoordinatesTemp; - mDragObject.x = coordinates[0]; mDragObject.y = coordinates[1]; @@ -750,8 +687,8 @@ public class DragController implements DragDriver.EventListener, TouchController if (dropTarget != null) { dropTarget.onDragExit(mDragObject); if (dropTarget.acceptDrop(mDragObject)) { - if (flingVel != null) { - dropTarget.onFlingToDelete(mDragObject, flingVel); + if (flingAnimation != null) { + flingAnimation.run(); } else if (!mIsInPreDrag) { dropTarget.onDrop(mDragObject); } @@ -762,7 +699,7 @@ public class DragController implements DragDriver.EventListener, TouchController mLauncher.getUserEventDispatcher().logDragNDrop(mDragObject, dropTargetAsView); if (!mIsInPreDrag) { mDragObject.dragSource.onDropCompleted( - dropTargetAsView, mDragObject, flingVel != null, accepted); + dropTargetAsView, mDragObject, flingAnimation != null, accepted); } } @@ -829,27 +766,6 @@ public class DragController implements DragDriver.EventListener, TouchController } /** - * 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) { diff --git a/src/com/android/launcher3/dragndrop/DragDriver.java b/src/com/android/launcher3/dragndrop/DragDriver.java index d0c8e1690..a90cfffdc 100644 --- a/src/com/android/launcher3/dragndrop/DragDriver.java +++ b/src/com/android/launcher3/dragndrop/DragDriver.java @@ -23,7 +23,6 @@ import android.content.Intent; import android.view.DragEvent; import android.view.MotionEvent; -import com.android.launcher3.DropTarget; import com.android.launcher3.DropTarget.DragObject; import com.android.launcher3.InstallShortcutReceiver; import com.android.launcher3.ShortcutInfo; @@ -40,7 +39,7 @@ public abstract class DragDriver { public interface EventListener { void onDriverDragMove(float x, float y); void onDriverDragExitWindow(); - void onDriverDragEnd(float x, float y, DropTarget dropTargetOverride); + void onDriverDragEnd(float x, float y); void onDriverDragCancel(); } @@ -62,7 +61,7 @@ public abstract class DragDriver { break; case MotionEvent.ACTION_UP: mEventListener.onDriverDragMove(ev.getX(), ev.getY()); - mEventListener.onDriverDragEnd(ev.getX(), ev.getY(), null); + mEventListener.onDriverDragEnd(ev.getX(), ev.getY()); break; case MotionEvent.ACTION_CANCEL: mEventListener.onDriverDragCancel(); @@ -80,7 +79,7 @@ public abstract class DragDriver { switch (action) { case MotionEvent.ACTION_UP: - mEventListener.onDriverDragEnd(ev.getX(), ev.getY(), null); + mEventListener.onDriverDragEnd(ev.getX(), ev.getY()); break; case MotionEvent.ACTION_CANCEL: mEventListener.onDriverDragCancel(); @@ -160,7 +159,7 @@ class SystemDragDriver extends DragDriver { case DragEvent.ACTION_DRAG_ENDED: if (mReceivedDropEvent) { - mEventListener.onDriverDragEnd(mLastX, mLastY, null); + mEventListener.onDriverDragEnd(mLastX, mLastY); } else { mEventListener.onDriverDragCancel(); } diff --git a/src/com/android/launcher3/dragndrop/FlingToDeleteHelper.java b/src/com/android/launcher3/dragndrop/FlingToDeleteHelper.java new file mode 100644 index 000000000..a2aa27d17 --- /dev/null +++ b/src/com/android/launcher3/dragndrop/FlingToDeleteHelper.java @@ -0,0 +1,109 @@ +/* + * Copyright (C) 2016 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 android.graphics.PointF; +import android.view.MotionEvent; +import android.view.VelocityTracker; +import android.view.ViewConfiguration; + +import com.android.launcher3.ButtonDropTarget; +import com.android.launcher3.DropTarget; +import com.android.launcher3.Launcher; +import com.android.launcher3.R; +import com.android.launcher3.util.FlingAnimation; + +/** + * Utility class to manage fling to delete action during drag and drop. + */ +public class FlingToDeleteHelper { + + private static final float MAX_FLING_DEGREES = 35f; + + private final Launcher mLauncher; + private final int mFlingToDeleteThresholdVelocity; + + private ButtonDropTarget mDropTarget; + private VelocityTracker mVelocityTracker; + + public FlingToDeleteHelper(Launcher launcher) { + mLauncher = launcher; + mFlingToDeleteThresholdVelocity = launcher.getResources() + .getDimensionPixelSize(R.dimen.drag_flingToDeleteMinVelocity); + } + + public void recordMotionEvent(MotionEvent ev) { + if (mVelocityTracker == null) { + mVelocityTracker = VelocityTracker.obtain(); + } + mVelocityTracker.addMovement(ev); + } + + public void releaseVelocityTracker() { + if (mVelocityTracker != null) { + mVelocityTracker.recycle(); + mVelocityTracker = null; + } + } + + public DropTarget getDropTarget() { + return mDropTarget; + } + + public Runnable getFlingAnimation(DropTarget.DragObject dragObject) { + PointF vel = isFlingingToDelete(); + if (vel == null) { + return null; + } + return new FlingAnimation(dragObject, vel, mDropTarget, mLauncher); + } + + /** + * 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() { + if (mDropTarget == null) { + mDropTarget = (ButtonDropTarget) mLauncher.findViewById(R.id.delete_target_text); + } + if (mDropTarget == null || !mDropTarget.isDropEnabled()) return null; + ViewConfiguration config = ViewConfiguration.get(mLauncher); + mVelocityTracker.computeCurrentVelocity(1000, config.getScaledMaximumFlingVelocity()); + PointF vel = new PointF(mVelocityTracker.getXVelocity(), mVelocityTracker.getYVelocity()); + float theta = MAX_FLING_DEGREES + 1; + if (mVelocityTracker.getYVelocity() < mFlingToDeleteThresholdVelocity) { + // Do a quick dot product test to ensure that we are flinging upwards + PointF upVec = new PointF(0f, -1f); + theta = getAngleBetweenVectors(vel, upVec); + } else if (mLauncher.getDeviceProfile().isVerticalBarLayout() && + mVelocityTracker.getXVelocity() < mFlingToDeleteThresholdVelocity) { + // Remove icon is on left side instead of top, so check if we are flinging to the left. + PointF leftVec = new PointF(-1f, 0f); + theta = getAngleBetweenVectors(vel, leftVec); + } + if (theta <= Math.toRadians(MAX_FLING_DEGREES)) { + return vel; + } + return null; + } + + private float getAngleBetweenVectors(PointF vec1, PointF vec2) { + return (float) Math.acos(((vec1.x * vec2.x) + (vec1.y * vec2.y)) / + (vec1.length() * vec2.length())); + } +} diff --git a/src/com/android/launcher3/folder/Folder.java b/src/com/android/launcher3/folder/Folder.java index a81b4caba..315f5118e 100644 --- a/src/com/android/launcher3/folder/Folder.java +++ b/src/com/android/launcher3/folder/Folder.java @@ -25,12 +25,10 @@ import android.annotation.SuppressLint; import android.annotation.TargetApi; import android.content.Context; import android.content.res.Resources; -import android.graphics.PointF; import android.graphics.Rect; import android.os.Build; import android.text.InputType; import android.text.Selection; -import android.text.Spannable; import android.util.AttributeSet; import android.util.Log; import android.view.ActionMode; @@ -51,7 +49,6 @@ import android.widget.TextView; import com.android.launcher3.AbstractFloatingView; import com.android.launcher3.Alarm; import com.android.launcher3.AppInfo; -import com.android.launcher3.BubbleTextView; import com.android.launcher3.CellLayout; import com.android.launcher3.DeviceProfile; import com.android.launcher3.DragSource; @@ -1010,11 +1007,6 @@ public class Folder extends AbstractFloatingView implements DragSource, View.OnC } @Override - public boolean supportsFlingToDelete() { - return true; - } - - @Override public boolean supportsAppInfoDropTarget() { return !FeatureFlags.LAUNCHER3_LEGACY_WORKSPACE_DND; } @@ -1024,16 +1016,6 @@ public class Folder extends AbstractFloatingView implements DragSource, View.OnC return true; } - @Override - public void onFlingToDelete(DragObject d, PointF vec) { - // Do nothing - } - - @Override - public void onFlingToDeleteCompleted() { - // Do nothing - } - private void updateItemLocationsInDatabaseBatch() { ArrayList<View> list = getItemsInReadingOrder(); ArrayList<ItemInfo> items = new ArrayList<ItemInfo>(); diff --git a/src/com/android/launcher3/shortcuts/DeepShortcutsContainer.java b/src/com/android/launcher3/shortcuts/DeepShortcutsContainer.java index 314a8628b..a2534926f 100644 --- a/src/com/android/launcher3/shortcuts/DeepShortcutsContainer.java +++ b/src/com/android/launcher3/shortcuts/DeepShortcutsContainer.java @@ -475,11 +475,6 @@ public class DeepShortcutsContainer extends AbstractFloatingView } @Override - public boolean supportsFlingToDelete() { - return true; - } - - @Override public boolean supportsAppInfoDropTarget() { return true; } @@ -495,11 +490,6 @@ public class DeepShortcutsContainer extends AbstractFloatingView } @Override - public void onFlingToDeleteCompleted() { - // Don't care; ignore. - } - - @Override public void onDropCompleted(View target, DropTarget.DragObject d, boolean isFlingToDelete, boolean success) { if (!success) { diff --git a/src/com/android/launcher3/util/FlingAnimation.java b/src/com/android/launcher3/util/FlingAnimation.java index da8bae712..d475ee9df 100644 --- a/src/com/android/launcher3/util/FlingAnimation.java +++ b/src/com/android/launcher3/util/FlingAnimation.java @@ -5,13 +5,16 @@ import android.animation.ValueAnimator; import android.animation.ValueAnimator.AnimatorUpdateListener; import android.graphics.PointF; import android.graphics.Rect; +import android.view.animation.AnimationUtils; import android.view.animation.DecelerateInterpolator; +import com.android.launcher3.ButtonDropTarget; import com.android.launcher3.DropTarget.DragObject; +import com.android.launcher3.Launcher; import com.android.launcher3.dragndrop.DragLayer; import com.android.launcher3.dragndrop.DragView; -public class FlingAnimation implements AnimatorUpdateListener { +public class FlingAnimation implements AnimatorUpdateListener, Runnable { /** * Maximum acceleration in one dimension (pixels per milliseconds) @@ -19,40 +22,86 @@ public class FlingAnimation implements AnimatorUpdateListener { private static final float MAX_ACCELERATION = 0.5f; private static final int DRAG_END_DELAY = 300; + private final ButtonDropTarget mDropTarget; + private final Launcher mLauncher; + protected final DragObject mDragObject; - protected final Rect mIconRect; protected final DragLayer mDragLayer; - protected final Rect mFrom; - protected final int mDuration; - protected final float mUX, mUY; - protected final float mAnimationTimeFraction; protected final TimeInterpolator mAlphaInterpolator = new DecelerateInterpolator(0.75f); + protected final float mUX, mUY; + + protected Rect mIconRect; + protected Rect mFrom; + protected int mDuration; + protected float mAnimationTimeFraction; protected float mAX, mAY; - /** - * @param vel initial fling velocity in pixels per second. - */ - public FlingAnimation(DragObject d, PointF vel, Rect iconRect, DragLayer dragLayer) { + public FlingAnimation(DragObject d, PointF vel, ButtonDropTarget dropTarget, Launcher launcher) { + mDropTarget = dropTarget; + mLauncher = launcher; mDragObject = d; mUX = vel.x / 1000; mUY = vel.y / 1000; - mIconRect = iconRect; + mDragLayer = mLauncher.getDragLayer(); + } - mDragLayer = dragLayer; - mFrom = new Rect(); - dragLayer.getViewRectRelativeToSelf(d.dragView, mFrom); + @Override + public void run() { + mIconRect = mDropTarget.getIconRect(mDragObject); - float scale = d.dragView.getScaleX(); - float xOffset = ((scale - 1f) * d.dragView.getMeasuredWidth()) / 2f; - float yOffset = ((scale - 1f) * d.dragView.getMeasuredHeight()) / 2f; + // Initiate from + mFrom = new Rect(); + mDragLayer.getViewRectRelativeToSelf(mDragObject.dragView, mFrom); + float scale = mDragObject.dragView.getScaleX(); + float xOffset = ((scale - 1f) * mDragObject.dragView.getMeasuredWidth()) / 2f; + float yOffset = ((scale - 1f) * mDragObject.dragView.getMeasuredHeight()) / 2f; mFrom.left += xOffset; mFrom.right -= xOffset; mFrom.top += yOffset; mFrom.bottom -= yOffset; + mDuration = Math.abs(mUY) > Math.abs(mUX) ? initFlingUpDuration() : initFlingLeftDuration(); - mDuration = Math.abs(vel.y) > Math.abs(vel.x) ? initFlingUpDuration() : initFlingLeftDuration(); mAnimationTimeFraction = ((float) mDuration) / (mDuration + DRAG_END_DELAY); + + // Don't highlight the icon as it's animating + mDragObject.dragView.setColor(0); + + final int duration = mDuration + DRAG_END_DELAY; + final long startTime = AnimationUtils.currentAnimationTimeMillis(); + + // NOTE: Because it takes time for the first frame of animation to actually be + // called and we expect the animation to be a continuation of the fling, we have + // to account for the time that has elapsed since the fling finished. And since + // we don't have a startDelay, we will always get call to update when we call + // start() (which we want to ignore). + final TimeInterpolator tInterpolator = new TimeInterpolator() { + private int mCount = -1; + private float mOffset = 0f; + + @Override + public float getInterpolation(float t) { + if (mCount < 0) { + mCount++; + } else if (mCount == 0) { + mOffset = Math.min(0.5f, (float) (AnimationUtils.currentAnimationTimeMillis() - + startTime) / duration); + mCount++; + } + return Math.min(1f, mOffset + t); + } + }; + + Runnable onAnimationEndRunnable = new Runnable() { + @Override + public void run() { + mLauncher.exitSpringLoadedDragMode(); + mDropTarget.completeDrop(mDragObject); + } + }; + + mDragLayer.animateView(mDragObject.dragView, this, duration, tInterpolator, + onAnimationEndRunnable, DragLayer.ANIMATION_END_DISAPPEAR, null); } /** @@ -111,10 +160,6 @@ public class FlingAnimation implements AnimatorUpdateListener { return (int) Math.round(t); } - public final int getDuration() { - return mDuration + DRAG_END_DELAY; - } - @Override public void onAnimationUpdate(ValueAnimator animation) { float t = animation.getAnimatedFraction(); diff --git a/src/com/android/launcher3/widget/WidgetsContainerView.java b/src/com/android/launcher3/widget/WidgetsContainerView.java index 2e1294251..165d12ef8 100644 --- a/src/com/android/launcher3/widget/WidgetsContainerView.java +++ b/src/com/android/launcher3/widget/WidgetsContainerView.java @@ -31,8 +31,6 @@ import com.android.launcher3.BaseContainerView; import com.android.launcher3.DeleteDropTarget; import com.android.launcher3.DragSource; import com.android.launcher3.DropTarget.DragObject; -import com.android.launcher3.dragndrop.DragOptions; -import com.android.launcher3.folder.Folder; import com.android.launcher3.IconCache; import com.android.launcher3.ItemInfo; import com.android.launcher3.Launcher; @@ -42,10 +40,11 @@ import com.android.launcher3.R; import com.android.launcher3.Utilities; import com.android.launcher3.WidgetPreviewLoader; import com.android.launcher3.dragndrop.DragController; +import com.android.launcher3.dragndrop.DragOptions; +import com.android.launcher3.folder.Folder; import com.android.launcher3.graphics.LauncherIcons; import com.android.launcher3.model.PackageItemInfo; import com.android.launcher3.model.WidgetItem; -import com.android.launcher3.model.WidgetsModel; import com.android.launcher3.userevent.nano.LauncherLogProto; import com.android.launcher3.userevent.nano.LauncherLogProto.Target; import com.android.launcher3.util.MultiHashMap; @@ -246,11 +245,6 @@ public class WidgetsContainerView extends BaseContainerView // @Override - public boolean supportsFlingToDelete() { - return true; - } - - @Override public boolean supportsAppInfoDropTarget() { return true; } @@ -270,14 +264,6 @@ public class WidgetsContainerView extends BaseContainerView } @Override - public void onFlingToDeleteCompleted() { - // We just dismiss the drag when we fling, so cleanup here - mLauncher.exitSpringLoadedDragModeDelayed(true, - Launcher.EXIT_SPRINGLOADED_MODE_SHORT_TIMEOUT, null); - mLauncher.unlockScreenOrientation(false); - } - - @Override public void onDropCompleted(View target, DragObject d, boolean isFlingToDelete, boolean success) { if (LOGD) { |