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