1ddec73471eb6cc1f15eb9421a205bb2362509075Sunny Goyalpackage com.android.launcher3.util; 2ddec73471eb6cc1f15eb9421a205bb2362509075Sunny Goyal 3ddec73471eb6cc1f15eb9421a205bb2362509075Sunny Goyalimport android.animation.TimeInterpolator; 4ddec73471eb6cc1f15eb9421a205bb2362509075Sunny Goyalimport android.animation.ValueAnimator; 5ddec73471eb6cc1f15eb9421a205bb2362509075Sunny Goyalimport android.animation.ValueAnimator.AnimatorUpdateListener; 6ddec73471eb6cc1f15eb9421a205bb2362509075Sunny Goyalimport android.graphics.PointF; 7ddec73471eb6cc1f15eb9421a205bb2362509075Sunny Goyalimport android.graphics.Rect; 80f76b56865bd7b63bd21d53aaac47300396aa38fSunny Goyalimport android.view.animation.AnimationUtils; 9ddec73471eb6cc1f15eb9421a205bb2362509075Sunny Goyalimport android.view.animation.DecelerateInterpolator; 10ddec73471eb6cc1f15eb9421a205bb2362509075Sunny Goyal 110f76b56865bd7b63bd21d53aaac47300396aa38fSunny Goyalimport com.android.launcher3.ButtonDropTarget; 12ddec73471eb6cc1f15eb9421a205bb2362509075Sunny Goyalimport com.android.launcher3.DropTarget.DragObject; 130f76b56865bd7b63bd21d53aaac47300396aa38fSunny Goyalimport com.android.launcher3.Launcher; 14fedca43d396d6fd7c46fbb2f37dfa7cfe3b31834Vadim Tryshevimport com.android.launcher3.dragndrop.DragLayer; 15fedca43d396d6fd7c46fbb2f37dfa7cfe3b31834Vadim Tryshevimport com.android.launcher3.dragndrop.DragView; 16ddec73471eb6cc1f15eb9421a205bb2362509075Sunny Goyal 170f76b56865bd7b63bd21d53aaac47300396aa38fSunny Goyalpublic class FlingAnimation implements AnimatorUpdateListener, Runnable { 18ddec73471eb6cc1f15eb9421a205bb2362509075Sunny Goyal 19ddec73471eb6cc1f15eb9421a205bb2362509075Sunny Goyal /** 20ddec73471eb6cc1f15eb9421a205bb2362509075Sunny Goyal * Maximum acceleration in one dimension (pixels per milliseconds) 21ddec73471eb6cc1f15eb9421a205bb2362509075Sunny Goyal */ 22ddec73471eb6cc1f15eb9421a205bb2362509075Sunny Goyal private static final float MAX_ACCELERATION = 0.5f; 23ddec73471eb6cc1f15eb9421a205bb2362509075Sunny Goyal private static final int DRAG_END_DELAY = 300; 24ddec73471eb6cc1f15eb9421a205bb2362509075Sunny Goyal 250f76b56865bd7b63bd21d53aaac47300396aa38fSunny Goyal private final ButtonDropTarget mDropTarget; 260f76b56865bd7b63bd21d53aaac47300396aa38fSunny Goyal private final Launcher mLauncher; 270f76b56865bd7b63bd21d53aaac47300396aa38fSunny Goyal 28ddec73471eb6cc1f15eb9421a205bb2362509075Sunny Goyal protected final DragObject mDragObject; 29ddec73471eb6cc1f15eb9421a205bb2362509075Sunny Goyal protected final DragLayer mDragLayer; 30ddec73471eb6cc1f15eb9421a205bb2362509075Sunny Goyal protected final TimeInterpolator mAlphaInterpolator = new DecelerateInterpolator(0.75f); 310f76b56865bd7b63bd21d53aaac47300396aa38fSunny Goyal protected final float mUX, mUY; 320f76b56865bd7b63bd21d53aaac47300396aa38fSunny Goyal 330f76b56865bd7b63bd21d53aaac47300396aa38fSunny Goyal protected Rect mIconRect; 340f76b56865bd7b63bd21d53aaac47300396aa38fSunny Goyal protected Rect mFrom; 350f76b56865bd7b63bd21d53aaac47300396aa38fSunny Goyal protected int mDuration; 360f76b56865bd7b63bd21d53aaac47300396aa38fSunny Goyal protected float mAnimationTimeFraction; 37ddec73471eb6cc1f15eb9421a205bb2362509075Sunny Goyal 38ddec73471eb6cc1f15eb9421a205bb2362509075Sunny Goyal protected float mAX, mAY; 39ddec73471eb6cc1f15eb9421a205bb2362509075Sunny Goyal 400f76b56865bd7b63bd21d53aaac47300396aa38fSunny Goyal public FlingAnimation(DragObject d, PointF vel, ButtonDropTarget dropTarget, Launcher launcher) { 410f76b56865bd7b63bd21d53aaac47300396aa38fSunny Goyal mDropTarget = dropTarget; 420f76b56865bd7b63bd21d53aaac47300396aa38fSunny Goyal mLauncher = launcher; 43ddec73471eb6cc1f15eb9421a205bb2362509075Sunny Goyal mDragObject = d; 44ddec73471eb6cc1f15eb9421a205bb2362509075Sunny Goyal mUX = vel.x / 1000; 45ddec73471eb6cc1f15eb9421a205bb2362509075Sunny Goyal mUY = vel.y / 1000; 460f76b56865bd7b63bd21d53aaac47300396aa38fSunny Goyal mDragLayer = mLauncher.getDragLayer(); 470f76b56865bd7b63bd21d53aaac47300396aa38fSunny Goyal } 48ddec73471eb6cc1f15eb9421a205bb2362509075Sunny Goyal 490f76b56865bd7b63bd21d53aaac47300396aa38fSunny Goyal @Override 500f76b56865bd7b63bd21d53aaac47300396aa38fSunny Goyal public void run() { 510f76b56865bd7b63bd21d53aaac47300396aa38fSunny Goyal mIconRect = mDropTarget.getIconRect(mDragObject); 52ddec73471eb6cc1f15eb9421a205bb2362509075Sunny Goyal 530f76b56865bd7b63bd21d53aaac47300396aa38fSunny Goyal // Initiate from 540f76b56865bd7b63bd21d53aaac47300396aa38fSunny Goyal mFrom = new Rect(); 550f76b56865bd7b63bd21d53aaac47300396aa38fSunny Goyal mDragLayer.getViewRectRelativeToSelf(mDragObject.dragView, mFrom); 560f76b56865bd7b63bd21d53aaac47300396aa38fSunny Goyal float scale = mDragObject.dragView.getScaleX(); 570f76b56865bd7b63bd21d53aaac47300396aa38fSunny Goyal float xOffset = ((scale - 1f) * mDragObject.dragView.getMeasuredWidth()) / 2f; 580f76b56865bd7b63bd21d53aaac47300396aa38fSunny Goyal float yOffset = ((scale - 1f) * mDragObject.dragView.getMeasuredHeight()) / 2f; 59ddec73471eb6cc1f15eb9421a205bb2362509075Sunny Goyal mFrom.left += xOffset; 60ddec73471eb6cc1f15eb9421a205bb2362509075Sunny Goyal mFrom.right -= xOffset; 61ddec73471eb6cc1f15eb9421a205bb2362509075Sunny Goyal mFrom.top += yOffset; 62ddec73471eb6cc1f15eb9421a205bb2362509075Sunny Goyal mFrom.bottom -= yOffset; 630f76b56865bd7b63bd21d53aaac47300396aa38fSunny Goyal mDuration = Math.abs(mUY) > Math.abs(mUX) ? initFlingUpDuration() : initFlingLeftDuration(); 64ddec73471eb6cc1f15eb9421a205bb2362509075Sunny Goyal 65ddec73471eb6cc1f15eb9421a205bb2362509075Sunny Goyal mAnimationTimeFraction = ((float) mDuration) / (mDuration + DRAG_END_DELAY); 660f76b56865bd7b63bd21d53aaac47300396aa38fSunny Goyal 670f76b56865bd7b63bd21d53aaac47300396aa38fSunny Goyal // Don't highlight the icon as it's animating 680f76b56865bd7b63bd21d53aaac47300396aa38fSunny Goyal mDragObject.dragView.setColor(0); 690f76b56865bd7b63bd21d53aaac47300396aa38fSunny Goyal 700f76b56865bd7b63bd21d53aaac47300396aa38fSunny Goyal final int duration = mDuration + DRAG_END_DELAY; 710f76b56865bd7b63bd21d53aaac47300396aa38fSunny Goyal final long startTime = AnimationUtils.currentAnimationTimeMillis(); 720f76b56865bd7b63bd21d53aaac47300396aa38fSunny Goyal 730f76b56865bd7b63bd21d53aaac47300396aa38fSunny Goyal // NOTE: Because it takes time for the first frame of animation to actually be 740f76b56865bd7b63bd21d53aaac47300396aa38fSunny Goyal // called and we expect the animation to be a continuation of the fling, we have 750f76b56865bd7b63bd21d53aaac47300396aa38fSunny Goyal // to account for the time that has elapsed since the fling finished. And since 760f76b56865bd7b63bd21d53aaac47300396aa38fSunny Goyal // we don't have a startDelay, we will always get call to update when we call 770f76b56865bd7b63bd21d53aaac47300396aa38fSunny Goyal // start() (which we want to ignore). 780f76b56865bd7b63bd21d53aaac47300396aa38fSunny Goyal final TimeInterpolator tInterpolator = new TimeInterpolator() { 790f76b56865bd7b63bd21d53aaac47300396aa38fSunny Goyal private int mCount = -1; 800f76b56865bd7b63bd21d53aaac47300396aa38fSunny Goyal private float mOffset = 0f; 810f76b56865bd7b63bd21d53aaac47300396aa38fSunny Goyal 820f76b56865bd7b63bd21d53aaac47300396aa38fSunny Goyal @Override 830f76b56865bd7b63bd21d53aaac47300396aa38fSunny Goyal public float getInterpolation(float t) { 840f76b56865bd7b63bd21d53aaac47300396aa38fSunny Goyal if (mCount < 0) { 850f76b56865bd7b63bd21d53aaac47300396aa38fSunny Goyal mCount++; 860f76b56865bd7b63bd21d53aaac47300396aa38fSunny Goyal } else if (mCount == 0) { 870f76b56865bd7b63bd21d53aaac47300396aa38fSunny Goyal mOffset = Math.min(0.5f, (float) (AnimationUtils.currentAnimationTimeMillis() - 880f76b56865bd7b63bd21d53aaac47300396aa38fSunny Goyal startTime) / duration); 890f76b56865bd7b63bd21d53aaac47300396aa38fSunny Goyal mCount++; 900f76b56865bd7b63bd21d53aaac47300396aa38fSunny Goyal } 910f76b56865bd7b63bd21d53aaac47300396aa38fSunny Goyal return Math.min(1f, mOffset + t); 920f76b56865bd7b63bd21d53aaac47300396aa38fSunny Goyal } 930f76b56865bd7b63bd21d53aaac47300396aa38fSunny Goyal }; 940f76b56865bd7b63bd21d53aaac47300396aa38fSunny Goyal 950f76b56865bd7b63bd21d53aaac47300396aa38fSunny Goyal Runnable onAnimationEndRunnable = new Runnable() { 960f76b56865bd7b63bd21d53aaac47300396aa38fSunny Goyal @Override 970f76b56865bd7b63bd21d53aaac47300396aa38fSunny Goyal public void run() { 980f76b56865bd7b63bd21d53aaac47300396aa38fSunny Goyal mLauncher.exitSpringLoadedDragMode(); 990f76b56865bd7b63bd21d53aaac47300396aa38fSunny Goyal mDropTarget.completeDrop(mDragObject); 1000f76b56865bd7b63bd21d53aaac47300396aa38fSunny Goyal } 1010f76b56865bd7b63bd21d53aaac47300396aa38fSunny Goyal }; 1020f76b56865bd7b63bd21d53aaac47300396aa38fSunny Goyal 1030f76b56865bd7b63bd21d53aaac47300396aa38fSunny Goyal mDragLayer.animateView(mDragObject.dragView, this, duration, tInterpolator, 1040f76b56865bd7b63bd21d53aaac47300396aa38fSunny Goyal onAnimationEndRunnable, DragLayer.ANIMATION_END_DISAPPEAR, null); 105ddec73471eb6cc1f15eb9421a205bb2362509075Sunny Goyal } 106ddec73471eb6cc1f15eb9421a205bb2362509075Sunny Goyal 107ddec73471eb6cc1f15eb9421a205bb2362509075Sunny Goyal /** 108ddec73471eb6cc1f15eb9421a205bb2362509075Sunny Goyal * The fling animation is based on the following system 109ddec73471eb6cc1f15eb9421a205bb2362509075Sunny Goyal * - Apply a constant force in the y direction to causing the fling to decelerate. 110ddec73471eb6cc1f15eb9421a205bb2362509075Sunny Goyal * - The animation runs for the time taken by the object to go out of the screen. 111ddec73471eb6cc1f15eb9421a205bb2362509075Sunny Goyal * - Calculate a constant acceleration in x direction such that the object reaches 112ddec73471eb6cc1f15eb9421a205bb2362509075Sunny Goyal * {@link #mIconRect} in the given time. 113ddec73471eb6cc1f15eb9421a205bb2362509075Sunny Goyal */ 114ab946a19d41f245e27580281c3904e6c6636bcddTony Wickham protected int initFlingUpDuration() { 115ddec73471eb6cc1f15eb9421a205bb2362509075Sunny Goyal float sY = -mFrom.bottom; 116ddec73471eb6cc1f15eb9421a205bb2362509075Sunny Goyal 117ddec73471eb6cc1f15eb9421a205bb2362509075Sunny Goyal float d = mUY * mUY + 2 * sY * MAX_ACCELERATION; 118ddec73471eb6cc1f15eb9421a205bb2362509075Sunny Goyal if (d >= 0) { 119ddec73471eb6cc1f15eb9421a205bb2362509075Sunny Goyal // sY can be reached under the MAX_ACCELERATION. Use MAX_ACCELERATION for y direction. 120ddec73471eb6cc1f15eb9421a205bb2362509075Sunny Goyal mAY = MAX_ACCELERATION; 121ddec73471eb6cc1f15eb9421a205bb2362509075Sunny Goyal } else { 122ddec73471eb6cc1f15eb9421a205bb2362509075Sunny Goyal // sY is not reachable, decrease the acceleration so that sY is almost reached. 123ddec73471eb6cc1f15eb9421a205bb2362509075Sunny Goyal d = 0; 124ddec73471eb6cc1f15eb9421a205bb2362509075Sunny Goyal mAY = mUY * mUY / (2 * -sY); 125ddec73471eb6cc1f15eb9421a205bb2362509075Sunny Goyal } 126ddec73471eb6cc1f15eb9421a205bb2362509075Sunny Goyal double t = (-mUY - Math.sqrt(d)) / mAY; 127ddec73471eb6cc1f15eb9421a205bb2362509075Sunny Goyal 128ddec73471eb6cc1f15eb9421a205bb2362509075Sunny Goyal float sX = -mFrom.exactCenterX() + mIconRect.exactCenterX(); 129ddec73471eb6cc1f15eb9421a205bb2362509075Sunny Goyal 130ddec73471eb6cc1f15eb9421a205bb2362509075Sunny Goyal // Find horizontal acceleration such that: u*t + a*t*t/2 = s 131ddec73471eb6cc1f15eb9421a205bb2362509075Sunny Goyal mAX = (float) ((sX - t * mUX) * 2 / (t * t)); 132ddec73471eb6cc1f15eb9421a205bb2362509075Sunny Goyal return (int) Math.round(t); 133ddec73471eb6cc1f15eb9421a205bb2362509075Sunny Goyal } 134ddec73471eb6cc1f15eb9421a205bb2362509075Sunny Goyal 135ab946a19d41f245e27580281c3904e6c6636bcddTony Wickham /** 136ab946a19d41f245e27580281c3904e6c6636bcddTony Wickham * The fling animation is based on the following system 137ab946a19d41f245e27580281c3904e6c6636bcddTony Wickham * - Apply a constant force in the x direction to causing the fling to decelerate. 138ab946a19d41f245e27580281c3904e6c6636bcddTony Wickham * - The animation runs for the time taken by the object to go out of the screen. 139ab946a19d41f245e27580281c3904e6c6636bcddTony Wickham * - Calculate a constant acceleration in y direction such that the object reaches 140ab946a19d41f245e27580281c3904e6c6636bcddTony Wickham * {@link #mIconRect} in the given time. 141ab946a19d41f245e27580281c3904e6c6636bcddTony Wickham */ 142ab946a19d41f245e27580281c3904e6c6636bcddTony Wickham protected int initFlingLeftDuration() { 143ab946a19d41f245e27580281c3904e6c6636bcddTony Wickham float sX = -mFrom.right; 144ab946a19d41f245e27580281c3904e6c6636bcddTony Wickham 145ab946a19d41f245e27580281c3904e6c6636bcddTony Wickham float d = mUX * mUX + 2 * sX * MAX_ACCELERATION; 146ab946a19d41f245e27580281c3904e6c6636bcddTony Wickham if (d >= 0) { 147ab946a19d41f245e27580281c3904e6c6636bcddTony Wickham // sX can be reached under the MAX_ACCELERATION. Use MAX_ACCELERATION for x direction. 148ab946a19d41f245e27580281c3904e6c6636bcddTony Wickham mAX = MAX_ACCELERATION; 149ab946a19d41f245e27580281c3904e6c6636bcddTony Wickham } else { 150ab946a19d41f245e27580281c3904e6c6636bcddTony Wickham // sX is not reachable, decrease the acceleration so that sX is almost reached. 151ab946a19d41f245e27580281c3904e6c6636bcddTony Wickham d = 0; 152ab946a19d41f245e27580281c3904e6c6636bcddTony Wickham mAX = mUX * mUX / (2 * -sX); 153ab946a19d41f245e27580281c3904e6c6636bcddTony Wickham } 154ab946a19d41f245e27580281c3904e6c6636bcddTony Wickham double t = (-mUX - Math.sqrt(d)) / mAX; 155ab946a19d41f245e27580281c3904e6c6636bcddTony Wickham 156ab946a19d41f245e27580281c3904e6c6636bcddTony Wickham float sY = -mFrom.exactCenterY() + mIconRect.exactCenterY(); 157ab946a19d41f245e27580281c3904e6c6636bcddTony Wickham 158ab946a19d41f245e27580281c3904e6c6636bcddTony Wickham // Find vertical acceleration such that: u*t + a*t*t/2 = s 159ab946a19d41f245e27580281c3904e6c6636bcddTony Wickham mAY = (float) ((sY - t * mUY) * 2 / (t * t)); 160ab946a19d41f245e27580281c3904e6c6636bcddTony Wickham return (int) Math.round(t); 161ab946a19d41f245e27580281c3904e6c6636bcddTony Wickham } 162ab946a19d41f245e27580281c3904e6c6636bcddTony Wickham 163ddec73471eb6cc1f15eb9421a205bb2362509075Sunny Goyal @Override 164ddec73471eb6cc1f15eb9421a205bb2362509075Sunny Goyal public void onAnimationUpdate(ValueAnimator animation) { 165ddec73471eb6cc1f15eb9421a205bb2362509075Sunny Goyal float t = animation.getAnimatedFraction(); 166ddec73471eb6cc1f15eb9421a205bb2362509075Sunny Goyal if (t > mAnimationTimeFraction) { 167ddec73471eb6cc1f15eb9421a205bb2362509075Sunny Goyal t = 1; 168ddec73471eb6cc1f15eb9421a205bb2362509075Sunny Goyal } else { 169ddec73471eb6cc1f15eb9421a205bb2362509075Sunny Goyal t = t / mAnimationTimeFraction; 170ddec73471eb6cc1f15eb9421a205bb2362509075Sunny Goyal } 171ddec73471eb6cc1f15eb9421a205bb2362509075Sunny Goyal final DragView dragView = (DragView) mDragLayer.getAnimatedView(); 172ddec73471eb6cc1f15eb9421a205bb2362509075Sunny Goyal final float time = t * mDuration; 173ddec73471eb6cc1f15eb9421a205bb2362509075Sunny Goyal dragView.setTranslationX(time * mUX + mFrom.left + mAX * time * time / 2); 174ddec73471eb6cc1f15eb9421a205bb2362509075Sunny Goyal dragView.setTranslationY(time * mUY + mFrom.top + mAY * time * time / 2); 175ddec73471eb6cc1f15eb9421a205bb2362509075Sunny Goyal dragView.setAlpha(1f - mAlphaInterpolator.getInterpolation(t)); 176ddec73471eb6cc1f15eb9421a205bb2362509075Sunny Goyal } 177ddec73471eb6cc1f15eb9421a205bb2362509075Sunny Goyal} 178