Merge "Adding fling-to-delete."
This commit is contained in:
@@ -39,7 +39,7 @@
|
||||
<bool name="config_workspaceFadeAdjacentScreens">false</bool>
|
||||
|
||||
<!-- The transition duration for the background of the drop targets -->
|
||||
<integer name="config_dropTargetBgTransitionDuration">100</integer>
|
||||
<integer name="config_dropTargetBgTransitionDuration">0</integer>
|
||||
|
||||
<integer name="config_crosshairsFadeInTime">600</integer>
|
||||
|
||||
|
||||
@@ -760,6 +760,10 @@ public class AppsCustomizePagedView extends PagedViewWithDraggableItems implemen
|
||||
mDraggingWidget = false;
|
||||
}
|
||||
|
||||
public boolean supportsFlingToDelete() {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onDetachedFromWindow() {
|
||||
super.onDetachedFromWindow();
|
||||
|
||||
@@ -18,7 +18,7 @@ package com.android.launcher2;
|
||||
|
||||
import android.content.Context;
|
||||
import android.content.res.Resources;
|
||||
import android.graphics.Paint;
|
||||
import android.graphics.PointF;
|
||||
import android.graphics.Rect;
|
||||
import android.util.AttributeSet;
|
||||
import android.widget.TextView;
|
||||
@@ -71,6 +71,10 @@ public class ButtonDropTarget extends TextView implements DropTarget, DragContro
|
||||
public void onDrop(DragObject d) {
|
||||
}
|
||||
|
||||
public void onFlingToDelete(DragObject d, int x, int y, PointF vec) {
|
||||
// Do nothing
|
||||
}
|
||||
|
||||
public void onDragEnter(DragObject d) {
|
||||
d.dragView.setColor(mHoverColor);
|
||||
}
|
||||
|
||||
@@ -16,24 +16,33 @@
|
||||
|
||||
package com.android.launcher2;
|
||||
|
||||
import android.animation.TimeInterpolator;
|
||||
import android.animation.ValueAnimator;
|
||||
import android.animation.ValueAnimator.AnimatorUpdateListener;
|
||||
import android.content.Context;
|
||||
import android.content.res.ColorStateList;
|
||||
import android.content.res.Configuration;
|
||||
import android.content.res.Resources;
|
||||
import android.graphics.PointF;
|
||||
import android.graphics.Rect;
|
||||
import android.graphics.drawable.TransitionDrawable;
|
||||
import android.util.AttributeSet;
|
||||
import android.view.View;
|
||||
import android.view.ViewConfiguration;
|
||||
import android.view.ViewGroup;
|
||||
import android.view.animation.AccelerateInterpolator;
|
||||
import android.view.animation.AnimationUtils;
|
||||
import android.view.animation.DecelerateInterpolator;
|
||||
import android.view.animation.LinearInterpolator;
|
||||
|
||||
import com.android.launcher.R;
|
||||
|
||||
public class DeleteDropTarget extends ButtonDropTarget {
|
||||
private static int DELETE_ANIMATION_DURATION = 285;
|
||||
private static int MODE_FLING_DELETE_TO_TRASH = 0;
|
||||
private static int MODE_FLING_DELETE_ALONG_VECTOR = 1;
|
||||
|
||||
private final int mFlingDeleteMode = MODE_FLING_DELETE_ALONG_VECTOR;
|
||||
|
||||
private static int DELETE_ANIMATION_DURATION = 300;
|
||||
private ColorStateList mOriginalTextColor;
|
||||
private TransitionDrawable mUninstallDrawable;
|
||||
private TransitionDrawable mRemoveDrawable;
|
||||
@@ -223,4 +232,175 @@ public class DeleteDropTarget extends ButtonDropTarget {
|
||||
public void onDrop(DragObject d) {
|
||||
animateToTrashAndCompleteDrop(d);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates an animation from the current drag view to the delete trash icon.
|
||||
*/
|
||||
private AnimatorUpdateListener createFlingToTrashAnimatorListener(final DragLayer dragLayer,
|
||||
DragObject d, PointF vel, ViewConfiguration config) {
|
||||
final Rect to = getIconRect(d.dragView.getMeasuredWidth(), d.dragView.getMeasuredHeight(),
|
||||
mCurrentDrawable.getIntrinsicWidth(), mCurrentDrawable.getIntrinsicHeight());
|
||||
final Rect from = new Rect();
|
||||
dragLayer.getViewRectRelativeToSelf(d.dragView, from);
|
||||
|
||||
// Calculate how far along the velocity vector we should put the intermediate point on
|
||||
// the bezier curve
|
||||
float velocity = Math.abs(vel.length());
|
||||
float vp = Math.min(1f, velocity / (config.getScaledMaximumFlingVelocity() / 2f));
|
||||
int offsetY = (int) (-from.top * vp);
|
||||
int offsetX = (int) (offsetY / (vel.y / vel.x));
|
||||
final float y2 = from.top + offsetY; // intermediate t/l
|
||||
final float x2 = from.left + offsetX;
|
||||
final float x1 = from.left; // drag view t/l
|
||||
final float y1 = from.top;
|
||||
final float x3 = to.left; // delete target t/l
|
||||
final float y3 = to.top;
|
||||
|
||||
final TimeInterpolator scaleAlphaInterpolator = new TimeInterpolator() {
|
||||
@Override
|
||||
public float getInterpolation(float t) {
|
||||
return t * t * t * t * t * t * t * t;
|
||||
}
|
||||
};
|
||||
return new AnimatorUpdateListener() {
|
||||
@Override
|
||||
public void onAnimationUpdate(ValueAnimator animation) {
|
||||
final DragView dragView = (DragView) dragLayer.getAnimatedView();
|
||||
float t = ((Float) animation.getAnimatedValue()).floatValue();
|
||||
float tp = scaleAlphaInterpolator.getInterpolation(t);
|
||||
float initialScale = dragView.getInitialScale();
|
||||
float finalAlpha = 0.5f;
|
||||
float scale = dragView.getScaleX();
|
||||
float x1o = ((1f - scale) * dragView.getMeasuredWidth()) / 2f;
|
||||
float y1o = ((1f - scale) * dragView.getMeasuredHeight()) / 2f;
|
||||
float x = (1f - t) * (1f - t) * (x1 - x1o) + 2 * (1f - t) * t * (x2 - x1o) +
|
||||
(t * t) * x3;
|
||||
float y = (1f - t) * (1f - t) * (y1 - y1o) + 2 * (1f - t) * t * (y2 - x1o) +
|
||||
(t * t) * y3;
|
||||
|
||||
dragView.setTranslationX(x);
|
||||
dragView.setTranslationY(y);
|
||||
dragView.setScaleX(initialScale * (1f - tp));
|
||||
dragView.setScaleY(initialScale * (1f - tp));
|
||||
dragView.setAlpha(finalAlpha + (1f - finalAlpha) * (1f - tp));
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates an animation from the current drag view along its current velocity vector.
|
||||
* For this animation, the alpha runs for a fixed duration and we update the position
|
||||
* progressively.
|
||||
*/
|
||||
private static class FlingAlongVectorAnimatorUpdateListener implements AnimatorUpdateListener {
|
||||
private static float FRICTION = 0.93f;
|
||||
|
||||
private DragLayer mDragLayer;
|
||||
private PointF mVelocity;
|
||||
private Rect mFrom;
|
||||
private long mPrevTime;
|
||||
private boolean mHasOffsetForScale;
|
||||
|
||||
private final TimeInterpolator mAlphaInterpolator = new DecelerateInterpolator(1.5f);
|
||||
|
||||
public FlingAlongVectorAnimatorUpdateListener(DragLayer dragLayer, PointF vel, Rect from,
|
||||
long startTime) {
|
||||
mDragLayer = dragLayer;
|
||||
mVelocity = vel;
|
||||
mFrom = from;
|
||||
mPrevTime = startTime;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onAnimationUpdate(ValueAnimator animation) {
|
||||
final DragView dragView = (DragView) mDragLayer.getAnimatedView();
|
||||
float t = ((Float) animation.getAnimatedValue()).floatValue();
|
||||
long curTime = AnimationUtils.currentAnimationTimeMillis();
|
||||
|
||||
if (!mHasOffsetForScale) {
|
||||
mHasOffsetForScale = true;
|
||||
float scale = dragView.getScaleX();
|
||||
float xOffset = ((scale - 1f) * dragView.getMeasuredWidth()) / 2f;
|
||||
float yOffset = ((scale - 1f) * dragView.getMeasuredHeight()) / 2f;
|
||||
|
||||
mFrom.left += xOffset;
|
||||
mFrom.top += yOffset;
|
||||
}
|
||||
|
||||
mFrom.left += (mVelocity.x * (curTime - mPrevTime) / 1000f);
|
||||
mFrom.top += (mVelocity.y * (curTime - mPrevTime) / 1000f);
|
||||
|
||||
dragView.setTranslationX(mFrom.left);
|
||||
dragView.setTranslationY(mFrom.top);
|
||||
dragView.setAlpha(1f - mAlphaInterpolator.getInterpolation(t));
|
||||
|
||||
mVelocity.x *= FRICTION;
|
||||
mVelocity.y *= FRICTION;
|
||||
mPrevTime = curTime;
|
||||
}
|
||||
};
|
||||
private AnimatorUpdateListener createFlingAlongVectorAnimatorListener(final DragLayer dragLayer,
|
||||
DragObject d, PointF vel, final long startTime, final int duration,
|
||||
ViewConfiguration config) {
|
||||
final Rect from = new Rect();
|
||||
dragLayer.getViewRectRelativeToSelf(d.dragView, from);
|
||||
|
||||
return new FlingAlongVectorAnimatorUpdateListener(dragLayer, vel, from, startTime);
|
||||
}
|
||||
|
||||
public void onFlingToDelete(final DragObject d, int x, int y, PointF vel) {
|
||||
// Don't highlight the icon as it's animating
|
||||
d.dragView.setColor(0);
|
||||
d.dragView.updateInitialScaleToCurrentScale();
|
||||
|
||||
if (mFlingDeleteMode == MODE_FLING_DELETE_TO_TRASH) {
|
||||
// Defer animating out the drop target if we are animating to it
|
||||
mSearchDropTargetBar.deferOnDragEnd();
|
||||
mSearchDropTargetBar.finishAnimations();
|
||||
}
|
||||
|
||||
final ViewConfiguration config = ViewConfiguration.get(mLauncher);
|
||||
final DragLayer dragLayer = mLauncher.getDragLayer();
|
||||
final int duration = DELETE_ANIMATION_DURATION;
|
||||
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);
|
||||
}
|
||||
};
|
||||
AnimatorUpdateListener updateCb = null;
|
||||
if (mFlingDeleteMode == MODE_FLING_DELETE_TO_TRASH) {
|
||||
updateCb = createFlingToTrashAnimatorListener(dragLayer, d, vel, config);
|
||||
} else if (mFlingDeleteMode == MODE_FLING_DELETE_ALONG_VECTOR) {
|
||||
updateCb = createFlingAlongVectorAnimatorListener(dragLayer, d, vel, startTime,
|
||||
duration, config);
|
||||
}
|
||||
Runnable onAnimationEndRunnable = new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
mSearchDropTargetBar.onDragEnd();
|
||||
mLauncher.exitSpringLoadedDragMode();
|
||||
completeDrop(d);
|
||||
}
|
||||
};
|
||||
dragLayer.animateView(d.dragView, updateCb, duration, tInterpolator, onAnimationEndRunnable,
|
||||
DragLayer.ANIMATION_END_DISAPPEAR, null);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -19,6 +19,7 @@ package com.android.launcher2;
|
||||
import android.content.Context;
|
||||
import android.graphics.Bitmap;
|
||||
import android.graphics.Point;
|
||||
import android.graphics.PointF;
|
||||
import android.graphics.Rect;
|
||||
import android.os.Handler;
|
||||
import android.os.IBinder;
|
||||
@@ -26,6 +27,7 @@ import android.os.Vibrator;
|
||||
import android.util.Log;
|
||||
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;
|
||||
@@ -60,6 +62,9 @@ public class DragController {
|
||||
static final int SCROLL_LEFT = 0;
|
||||
static final int SCROLL_RIGHT = 1;
|
||||
|
||||
private static final float MAX_FLING_DEGREES = 35f;
|
||||
private static final int FLING_TO_DELETE_THRESHOLD_Y_VELOCITY = -1400;
|
||||
|
||||
private Launcher mLauncher;
|
||||
private Handler mHandler;
|
||||
private final Vibrator mVibrator = new Vibrator();
|
||||
@@ -86,8 +91,8 @@ public class DragController {
|
||||
|
||||
/** 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;
|
||||
@@ -111,6 +116,9 @@ public class DragController {
|
||||
private int mTmpPoint[] = new int[2];
|
||||
private Rect mDragLayerRect = new Rect();
|
||||
|
||||
protected int mFlingToDeleteThresholdVelocity;
|
||||
private VelocityTracker mVelocityTracker;
|
||||
|
||||
/**
|
||||
* Interface to receive notifications when a drag starts or stops
|
||||
*/
|
||||
@@ -141,6 +149,10 @@ public class DragController {
|
||||
mLauncher = launcher;
|
||||
mHandler = new Handler();
|
||||
mScrollZone = launcher.getResources().getDimensionPixelSize(R.dimen.scroll_zone);
|
||||
mVelocityTracker = VelocityTracker.obtain();
|
||||
|
||||
float density = launcher.getResources().getDisplayMetrics().density;
|
||||
mFlingToDeleteThresholdVelocity = (int) (FLING_TO_DELETE_THRESHOLD_Y_VELOCITY * density);
|
||||
}
|
||||
|
||||
public boolean dragging() {
|
||||
@@ -378,15 +390,35 @@ public class DragController {
|
||||
if (mDragging) {
|
||||
mDragging = false;
|
||||
clearScrollRunnable();
|
||||
for (DragListener listener : mListeners) {
|
||||
listener.onDragEnd();
|
||||
}
|
||||
boolean isDeferred = false;
|
||||
if (mDragObject.dragView != null) {
|
||||
if (!mDragObject.deferDragViewCleanupPostAnimation) {
|
||||
isDeferred = mDragObject.deferDragViewCleanupPostAnimation;
|
||||
if (!isDeferred) {
|
||||
mDragObject.dragView.remove();
|
||||
}
|
||||
mDragObject.dragView = null;
|
||||
}
|
||||
|
||||
// Only end the drag if we are not deferred
|
||||
if (!isDeferred) {
|
||||
for (DragListener listener : mListeners) {
|
||||
listener.onDragEnd();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
releaseVelocityTracker();
|
||||
}
|
||||
|
||||
/**
|
||||
* This only gets called as a result of drag view cleanup being deferred in endDrag();
|
||||
*/
|
||||
void onDeferredEndDrag(DragView dragView) {
|
||||
dragView.remove();
|
||||
|
||||
// If we skipped calling onDragEnd() before, do it now
|
||||
for (DragListener listener : mListeners) {
|
||||
listener.onDragEnd();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -408,8 +440,11 @@ public class DragController {
|
||||
Log.d(Launcher.TAG, "DragController.onInterceptTouchEvent " + ev + " mDragging="
|
||||
+ mDragging);
|
||||
}
|
||||
final int action = ev.getAction();
|
||||
|
||||
// Update the velocity tracker
|
||||
acquireVelocityTrackerAndAddMovement(ev);
|
||||
|
||||
final int action = ev.getAction();
|
||||
final int[] dragLayerPos = getClampedDragLayerPos(ev.getX(), ev.getY());
|
||||
final int dragLayerX = dragLayerPos[0];
|
||||
final int dragLayerY = dragLayerPos[1];
|
||||
@@ -425,7 +460,12 @@ public class DragController {
|
||||
break;
|
||||
case MotionEvent.ACTION_UP:
|
||||
if (mDragging) {
|
||||
drop(dragLayerX, dragLayerY);
|
||||
PointF vec = isFlingingToDelete(mDragObject.dragSource);
|
||||
if (vec != null) {
|
||||
dropOnFlingToDeleteTarget(dragLayerX, dragLayerY, vec);
|
||||
} else {
|
||||
drop(dragLayerX, dragLayerY);
|
||||
}
|
||||
}
|
||||
endDrag();
|
||||
break;
|
||||
@@ -529,6 +569,9 @@ public class DragController {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Update the velocity tracker
|
||||
acquireVelocityTrackerAndAddMovement(ev);
|
||||
|
||||
final int action = ev.getAction();
|
||||
final int[] dragLayerPos = getClampedDragLayerPos(ev.getX(), ev.getY());
|
||||
final int dragLayerX = dragLayerPos[0];
|
||||
@@ -553,10 +596,15 @@ public class DragController {
|
||||
case MotionEvent.ACTION_UP:
|
||||
// Ensure that we've processed a move event at the current pointer location.
|
||||
handleMoveEvent(dragLayerX, dragLayerY);
|
||||
|
||||
mHandler.removeCallbacks(mScrollRunnable);
|
||||
|
||||
if (mDragging) {
|
||||
drop(dragLayerX, dragLayerY);
|
||||
PointF vec = isFlingingToDelete(mDragObject.dragSource);
|
||||
if (vec != null) {
|
||||
dropOnFlingToDeleteTarget(dragLayerX, dragLayerY, vec);
|
||||
} else {
|
||||
drop(dragLayerX, dragLayerY);
|
||||
}
|
||||
}
|
||||
endDrag();
|
||||
break;
|
||||
@@ -569,6 +617,58 @@ public class DragController {
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* 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());
|
||||
|
||||
if (mVelocityTracker.getYVelocity() < mFlingToDeleteThresholdVelocity) {
|
||||
// Do a quick dot product test to ensure that we are flinging upwards
|
||||
PointF vel = new PointF(mVelocityTracker.getXVelocity(),
|
||||
mVelocityTracker.getYVelocity());
|
||||
PointF upVec = new PointF(0f, -1f);
|
||||
float theta = (float) Math.acos(((vel.x * upVec.x) + (vel.y * upVec.y)) /
|
||||
(vel.length() * upVec.length()));
|
||||
if (theta <= Math.toRadians(MAX_FLING_DEGREES)) {
|
||||
return vel;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
private void dropOnFlingToDeleteTarget(float x, float y, PointF vel) {
|
||||
final int[] coordinates = mCoordinatesTemp;
|
||||
|
||||
mDragObject.x = coordinates[0];
|
||||
mDragObject.y = coordinates[1];
|
||||
mDragObject.dragComplete = true;
|
||||
|
||||
// Clean up dragging on the target if it's not the current fling delete target otherwise,
|
||||
// start dragging to it.
|
||||
if (mLastDropTarget != null && mFlingToDeleteDropTarget != mLastDropTarget) {
|
||||
mLastDropTarget.onDragExit(mDragObject);
|
||||
}
|
||||
|
||||
// Drop onto the fling-to-delete target
|
||||
boolean accepted = false;
|
||||
mFlingToDeleteDropTarget.onDragEnter(mDragObject);
|
||||
mFlingToDeleteDropTarget.onDragExit(mDragObject);
|
||||
if (mFlingToDeleteDropTarget.acceptDrop(mDragObject)) {
|
||||
mFlingToDeleteDropTarget.onFlingToDelete(mDragObject, mDragObject.x, mDragObject.y,
|
||||
vel);
|
||||
accepted = true;
|
||||
}
|
||||
mDragObject.dragSource.onDropCompleted((View) mFlingToDeleteDropTarget, mDragObject,
|
||||
accepted);
|
||||
}
|
||||
|
||||
private void drop(float x, float y) {
|
||||
final int[] coordinates = mCoordinatesTemp;
|
||||
final DropTarget dropTarget = findDropTarget((int) x, (int) y, coordinates);
|
||||
@@ -658,6 +758,27 @@ public class DragController {
|
||||
mDropTargets.remove(target);
|
||||
}
|
||||
|
||||
/**
|
||||
* 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.
|
||||
*/
|
||||
|
||||
@@ -18,15 +18,15 @@ package com.android.launcher2;
|
||||
|
||||
import android.animation.Animator;
|
||||
import android.animation.AnimatorListenerAdapter;
|
||||
import android.animation.ObjectAnimator;
|
||||
import android.animation.TimeInterpolator;
|
||||
import android.animation.ValueAnimator;
|
||||
import android.animation.ValueAnimator.AnimatorUpdateListener;
|
||||
import android.content.Context;
|
||||
import android.content.res.Resources;
|
||||
import android.graphics.Canvas;
|
||||
import android.graphics.Paint;
|
||||
import android.graphics.PointF;
|
||||
import android.graphics.Rect;
|
||||
import android.graphics.drawable.Drawable;
|
||||
import android.util.AttributeSet;
|
||||
import android.view.KeyEvent;
|
||||
import android.view.MotionEvent;
|
||||
@@ -34,6 +34,7 @@ import android.view.View;
|
||||
import android.view.ViewParent;
|
||||
import android.view.accessibility.AccessibilityEvent;
|
||||
import android.view.accessibility.AccessibilityManager;
|
||||
import android.view.animation.AnimationUtils;
|
||||
import android.view.animation.DecelerateInterpolator;
|
||||
import android.view.animation.Interpolator;
|
||||
import android.widget.FrameLayout;
|
||||
@@ -542,35 +543,17 @@ public class DragLayer extends FrameLayout {
|
||||
duration = Math.max(duration, res.getInteger(R.integer.config_dropAnimMinDuration));
|
||||
}
|
||||
|
||||
if (mDropAnim != null) {
|
||||
mDropAnim.cancel();
|
||||
}
|
||||
|
||||
if (mFadeOutAnim != null) {
|
||||
mFadeOutAnim.cancel();
|
||||
}
|
||||
|
||||
// Show the drop view if it was previously hidden
|
||||
mDropView = view;
|
||||
mDropView.cancelAnimation();
|
||||
mDropView.resetLayoutParams();
|
||||
mDropAnim = new ValueAnimator();
|
||||
// Fall back to cubic ease out interpolator for the animation if none is specified
|
||||
TimeInterpolator interpolator = null;
|
||||
if (alphaInterpolator == null || motionInterpolator == null) {
|
||||
mDropAnim.setInterpolator(mCubicEaseOutInterpolator);
|
||||
interpolator = mCubicEaseOutInterpolator;
|
||||
}
|
||||
|
||||
if (anchorView != null) {
|
||||
mAnchorViewInitialScrollX = anchorView.getScrollX();
|
||||
}
|
||||
mAnchorView = anchorView;
|
||||
|
||||
// Animate the view
|
||||
final float initAlpha = view.getAlpha();
|
||||
final float dropViewScale = mDropView.getScaleX();
|
||||
|
||||
mDropAnim.setDuration(duration);
|
||||
mDropAnim.setFloatValues(0.0f, 1.0f);
|
||||
mDropAnim.removeAllUpdateListeners();
|
||||
mDropAnim.addUpdateListener(new AnimatorUpdateListener() {
|
||||
final float dropViewScale = view.getScaleX();
|
||||
AnimatorUpdateListener updateCb = new AnimatorUpdateListener() {
|
||||
@Override
|
||||
public void onAnimationUpdate(ValueAnimator animation) {
|
||||
final float percent = (Float) animation.getAnimatedValue();
|
||||
final int width = view.getMeasuredWidth();
|
||||
@@ -603,7 +586,35 @@ public class DragLayer extends FrameLayout {
|
||||
mDropView.setScaleY(scaleY);
|
||||
mDropView.setAlpha(alpha);
|
||||
}
|
||||
});
|
||||
};
|
||||
animateView(view, updateCb, duration, interpolator, onCompleteRunnable, animationEndStyle,
|
||||
anchorView);
|
||||
}
|
||||
|
||||
public void animateView(final DragView view, AnimatorUpdateListener updateCb, int duration,
|
||||
TimeInterpolator interpolator, final Runnable onCompleteRunnable,
|
||||
final int animationEndStyle, View anchorView) {
|
||||
// Clean up the previous animations
|
||||
if (mDropAnim != null) mDropAnim.cancel();
|
||||
if (mFadeOutAnim != null) mFadeOutAnim.cancel();
|
||||
|
||||
// Show the drop view if it was previously hidden
|
||||
mDropView = view;
|
||||
mDropView.cancelAnimation();
|
||||
mDropView.resetLayoutParams();
|
||||
|
||||
// Set the anchor view if the page is scrolling
|
||||
if (anchorView != null) {
|
||||
mAnchorViewInitialScrollX = anchorView.getScrollX();
|
||||
}
|
||||
mAnchorView = anchorView;
|
||||
|
||||
// Create and start the animation
|
||||
mDropAnim = new ValueAnimator();
|
||||
mDropAnim.setInterpolator(interpolator);
|
||||
mDropAnim.setDuration(duration);
|
||||
mDropAnim.setFloatValues(0f, 1f);
|
||||
mDropAnim.addUpdateListener(updateCb);
|
||||
mDropAnim.addListener(new AnimatorListenerAdapter() {
|
||||
public void onAnimationEnd(Animator animation) {
|
||||
if (onCompleteRunnable != null) {
|
||||
@@ -629,7 +640,7 @@ public class DragLayer extends FrameLayout {
|
||||
mDropAnim.cancel();
|
||||
}
|
||||
if (mDropView != null) {
|
||||
mDropView.remove();
|
||||
mDragController.onDeferredEndDrag(mDropView);
|
||||
}
|
||||
mDropView = null;
|
||||
invalidate();
|
||||
@@ -655,7 +666,7 @@ public class DragLayer extends FrameLayout {
|
||||
mFadeOutAnim.addListener(new AnimatorListenerAdapter() {
|
||||
public void onAnimationEnd(Animator animation) {
|
||||
if (mDropView != null) {
|
||||
mDropView.remove();
|
||||
mDragController.onDeferredEndDrag(mDropView);
|
||||
}
|
||||
mDropView = null;
|
||||
invalidate();
|
||||
|
||||
@@ -25,5 +25,6 @@ import com.android.launcher2.DropTarget.DragObject;
|
||||
*
|
||||
*/
|
||||
public interface DragSource {
|
||||
boolean supportsFlingToDelete();
|
||||
void onDropCompleted(View target, DragObject d, boolean success);
|
||||
}
|
||||
|
||||
@@ -51,6 +51,7 @@ public class DragView extends View {
|
||||
ValueAnimator mAnim;
|
||||
private float mOffsetX = 0.0f;
|
||||
private float mOffsetY = 0.0f;
|
||||
private float mInitialScale = 1f;
|
||||
|
||||
/**
|
||||
* Construct the drag view.
|
||||
@@ -67,6 +68,7 @@ public class DragView extends View {
|
||||
int left, int top, int width, int height, final float initialScale) {
|
||||
super(launcher);
|
||||
mDragLayer = launcher.getDragLayer();
|
||||
mInitialScale = initialScale;
|
||||
|
||||
final Resources res = getResources();
|
||||
final float offsetX = res.getDimensionPixelSize(R.dimen.dragViewOffsetX);
|
||||
@@ -151,6 +153,14 @@ public class DragView extends View {
|
||||
return mDragRegion;
|
||||
}
|
||||
|
||||
public float getInitialScale() {
|
||||
return mInitialScale;
|
||||
}
|
||||
|
||||
public void updateInitialScaleToCurrentScale() {
|
||||
mInitialScale = getScaleX();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
|
||||
setMeasuredDimension(mBitmap.getWidth(), mBitmap.getHeight());
|
||||
@@ -233,6 +243,11 @@ public class DragView extends View {
|
||||
*/
|
||||
public void show(int touchX, int touchY) {
|
||||
mDragLayer.addView(this);
|
||||
|
||||
// Enable hw-layers on this view
|
||||
setLayerType(View.LAYER_TYPE_HARDWARE, null);
|
||||
|
||||
// Start the pick-up animation
|
||||
DragLayer.LayoutParams lp = new DragLayer.LayoutParams(0, 0);
|
||||
lp.width = mBitmap.getWidth();
|
||||
lp.height = mBitmap.getHeight();
|
||||
@@ -267,6 +282,9 @@ public class DragView extends View {
|
||||
|
||||
void remove() {
|
||||
if (getParent() != null) {
|
||||
// Disable hw-layers on this view
|
||||
setLayerType(View.LAYER_TYPE_NONE, null);
|
||||
|
||||
mDragLayer.removeView(DragView.this);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -16,6 +16,7 @@
|
||||
|
||||
package com.android.launcher2;
|
||||
|
||||
import android.graphics.PointF;
|
||||
import android.graphics.Rect;
|
||||
|
||||
/**
|
||||
@@ -91,6 +92,13 @@ 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, int x, int y, PointF vec);
|
||||
|
||||
/**
|
||||
* Allows a DropTarget to delegate drag and drop events to another object.
|
||||
*
|
||||
|
||||
@@ -22,6 +22,7 @@ import android.animation.ObjectAnimator;
|
||||
import android.animation.PropertyValuesHolder;
|
||||
import android.content.Context;
|
||||
import android.content.res.Resources;
|
||||
import android.graphics.PointF;
|
||||
import android.graphics.Rect;
|
||||
import android.graphics.drawable.Drawable;
|
||||
import android.text.InputType;
|
||||
@@ -35,7 +36,6 @@ import android.view.Menu;
|
||||
import android.view.MenuItem;
|
||||
import android.view.MotionEvent;
|
||||
import android.view.View;
|
||||
import android.view.View.MeasureSpec;
|
||||
import android.view.accessibility.AccessibilityEvent;
|
||||
import android.view.accessibility.AccessibilityManager;
|
||||
import android.view.inputmethod.EditorInfo;
|
||||
@@ -653,6 +653,10 @@ public class Folder extends LinearLayout implements DragSource, View.OnClickList
|
||||
updateItemLocationsInDatabase();
|
||||
}
|
||||
|
||||
public boolean supportsFlingToDelete() {
|
||||
return true;
|
||||
}
|
||||
|
||||
private void updateItemLocationsInDatabase() {
|
||||
ArrayList<View> list = getItemsInReadingOrder();
|
||||
for (int i = 0; i < list.size(); i++) {
|
||||
@@ -924,6 +928,10 @@ public class Folder extends LinearLayout implements DragSource, View.OnClickList
|
||||
mInfo.add(item);
|
||||
}
|
||||
|
||||
public void onFlingToDelete(DragObject d, int x, int y, PointF vec) {
|
||||
// Do nothing
|
||||
}
|
||||
|
||||
public void onAdd(ShortcutInfo item) {
|
||||
mItemsInvalidated = true;
|
||||
// If the item was dropped onto this open folder, we have done the work associated
|
||||
|
||||
@@ -69,6 +69,7 @@ public class SearchDropTargetBar extends FrameLayout implements DragController.D
|
||||
dragController.addDragListener(mDeleteDropTarget);
|
||||
dragController.addDropTarget(mInfoDropTarget);
|
||||
dragController.addDropTarget(mDeleteDropTarget);
|
||||
dragController.setFlingToDeleteDropTarget(mDeleteDropTarget);
|
||||
mInfoDropTarget.setLauncher(launcher);
|
||||
mDeleteDropTarget.setLauncher(launcher);
|
||||
}
|
||||
@@ -153,6 +154,13 @@ public class SearchDropTargetBar extends FrameLayout implements DragController.D
|
||||
});
|
||||
}
|
||||
|
||||
public void finishAnimations() {
|
||||
mDropTargetBarFadeInAnim.end();
|
||||
mDropTargetBarFadeOutAnim.end();
|
||||
mQSBSearchBarFadeInAnim.end();
|
||||
mQSBSearchBarFadeOutAnim.end();
|
||||
}
|
||||
|
||||
private void cancelAnimations() {
|
||||
mDropTargetBarFadeInAnim.cancel();
|
||||
mDropTargetBarFadeOutAnim.cancel();
|
||||
|
||||
@@ -38,6 +38,7 @@ import android.graphics.Canvas;
|
||||
import android.graphics.Matrix;
|
||||
import android.graphics.Paint;
|
||||
import android.graphics.Point;
|
||||
import android.graphics.PointF;
|
||||
import android.graphics.Rect;
|
||||
import android.graphics.Region.Op;
|
||||
import android.graphics.drawable.Drawable;
|
||||
@@ -2333,6 +2334,10 @@ public class Workspace extends SmoothPagedView
|
||||
}
|
||||
}
|
||||
|
||||
public void onFlingToDelete(DragObject d, int x, int y, PointF vec) {
|
||||
// Do nothing
|
||||
}
|
||||
|
||||
public void setFinalScrollForPageChange(int screen) {
|
||||
if (screen >= 0) {
|
||||
mSavedScrollX = getScrollX();
|
||||
@@ -3325,6 +3330,10 @@ public class Workspace extends SmoothPagedView
|
||||
}
|
||||
}
|
||||
|
||||
public boolean supportsFlingToDelete() {
|
||||
return true;
|
||||
}
|
||||
|
||||
public boolean isDropEnabled() {
|
||||
return true;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user