14c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein/*
24c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein * Copyright (C) 2013 The Android Open Source Project
34c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein *
44c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein * Licensed under the Apache License, Version 2.0 (the "License");
54c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein * you may not use this file except in compliance with the License.
64c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein * You may obtain a copy of the License at
74c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein *
84c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein *      http://www.apache.org/licenses/LICENSE-2.0
94c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein *
104c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein * Unless required by applicable law or agreed to in writing, software
114c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein * distributed under the License is distributed on an "AS IS" BASIS,
124c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
134c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein * See the License for the specific language governing permissions and
144c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein * limitations under the License.
154c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein */
164c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein
174c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzsteinpackage com.android.deskclock.widget.sgv;
184c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein
194c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzsteinimport android.content.Context;
204c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzsteinimport android.hardware.SensorManager;
214c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzsteinimport android.util.FloatMath;
224c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzsteinimport android.util.Log;
234c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzsteinimport android.view.ViewConfiguration;
244c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzsteinimport android.view.animation.AnimationUtils;
254c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzsteinimport android.view.animation.Interpolator;
264c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein
274c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein/**
284c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein * Temporarily copied from the framework so that StaggeredGridView can properly show the bounce at
294c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein * the end of flings. See TODO and b/8252293 for more info.
304c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein */
314c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein/**
324c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein * This class encapsulates scrolling with the ability to overshoot the bounds
334c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein * of a scrolling operation. This class is a drop-in replacement for
344c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein * {@link android.widget.Scroller} in most cases.
354c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein */
364c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzsteinpublic class OverScrollerSGV {
374c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein    private int mMode;
384c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein
394c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein    private final SplineOverScroller mScrollerX;
404c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein    private final SplineOverScroller mScrollerY;
414c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein
424c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein    private Interpolator mInterpolator;
434c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein
444c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein    private final boolean mFlywheel;
454c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein
464c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein    private static final int DEFAULT_DURATION = 250;
474c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein    private static final int SCROLL_MODE = 0;
484c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein    private static final int FLING_MODE = 1;
494c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein
504c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein    /**
514c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein     * Creates an OverScroller with a viscous fluid scroll interpolator and flywheel.
524c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein     * @param context
534c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein     */
544c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein    public OverScrollerSGV(Context context) {
554c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein        this(context, null);
564c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein    }
574c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein
584c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein    /**
594c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein     * Creates an OverScroller with flywheel enabled.
604c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein     * @param context The context of this application.
614c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein     * @param interpolator The scroll interpolator. If null, a default (viscous) interpolator will
624c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein     * be used.
634c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein     */
644c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein    public OverScrollerSGV(Context context, Interpolator interpolator) {
654c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein        this(context, interpolator, true);
664c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein    }
674c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein
684c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein    /**
694c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein     * Creates an OverScroller.
704c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein     * @param context The context of this application.
714c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein     * @param interpolator The scroll interpolator. If null, a default (viscous) interpolator will
724c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein     * be used.
734c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein     * @param flywheel If true, successive fling motions will keep on increasing scroll speed.
744c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein     * @hide
754c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein     */
764c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein    public OverScrollerSGV(Context context, Interpolator interpolator, boolean flywheel) {
774c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein        mInterpolator = interpolator;
784c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein        mFlywheel = flywheel;
794c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein        mScrollerX = new SplineOverScroller(context);
804c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein        mScrollerY = new SplineOverScroller(context);
814c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein    }
824c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein
834c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein    /**
844c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein     * Creates an OverScroller with flywheel enabled.
854c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein     * @param context The context of this application.
864c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein     * @param interpolator The scroll interpolator. If null, a default (viscous) interpolator will
874c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein     * be used.
884c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein     * @param bounceCoefficientX A value between 0 and 1 that will determine the proportion of the
894c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein     * velocity which is preserved in the bounce when the horizontal edge is reached. A null value
904c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein     * means no bounce. This behavior is no longer supported and this coefficient has no effect.
914c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein     * @param bounceCoefficientY Same as bounceCoefficientX but for the vertical direction. This
924c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein     * behavior is no longer supported and this coefficient has no effect.
934c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein     * !deprecated Use {!link #OverScroller(Context, Interpolator, boolean)} instead.
944c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein     */
954c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein    public OverScrollerSGV(Context context, Interpolator interpolator,
964c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein            float bounceCoefficientX, float bounceCoefficientY) {
974c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein        this(context, interpolator, true);
984c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein    }
994c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein
1004c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein    /**
1014c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein     * Creates an OverScroller.
1024c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein     * @param context The context of this application.
1034c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein     * @param interpolator The scroll interpolator. If null, a default (viscous) interpolator will
1044c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein     * be used.
1054c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein     * @param bounceCoefficientX A value between 0 and 1 that will determine the proportion of the
1064c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein     * velocity which is preserved in the bounce when the horizontal edge is reached. A null value
1074c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein     * means no bounce. This behavior is no longer supported and this coefficient has no effect.
1084c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein     * @param bounceCoefficientY Same as bounceCoefficientX but for the vertical direction. This
1094c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein     * behavior is no longer supported and this coefficient has no effect.
1104c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein     * @param flywheel If true, successive fling motions will keep on increasing scroll speed.
1114c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein     * !deprecated Use {!link OverScroller(Context, Interpolator, boolean)} instead.
1124c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein     */
1134c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein    public OverScrollerSGV(Context context, Interpolator interpolator,
1144c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein            float bounceCoefficientX, float bounceCoefficientY, boolean flywheel) {
1154c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein        this(context, interpolator, flywheel);
1164c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein    }
1174c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein
1184c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein    void setInterpolator(Interpolator interpolator) {
1194c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein        mInterpolator = interpolator;
1204c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein    }
1214c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein
1224c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein    /**
1234c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein     * The amount of friction applied to flings. The default value
1244c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein     * is {@link ViewConfiguration#getScrollFriction}.
1254c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein     *
1264c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein     * @param friction A scalar dimension-less value representing the coefficient of
1274c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein     *         friction.
1284c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein     */
1294c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein    public final void setFriction(float friction) {
1304c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein        mScrollerX.setFriction(friction);
1314c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein        mScrollerY.setFriction(friction);
1324c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein    }
1334c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein
1344c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein    /**
1354c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein     *
1364c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein     * Returns whether the scroller has finished scrolling.
1374c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein     *
1384c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein     * @return True if the scroller has finished scrolling, false otherwise.
1394c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein     */
1404c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein    public final boolean isFinished() {
1414c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein        return mScrollerX.mFinished && mScrollerY.mFinished;
1424c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein    }
1434c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein
1444c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein    /**
1454c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein     * Force the finished field to a particular value. Contrary to
1464c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein     * {@link #abortAnimation()}, forcing the animation to finished
1474c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein     * does NOT cause the scroller to move to the final x and y
1484c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein     * position.
1494c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein     *
1504c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein     * @param finished The new finished value.
1514c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein     */
1524c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein    public final void forceFinished(boolean finished) {
1534c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein        mScrollerX.mFinished = mScrollerY.mFinished = finished;
1544c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein    }
1554c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein
1564c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein    /**
1574c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein     * Returns the current X offset in the scroll.
1584c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein     *
1594c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein     * @return The new X offset as an absolute distance from the origin.
1604c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein     */
1614c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein    public final int getCurrX() {
1624c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein        return mScrollerX.mCurrentPosition;
1634c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein    }
1644c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein
1654c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein    /**
1664c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein     * Returns the current Y offset in the scroll.
1674c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein     *
1684c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein     * @return The new Y offset as an absolute distance from the origin.
1694c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein     */
1704c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein    public final int getCurrY() {
1714c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein        return mScrollerY.mCurrentPosition;
1724c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein    }
1734c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein
1744c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein    /**
1754c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein     * Returns the absolute value of the current velocity.
1764c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein     *
1774c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein     * @return The original velocity less the deceleration, norm of the X and Y velocity vector.
1784c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein     */
1794c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein    public float getCurrVelocity() {
1804c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein        float squaredNorm = mScrollerX.mCurrVelocity * mScrollerX.mCurrVelocity;
1814c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein        squaredNorm += mScrollerY.mCurrVelocity * mScrollerY.mCurrVelocity;
1824c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein        return FloatMath.sqrt(squaredNorm);
1834c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein    }
1844c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein
1854c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein    /**
1864c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein     * Returns the start X offset in the scroll.
1874c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein     *
1884c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein     * @return The start X offset as an absolute distance from the origin.
1894c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein     */
1904c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein    public final int getStartX() {
1914c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein        return mScrollerX.mStart;
1924c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein    }
1934c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein
1944c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein    /**
1954c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein     * Returns the start Y offset in the scroll.
1964c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein     *
1974c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein     * @return The start Y offset as an absolute distance from the origin.
1984c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein     */
1994c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein    public final int getStartY() {
2004c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein        return mScrollerY.mStart;
2014c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein    }
2024c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein
2034c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein    /**
2044c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein     * Returns where the scroll will end. Valid only for "fling" scrolls.
2054c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein     *
2064c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein     * @return The final X offset as an absolute distance from the origin.
2074c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein     */
2084c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein    public final int getFinalX() {
2094c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein        return mScrollerX.mFinal;
2104c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein    }
2114c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein
2124c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein    /**
2134c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein     * Returns where the scroll will end. Valid only for "fling" scrolls.
2144c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein     *
2154c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein     * @return The final Y offset as an absolute distance from the origin.
2164c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein     */
2174c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein    public final int getFinalY() {
2184c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein        return mScrollerY.mFinal;
2194c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein    }
2204c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein
2214c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein    /**
2224c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein     * Returns how long the scroll event will take, in milliseconds.
2234c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein     *
2244c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein     * @return The duration of the scroll in milliseconds.
2254c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein     *
2264c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein     * @hide Pending removal once nothing depends on it
2274c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein     * @deprecated OverScrollers don't necessarily have a fixed duration.
2284c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein     *             This function will lie to the best of its ability.
2294c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein     */
2304c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein    @Deprecated
2314c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein    public final int getDuration() {
2324c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein        return Math.max(mScrollerX.mDuration, mScrollerY.mDuration);
2334c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein    }
2344c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein
2354c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein    /**
2364c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein     * Extend the scroll animation. This allows a running animation to scroll
2374c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein     * further and longer, when used with {@link #setFinalX(int)} or {@link #setFinalY(int)}.
2384c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein     *
2394c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein     * @param extend Additional time to scroll in milliseconds.
2404c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein     * @see #setFinalX(int)
2414c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein     * @see #setFinalY(int)
2424c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein     *
2434c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein     * @hide Pending removal once nothing depends on it
2444c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein     * @deprecated OverScrollers don't necessarily have a fixed duration.
2454c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein     *             Instead of setting a new final position and extending
2464c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein     *             the duration of an existing scroll, use startScroll
2474c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein     *             to begin a new animation.
2484c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein     */
2494c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein    @Deprecated
2504c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein    public void extendDuration(int extend) {
2514c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein        mScrollerX.extendDuration(extend);
2524c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein        mScrollerY.extendDuration(extend);
2534c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein    }
2544c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein
2554c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein    /**
2564c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein     * Sets the final position (X) for this scroller.
2574c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein     *
2584c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein     * @param newX The new X offset as an absolute distance from the origin.
2594c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein     * @see #extendDuration(int)
2604c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein     * @see #setFinalY(int)
2614c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein     *
2624c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein     * @hide Pending removal once nothing depends on it
2634c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein     * @deprecated OverScroller's final position may change during an animation.
2644c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein     *             Instead of setting a new final position and extending
2654c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein     *             the duration of an existing scroll, use startScroll
2664c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein     *             to begin a new animation.
2674c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein     */
2684c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein    @Deprecated
2694c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein    public void setFinalX(int newX) {
2704c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein        mScrollerX.setFinalPosition(newX);
2714c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein    }
2724c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein
2734c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein    /**
2744c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein     * Sets the final position (Y) for this scroller.
2754c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein     *
2764c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein     * @param newY The new Y offset as an absolute distance from the origin.
2774c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein     * @see #extendDuration(int)
2784c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein     * @see #setFinalX(int)
2794c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein     *
2804c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein     * @hide Pending removal once nothing depends on it
2814c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein     * @deprecated OverScroller's final position may change during an animation.
2824c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein     *             Instead of setting a new final position and extending
2834c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein     *             the duration of an existing scroll, use startScroll
2844c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein     *             to begin a new animation.
2854c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein     */
2864c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein    @Deprecated
2874c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein    public void setFinalY(int newY) {
2884c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein        mScrollerY.setFinalPosition(newY);
2894c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein    }
2904c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein
2914c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein    /**
2924c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein     * Call this when you want to know the new location. If it returns true, the
2934c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein     * animation is not yet finished.
2944c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein     */
2954c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein    public boolean computeScrollOffset() {
2964c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein        if (isFinished()) {
2974c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein            return false;
2984c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein        }
2994c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein
3004c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein        switch (mMode) {
3014c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein            case SCROLL_MODE:
3024c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein                long time = AnimationUtils.currentAnimationTimeMillis();
3034c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein                // Any scroller can be used for time, since they were started
3044c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein                // together in scroll mode. We use X here.
3054c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein                final long elapsedTime = time - mScrollerX.mStartTime;
3064c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein
3074c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein                final int duration = mScrollerX.mDuration;
3084c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein                if (elapsedTime < duration) {
3094c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein                    float q = (float) (elapsedTime) / duration;
3104c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein
3114c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein                    if (mInterpolator == null) {
3124c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein                        q = mInterpolator.getInterpolation(q);
3134c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein                    } else {
3144c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein                        q = mInterpolator.getInterpolation(q);
3154c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein                    }
3164c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein
3174c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein                    mScrollerX.updateScroll(q);
3184c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein                    mScrollerY.updateScroll(q);
3194c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein                } else {
3204c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein                    abortAnimation();
3214c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein                }
3224c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein                break;
3234c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein
3244c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein            case FLING_MODE:
3254c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein                if (!mScrollerX.mFinished) {
3264c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein                    if (!mScrollerX.update()) {
3274c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein                        if (!mScrollerX.continueWhenFinished()) {
3284c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein                            mScrollerX.finish();
3294c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein                        }
3304c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein                    }
3314c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein                }
3324c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein
3334c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein                if (!mScrollerY.mFinished) {
3344c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein                    if (!mScrollerY.update()) {
3354c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein                        if (!mScrollerY.continueWhenFinished()) {
3364c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein                            mScrollerY.finish();
3374c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein                        }
3384c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein                    }
3394c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein                }
3404c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein
3414c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein                break;
3424c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein        }
3434c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein
3444c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein        return true;
3454c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein    }
3464c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein
3474c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein    /**
3484c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein     * Start scrolling by providing a starting point and the distance to travel.
3494c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein     * The scroll will use the default value of 250 milliseconds for the
3504c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein     * duration.
3514c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein     *
3524c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein     * @param startX Starting horizontal scroll offset in pixels. Positive
3534c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein     *        numbers will scroll the content to the left.
3544c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein     * @param startY Starting vertical scroll offset in pixels. Positive numbers
3554c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein     *        will scroll the content up.
3564c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein     * @param dx Horizontal distance to travel. Positive numbers will scroll the
3574c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein     *        content to the left.
3584c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein     * @param dy Vertical distance to travel. Positive numbers will scroll the
3594c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein     *        content up.
3604c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein     */
3614c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein    public void startScroll(int startX, int startY, int dx, int dy) {
3624c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein        startScroll(startX, startY, dx, dy, DEFAULT_DURATION);
3634c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein    }
3644c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein
3654c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein    /**
3664c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein     * Start scrolling by providing a starting point and the distance to travel.
3674c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein     *
3684c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein     * @param startX Starting horizontal scroll offset in pixels. Positive
3694c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein     *        numbers will scroll the content to the left.
3704c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein     * @param startY Starting vertical scroll offset in pixels. Positive numbers
3714c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein     *        will scroll the content up.
3724c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein     * @param dx Horizontal distance to travel. Positive numbers will scroll the
3734c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein     *        content to the left.
3744c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein     * @param dy Vertical distance to travel. Positive numbers will scroll the
3754c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein     *        content up.
3764c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein     * @param duration Duration of the scroll in milliseconds.
3774c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein     */
3784c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein    public void startScroll(int startX, int startY, int dx, int dy, int duration) {
3794c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein        mMode = SCROLL_MODE;
3804c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein        mScrollerX.startScroll(startX, dx, duration);
3814c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein        mScrollerY.startScroll(startY, dy, duration);
3824c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein    }
3834c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein
3844c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein    /**
3854c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein     * Call this when you want to 'spring back' into a valid coordinate range.
3864c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein     *
3874c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein     * @param startX Starting X coordinate
3884c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein     * @param startY Starting Y coordinate
3894c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein     * @param minX Minimum valid X value
3904c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein     * @param maxX Maximum valid X value
3914c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein     * @param minY Minimum valid Y value
3924c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein     * @param maxY Minimum valid Y value
3934c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein     * @return true if a springback was initiated, false if startX and startY were
3944c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein     *          already within the valid range.
3954c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein     */
3964c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein    public boolean springBack(int startX, int startY, int minX, int maxX, int minY, int maxY) {
3974c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein        mMode = FLING_MODE;
3984c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein
3994c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein        // Make sure both methods are called.
4004c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein        final boolean spingbackX = mScrollerX.springback(startX, minX, maxX);
4014c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein        final boolean spingbackY = mScrollerY.springback(startY, minY, maxY);
4024c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein        return spingbackX || spingbackY;
4034c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein    }
4044c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein
4054c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein    public void fling(int startX, int startY, int velocityX, int velocityY,
4064c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein            int minX, int maxX, int minY, int maxY) {
4074c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein        fling(startX, startY, velocityX, velocityY, minX, maxX, minY, maxY, 0, 0);
4084c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein    }
4094c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein
4104c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein    /**
4114c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein     * Start scrolling based on a fling gesture. The distance traveled will
4124c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein     * depend on the initial velocity of the fling.
4134c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein     *
4144c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein     * @param startX Starting point of the scroll (X)
4154c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein     * @param startY Starting point of the scroll (Y)
4164c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein     * @param velocityX Initial velocity of the fling (X) measured in pixels per
4174c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein     *            second.
4184c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein     * @param velocityY Initial velocity of the fling (Y) measured in pixels per
4194c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein     *            second
4204c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein     * @param minX Minimum X value. The scroller will not scroll past this point
4214c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein     *            unless overX > 0. If overfling is allowed, it will use minX as
4224c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein     *            a springback boundary.
4234c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein     * @param maxX Maximum X value. The scroller will not scroll past this point
4244c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein     *            unless overX > 0. If overfling is allowed, it will use maxX as
4254c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein     *            a springback boundary.
4264c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein     * @param minY Minimum Y value. The scroller will not scroll past this point
4274c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein     *            unless overY > 0. If overfling is allowed, it will use minY as
4284c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein     *            a springback boundary.
4294c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein     * @param maxY Maximum Y value. The scroller will not scroll past this point
4304c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein     *            unless overY > 0. If overfling is allowed, it will use maxY as
4314c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein     *            a springback boundary.
4324c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein     * @param overX Overfling range. If > 0, horizontal overfling in either
4334c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein     *            direction will be possible.
4344c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein     * @param overY Overfling range. If > 0, vertical overfling in either
4354c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein     *            direction will be possible.
4364c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein     */
4374c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein    public void fling(int startX, int startY, int velocityX, int velocityY,
4384c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein            int minX, int maxX, int minY, int maxY, int overX, int overY) {
4394c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein        // Continue a scroll or fling in progress
4404c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein        if (mFlywheel && !isFinished()) {
4414c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein            float oldVelocityX = mScrollerX.mCurrVelocity;
4424c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein            float oldVelocityY = mScrollerY.mCurrVelocity;
4434c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein            if (Math.signum(velocityX) == Math.signum(oldVelocityX) &&
4444c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein                    Math.signum(velocityY) == Math.signum(oldVelocityY)) {
4454c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein                velocityX += oldVelocityX;
4464c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein                velocityY += oldVelocityY;
4474c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein            }
4484c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein        }
4494c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein
4504c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein        mMode = FLING_MODE;
4514c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein        mScrollerX.fling(startX, velocityX, minX, maxX, overX);
4524c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein        mScrollerY.fling(startY, velocityY, minY, maxY, overY);
4534c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein    }
4544c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein
4554c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein    /**
4564c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein     * Notify the scroller that we've reached a horizontal boundary.
4574c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein     * Normally the information to handle this will already be known
4584c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein     * when the animation is started, such as in a call to one of the
4594c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein     * fling functions. However there are cases where this cannot be known
4604c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein     * in advance. This function will transition the current motion and
4614c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein     * animate from startX to finalX as appropriate.
4624c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein     *
4634c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein     * @param startX Starting/current X position
4644c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein     * @param finalX Desired final X position
4654c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein     * @param overX Magnitude of overscroll allowed. This should be the maximum
4664c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein     *              desired distance from finalX. Absolute value - must be positive.
4674c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein     */
4684c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein    public void notifyHorizontalEdgeReached(int startX, int finalX, int overX) {
4694c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein        mScrollerX.notifyEdgeReached(startX, finalX, overX);
4704c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein    }
4714c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein
4724c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein    /**
4734c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein     * Notify the scroller that we've reached a vertical boundary.
4744c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein     * Normally the information to handle this will already be known
4754c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein     * when the animation is started, such as in a call to one of the
4764c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein     * fling functions. However there are cases where this cannot be known
4774c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein     * in advance. This function will animate a parabolic motion from
4784c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein     * startY to finalY.
4794c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein     *
4804c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein     * @param startY Starting/current Y position
4814c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein     * @param finalY Desired final Y position
4824c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein     * @param overY Magnitude of overscroll allowed. This should be the maximum
4834c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein     *              desired distance from finalY. Absolute value - must be positive.
4844c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein     */
4854c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein    public void notifyVerticalEdgeReached(int startY, int finalY, int overY) {
4864c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein        mScrollerY.notifyEdgeReached(startY, finalY, overY);
4874c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein    }
4884c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein
4894c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein    /**
4904c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein     * Returns whether the current Scroller is currently returning to a valid position.
4914c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein     * Valid bounds were provided by the
4924c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein     * {@link #fling(int, int, int, int, int, int, int, int, int, int)} method.
4934c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein     *
4944c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein     * One should check this value before calling
4954c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein     * {@link #startScroll(int, int, int, int)} as the interpolation currently in progress
4964c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein     * to restore a valid position will then be stopped. The caller has to take into account
4974c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein     * the fact that the started scroll will start from an overscrolled position.
4984c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein     *
4994c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein     * @return true when the current position is overscrolled and in the process of
5004c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein     *         interpolating back to a valid value.
5014c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein     */
5024c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein    public boolean isOverScrolled() {
5034c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein        return ((!mScrollerX.mFinished &&
5044c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein                mScrollerX.mState != SplineOverScroller.SPLINE) ||
5054c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein                (!mScrollerY.mFinished &&
5064c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein                        mScrollerY.mState != SplineOverScroller.SPLINE));
5074c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein    }
5084c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein
5094c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein    /**
5104c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein     * Stops the animation. Contrary to {@link #forceFinished(boolean)},
5114c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein     * aborting the animating causes the scroller to move to the final x and y
5124c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein     * positions.
5134c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein     *
5144c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein     * @see #forceFinished(boolean)
5154c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein     */
5164c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein    public void abortAnimation() {
5174c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein        mScrollerX.finish();
5184c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein        mScrollerY.finish();
5194c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein    }
5204c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein
5214c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein    /**
5224c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein     * Returns the time elapsed since the beginning of the scrolling.
5234c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein     *
5244c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein     * @return The elapsed time in milliseconds.
5254c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein     *
5264c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein     * @hide
5274c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein     */
5284c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein    public int timePassed() {
5294c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein        final long time = AnimationUtils.currentAnimationTimeMillis();
5304c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein        final long startTime = Math.min(mScrollerX.mStartTime, mScrollerY.mStartTime);
5314c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein        return (int) (time - startTime);
5324c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein    }
5334c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein
5344c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein    /**
5354c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein     * @hide
5364c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein     */
5374c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein    public boolean isScrollingInDirection(float xvel, float yvel) {
5384c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein        final int dx = mScrollerX.mFinal - mScrollerX.mStart;
5394c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein        final int dy = mScrollerY.mFinal - mScrollerY.mStart;
5404c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein        return !isFinished() && Math.signum(xvel) == Math.signum(dx) &&
5414c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein                Math.signum(yvel) == Math.signum(dy);
5424c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein    }
5434c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein
5444c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein    static class SplineOverScroller {
5454c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein        // Initial position
5464c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein        private int mStart;
5474c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein
5484c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein        // Current position
5494c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein        private int mCurrentPosition;
5504c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein
5514c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein        // Final position
5524c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein        private int mFinal;
5534c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein
5544c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein        // Initial velocity
5554c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein        private int mVelocity;
5564c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein
5574c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein        // Current velocity
5584c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein        private float mCurrVelocity;
5594c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein
5604c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein        // Constant current deceleration
5614c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein        private float mDeceleration;
5624c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein
5634c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein        // Animation starting time, in system milliseconds
5644c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein        private long mStartTime;
5654c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein
5664c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein        // Animation duration, in milliseconds
5674c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein        private int mDuration;
5684c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein
5694c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein        // Duration to complete spline component of animation
5704c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein        private int mSplineDuration;
5714c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein
5724c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein        // Distance to travel along spline animation
5734c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein        private int mSplineDistance;
5744c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein
5754c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein        // Whether the animation is currently in progress
5764c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein        private boolean mFinished;
5774c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein
5784c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein        // The allowed overshot distance before boundary is reached.
5794c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein        private int mOver;
5804c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein
5814c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein        // Fling friction
5824c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein        private float mFlingFriction = ViewConfiguration.getScrollFriction();
5834c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein
5844c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein        // Current state of the animation.
5854c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein        private int mState = SPLINE;
5864c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein
5874c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein        // Constant gravity value, used in the deceleration phase.
5884c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein        private static final float GRAVITY = 2000.0f;
5894c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein
5904c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein        // A context-specific coefficient adjusted to physical values.
5914c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein        private final float mPhysicalCoeff;
5924c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein
5934c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein        private static float DECELERATION_RATE = (float) (Math.log(0.78) / Math.log(0.9));
5944c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein        private static final float INFLEXION = 0.35f; // Tension lines cross at (INFLEXION, 1)
5954c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein        private static final float START_TENSION = 0.5f;
5964c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein        private static final float END_TENSION = 1.0f;
5974c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein        private static final float P1 = START_TENSION * INFLEXION;
5984c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein        private static final float P2 = 1.0f - END_TENSION * (1.0f - INFLEXION);
5994c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein
6004c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein        private static final int NB_SAMPLES = 100;
6014c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein        private static final float[] SPLINE_POSITION = new float[NB_SAMPLES + 1];
6024c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein        private static final float[] SPLINE_TIME = new float[NB_SAMPLES + 1];
6034c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein
6044c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein        private static final int SPLINE = 0;
6054c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein        private static final int CUBIC = 1;
6064c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein        private static final int BALLISTIC = 2;
6074c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein
6084c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein        static {
6094c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein            float x_min = 0.0f;
6104c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein            float y_min = 0.0f;
6114c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein            for (int i = 0; i < NB_SAMPLES; i++) {
6124c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein                final float alpha = (float) i / NB_SAMPLES;
6134c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein
6144c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein                float x_max = 1.0f;
6154c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein                float x, tx, coef;
6164c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein                while (true) {
6174c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein                    x = x_min + (x_max - x_min) / 2.0f;
6184c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein                    coef = 3.0f * x * (1.0f - x);
6194c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein                    tx = coef * ((1.0f - x) * P1 + x * P2) + x * x * x;
6204c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein                    if (Math.abs(tx - alpha) < 1E-5) break;
6214c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein                    if (tx > alpha) x_max = x;
6224c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein                    else x_min = x;
6234c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein                }
6244c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein                SPLINE_POSITION[i] = coef * ((1.0f - x) * START_TENSION + x) + x * x * x;
6254c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein
6264c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein                float y_max = 1.0f;
6274c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein                float y, dy;
6284c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein                while (true) {
6294c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein                    y = y_min + (y_max - y_min) / 2.0f;
6304c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein                    coef = 3.0f * y * (1.0f - y);
6314c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein                    dy = coef * ((1.0f - y) * START_TENSION + y) + y * y * y;
6324c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein                    if (Math.abs(dy - alpha) < 1E-5) break;
6334c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein                    if (dy > alpha) y_max = y;
6344c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein                    else y_min = y;
6354c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein                }
6364c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein                SPLINE_TIME[i] = coef * ((1.0f - y) * P1 + y * P2) + y * y * y;
6374c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein            }
6384c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein            SPLINE_POSITION[NB_SAMPLES] = SPLINE_TIME[NB_SAMPLES] = 1.0f;
6394c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein        }
6404c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein
6414c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein        void setFriction(float friction) {
6424c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein            mFlingFriction = friction;
6434c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein        }
6444c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein
6454c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein        SplineOverScroller(Context context) {
6464c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein            mFinished = true;
6474c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein            final float ppi = context.getResources().getDisplayMetrics().density * 160.0f;
6484c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein            mPhysicalCoeff = SensorManager.GRAVITY_EARTH // g (m/s^2)
6494c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein                    * 39.37f // inch/meter
6504c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein                    * ppi
6514c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein                    * 0.84f; // look and feel tuning
6524c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein        }
6534c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein
6544c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein        void updateScroll(float q) {
6554c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein            mCurrentPosition = mStart + Math.round(q * (mFinal - mStart));
6564c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein        }
6574c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein
6584c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein        /*
6594c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein         * Get a signed deceleration that will reduce the velocity.
6604c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein         */
6614c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein        static private float getDeceleration(int velocity) {
6624c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein            return velocity > 0 ? -GRAVITY : GRAVITY;
6634c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein        }
6644c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein
6654c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein        /*
6664c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein         * Modifies mDuration to the duration it takes to get from start to newFinal using the
6674c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein         * spline interpolation. The previous duration was needed to get to oldFinal.
6684c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein         */
6694c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein        private void adjustDuration(int start, int oldFinal, int newFinal) {
6704c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein            final int oldDistance = oldFinal - start;
6714c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein            final int newDistance = newFinal - start;
6724c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein            final float x = Math.abs((float) newDistance / oldDistance);
6734c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein            final int index = (int) (NB_SAMPLES * x);
6744c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein            if (index < NB_SAMPLES) {
6754c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein                final float x_inf = (float) index / NB_SAMPLES;
6764c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein                final float x_sup = (float) (index + 1) / NB_SAMPLES;
6774c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein                final float t_inf = SPLINE_TIME[index];
6784c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein                final float t_sup = SPLINE_TIME[index + 1];
6794c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein                final float timeCoef = t_inf + (x - x_inf) / (x_sup - x_inf) * (t_sup - t_inf);
6804c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein                mDuration *= timeCoef;
6814c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein            }
6824c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein        }
6834c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein
6844c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein        void startScroll(int start, int distance, int duration) {
6854c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein            mFinished = false;
6864c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein
6874c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein            mStart = start;
6884c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein            mFinal = start + distance;
6894c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein
6904c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein            mStartTime = AnimationUtils.currentAnimationTimeMillis();
6914c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein            mDuration = duration;
6924c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein
6934c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein            // Unused
6944c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein            mDeceleration = 0.0f;
6954c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein            mVelocity = 0;
6964c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein        }
6974c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein
6984c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein        void finish() {
6994c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein            mCurrentPosition = mFinal;
7004c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein            // Not reset since WebView relies on this value for fast fling.
7014c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein            // TODO: restore when WebView uses the fast fling implemented in this class.
7024c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein            // mCurrVelocity = 0.0f;
7034c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein            mFinished = true;
7044c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein        }
7054c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein
7064c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein        void setFinalPosition(int position) {
7074c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein            mFinal = position;
7084c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein            mFinished = false;
7094c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein        }
7104c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein
7114c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein        void extendDuration(int extend) {
7124c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein            final long time = AnimationUtils.currentAnimationTimeMillis();
7134c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein            final int elapsedTime = (int) (time - mStartTime);
7144c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein            mDuration = elapsedTime + extend;
7154c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein            mFinished = false;
7164c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein        }
7174c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein
7184c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein        boolean springback(int start, int min, int max) {
7194c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein            mFinished = true;
7204c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein
7214c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein            mStart = mFinal = start;
7224c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein            mVelocity = 0;
7234c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein
7244c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein            mStartTime = AnimationUtils.currentAnimationTimeMillis();
7254c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein            mDuration = 0;
7264c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein
7274c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein            if (start < min) {
7284c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein                startSpringback(start, min, 0);
7294c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein            } else if (start > max) {
7304c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein                startSpringback(start, max, 0);
7314c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein            }
7324c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein
7334c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein            return !mFinished;
7344c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein        }
7354c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein
7364c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein        private void startSpringback(int start, int end, int velocity) {
7374c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein            // mStartTime has been set
7384c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein            mFinished = false;
7394c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein            mState = CUBIC;
7404c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein            mStart = start;
7414c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein            mFinal = end;
7424c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein            final int delta = start - end;
7434c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein            mDeceleration = getDeceleration(delta);
7444c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein            // TODO take velocity into account
7454c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein            mVelocity = -delta; // only sign is used
7464c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein            mOver = Math.abs(delta);
7474c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein            mDuration = (int) (1000.0 * Math.sqrt(-2.0 * delta / mDeceleration));
7484c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein        }
7494c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein
7504c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein        void fling(int start, int velocity, int min, int max, int over) {
7514c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein            mOver = over;
7524c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein            mFinished = false;
7534c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein            mCurrVelocity = mVelocity = velocity;
7544c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein            mDuration = mSplineDuration = 0;
7554c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein            mStartTime = AnimationUtils.currentAnimationTimeMillis();
7564c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein            mCurrentPosition = mStart = start;
7574c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein
7584c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein            if (start > max || start < min) {
7594c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein                startAfterEdge(start, min, max, velocity);
7604c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein                return;
7614c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein            }
7624c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein
7634c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein            mState = SPLINE;
7644c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein            double totalDistance = 0.0;
7654c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein
7664c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein            if (velocity != 0) {
7674c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein                mDuration = mSplineDuration = getSplineFlingDuration(velocity);
7684c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein                totalDistance = getSplineFlingDistance(velocity);
7694c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein            }
7704c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein
7714c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein            mSplineDistance = (int) (totalDistance * Math.signum(velocity));
7724c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein            mFinal = start + mSplineDistance;
7734c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein
7744c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein            // Clamp to a valid final position
7754c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein            if (mFinal < min) {
7764c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein                adjustDuration(mStart, mFinal, min);
7774c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein                mFinal = min;
7784c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein            }
7794c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein
7804c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein            if (mFinal > max) {
7814c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein                adjustDuration(mStart, mFinal, max);
7824c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein                mFinal = max;
7834c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein            }
7844c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein        }
7854c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein
7864c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein        private double getSplineDeceleration(int velocity) {
7874c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein            return Math.log(INFLEXION * Math.abs(velocity) / (mFlingFriction * mPhysicalCoeff));
7884c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein        }
7894c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein
7904c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein        private double getSplineFlingDistance(int velocity) {
7914c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein            final double l = getSplineDeceleration(velocity);
7924c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein            final double decelMinusOne = DECELERATION_RATE - 1.0;
7934c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein            return mFlingFriction * mPhysicalCoeff * Math.exp(DECELERATION_RATE / decelMinusOne * l);
7944c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein        }
7954c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein
7964c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein        /* Returns the duration, expressed in milliseconds */
7974c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein        private int getSplineFlingDuration(int velocity) {
7984c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein            final double l = getSplineDeceleration(velocity);
7994c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein            final double decelMinusOne = DECELERATION_RATE - 1.0;
8004c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein            return (int) (1000.0 * Math.exp(l / decelMinusOne));
8014c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein        }
8024c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein
8034c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein        private void fitOnBounceCurve(int start, int end, int velocity) {
8044c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein            // Simulate a bounce that started from edge
8054c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein            final float durationToApex = - velocity / mDeceleration;
8064c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein            final float distanceToApex = velocity * velocity / 2.0f / Math.abs(mDeceleration);
8074c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein            final float distanceToEdge = Math.abs(end - start);
8084c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein            final float totalDuration = (float) Math.sqrt(
8094c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein                    2.0 * (distanceToApex + distanceToEdge) / Math.abs(mDeceleration));
8104c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein            mStartTime -= (int) (1000.0f * (totalDuration - durationToApex));
8114c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein            mStart = end;
8124c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein            mVelocity = (int) (- mDeceleration * totalDuration);
8134c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein        }
8144c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein
8154c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein        private void startBounceAfterEdge(int start, int end, int velocity) {
8164c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein            mDeceleration = getDeceleration(velocity == 0 ? start - end : velocity);
8174c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein            fitOnBounceCurve(start, end, velocity);
8184c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein            onEdgeReached();
8194c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein        }
8204c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein
8214c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein        private void startAfterEdge(int start, int min, int max, int velocity) {
8224c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein            if (start > min && start < max) {
8234c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein                Log.e("OverScroller", "startAfterEdge called from a valid position");
8244c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein                mFinished = true;
8254c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein                return;
8264c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein            }
8274c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein            final boolean positive = start > max;
8284c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein            final int edge = positive ? max : min;
8294c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein            final int overDistance = start - edge;
8304c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein            boolean keepIncreasing = overDistance * velocity >= 0;
8314c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein            if (keepIncreasing) {
8324c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein                // Will result in a bounce or a to_boundary depending on velocity.
8334c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein                startBounceAfterEdge(start, edge, velocity);
8344c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein            } else {
8354c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein                // TODO: figure out how to absorb the velocity properly.
8364c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein                final double totalDistance = getSplineFlingDistance(velocity);
8374c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein                if (false) {
8384c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein                    fling(start, velocity, positive ? min : start, positive ? start : max, mOver);
8394c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein                } else {
8404c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein                    startSpringback(start, edge, velocity);
8414c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein                }
8424c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein            }
8434c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein        }
8444c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein
8454c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein        void notifyEdgeReached(int start, int end, int over) {
8464c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein            // mState is used to detect successive notifications
8474c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein            if (mState == SPLINE) {
8484c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein                mOver = over;
8494c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein                mStartTime = AnimationUtils.currentAnimationTimeMillis();
8504c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein                // We were in fling/scroll mode before: current velocity is such that distance to
8514c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein                // edge is increasing. This ensures that startAfterEdge will not start a new fling.
8524c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein                startAfterEdge(start, end, end, (int) mCurrVelocity);
8534c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein            }
8544c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein        }
8554c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein
8564c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein        private void onEdgeReached() {
8574c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein            // mStart, mVelocity and mStartTime were adjusted to their values when edge was reached.
8584c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein            float distance = mVelocity * mVelocity / (2.0f * Math.abs(mDeceleration));
8594c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein            final float sign = Math.signum(mVelocity);
8604c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein
8614c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein            if (distance > mOver) {
8624c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein                // Default deceleration is not sufficient to slow us down before boundary
8634c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein                 mDeceleration = - sign * mVelocity * mVelocity / (2.0f * mOver);
8644c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein                 distance = mOver;
8654c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein            }
8664c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein
8674c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein            mOver = (int) distance;
8684c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein            mState = BALLISTIC;
8694c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein            mFinal = mStart + (int) (mVelocity > 0 ? distance : -distance);
8704c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein            mDuration = - (int) (1000.0f * mVelocity / mDeceleration);
8714c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein        }
8724c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein
8734c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein        boolean continueWhenFinished() {
8744c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein            switch (mState) {
8754c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein                case SPLINE:
8764c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein                    // Duration from start to null velocity
8774c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein                    if (mDuration < mSplineDuration) {
8784c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein                        // If the animation was clamped, we reached the edge
8794c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein                        mStart = mFinal;
8804c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein                        // TODO Better compute speed when edge was reached
8814c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein                        mVelocity = (int) mCurrVelocity;
8824c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein                        mDeceleration = getDeceleration(mVelocity);
8834c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein                        mStartTime += mDuration;
8844c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein                        onEdgeReached();
8854c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein                    } else {
8864c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein                        // Normal stop, no need to continue
8874c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein                        return false;
8884c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein                    }
8894c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein                    break;
8904c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein                case BALLISTIC:
8914c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein                    mStartTime += mDuration;
8924c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein                    startSpringback(mFinal, mStart, 0);
8934c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein                    break;
8944c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein                case CUBIC:
8954c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein                    return false;
8964c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein            }
8974c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein
8984c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein            update();
8994c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein            return true;
9004c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein        }
9014c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein
9024c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein        /*
9034c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein         * Update the current position and velocity for current time. Returns
9044c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein         * true if update has been done and false if animation duration has been
9054c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein         * reached.
9064c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein         */
9074c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein        boolean update() {
9084c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein            final long time = AnimationUtils.currentAnimationTimeMillis();
9094c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein            final long currentTime = time - mStartTime;
9104c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein
9114c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein            if (currentTime > mDuration) {
9124c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein                return false;
9134c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein            }
9144c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein
9154c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein            double distance = 0.0;
9164c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein            switch (mState) {
9174c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein                case SPLINE: {
9184c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein                    final float t = (float) currentTime / mSplineDuration;
9194c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein                    final int index = (int) (NB_SAMPLES * t);
9204c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein                    float distanceCoef = 1.f;
9214c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein                    float velocityCoef = 0.f;
9224c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein                    if (index < NB_SAMPLES) {
9234c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein                        final float t_inf = (float) index / NB_SAMPLES;
9244c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein                        final float t_sup = (float) (index + 1) / NB_SAMPLES;
9254c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein                        final float d_inf = SPLINE_POSITION[index];
9264c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein                        final float d_sup = SPLINE_POSITION[index + 1];
9274c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein                        velocityCoef = (d_sup - d_inf) / (t_sup - t_inf);
9284c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein                        distanceCoef = d_inf + (t - t_inf) * velocityCoef;
9294c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein                    }
9304c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein
9314c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein                    distance = distanceCoef * mSplineDistance;
9324c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein                    mCurrVelocity = velocityCoef * mSplineDistance / mSplineDuration * 1000.0f;
9334c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein                    break;
9344c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein                }
9354c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein
9364c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein                case BALLISTIC: {
9374c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein                    final float t = currentTime / 1000.0f;
9384c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein                    mCurrVelocity = mVelocity + mDeceleration * t;
9394c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein                    distance = mVelocity * t + mDeceleration * t * t / 2.0f;
9404c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein                    break;
9414c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein                }
9424c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein
9434c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein                case CUBIC: {
9444c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein                    final float t = (float) (currentTime) / mDuration;
9454c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein                    final float t2 = t * t;
9464c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein                    final float sign = Math.signum(mVelocity);
9474c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein                    distance = sign * mOver * (3.0f * t2 - 2.0f * t * t2);
9484c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein                    mCurrVelocity = sign * mOver * 6.0f * (- t + t2);
9494c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein                    break;
9504c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein                }
9514c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein            }
9524c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein
9534c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein            mCurrentPosition = mStart + (int) Math.round(distance);
9544c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein
9554c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein            return true;
9564c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein        }
9574c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein    }
9584c68a4b737d7776fd23e857eb612f89c6dba3ec0Sam Blitzstein}
959